前端的框架作用其实都是为了让你更快速、更好地完成前端开发,一个需求如果单纯使用 HTML、JS 需要几百行代码实现,而使用框架几行代码即可搞定。另外框架可以很好地做到模块化、组件化开发,以数据来渲染 UI,不需要通过繁琐的 API 来操作 DOM。san 是一个开源的MVVM前端框架,只有 15k 左右,它与 Vue 和 React 类似。
先看一个完整例子:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>第一个例子title>
<script src="https://unpkg.com/san@latest">script>
head>
<body>
<script>
var MyApp = san.defineComponent({
template: `Hello {{name}}!
`,
initData: function() {
return {
name: 'lefe'
};
}
})
var app = new MyApp();
app.attach(document.body);
script>
body>
html>
上面的代码运行后,结果如下:
上面的例子中,通过数据来渲染 UI,当 name 值改变的时候 UI 将会自动改变。我们一起看看其它使用方式:
san 可以通过引用的方式(直接从线上获取代码)使用 san:
// 开发版本
<script src="https://unpkg.com/san@latest/dist/san.dev.js"></script>
// 线上版本
<script src="https://unpkg.com/san@latest"></script>
也可以通过本地引用来使用 san,通过 sudo npm install -g san
全局安装 san,复制一份到开发目录,这样方便看源码。看一个例子:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>day1title>
// 必须要加入
<script src="../san-sdk/dist/san.dev.js">script>
head>
<body>
<script>
var MyApp = san.defineComponent({
template: 'Hello {{name}}!
',
initData: function () {
return {
name: 'San'
};
}
});
var myApp = new MyApp();
myApp.attach(document.body);
script>
body>
html>
通过 san.defineComponent
来创建一个组件,函数原型为:
function defineComponent(proto, SuperComponent) {}
创建组件时需要传入一个对象,SuperComponent 是组件的父类,可忽略。
定义组件时需要提供一个模板:
<p>Hello {{name}}!p>
San使用基于HTML的模板,允许你以声明的方式将渲染的DOM和San实例的数据绑定
看几个例子:
例1:插值
var MyApp = san.defineComponent({
template: 'Hello {{name}}!
',
initData: function () {
return {
name: 'San'
};
}
});
例2:过滤器
<script>
var MyApp = san.defineComponent({
template: 'Hello {{name | upper}}!
',
initData: function () {
return {
name: 'san',
};
},
filters: {
upper: function(value) {
return value.toUpperCase();
}
}
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
例3:双向绑定
<script>
var MyApp = san.defineComponent({
template: ''
+ ''
+ ''
+ 'Hello {{name}}!
'
+ ''
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
例4:方法调用
<script>
var MyApp = san.defineComponent({
template: '{{sum(a, b)}}',
sum: function (a, b) {
return a + b;
},
initData: function () {
return {
a: 1,
b: 10,
}
}
})
var myApp = new MyApp();
myApp.attach(document.body);
</script>
例5:表达式
<script>
var MyApp = san.defineComponent({
template: ''
+ ''
+ 'a+b = {{ a+b }}
'
+ 'LiLei & HanMeiMei are a couple.
'
+ '1 + 1 < 3
'
+ 'a+100 = {{ a+ 100 }}
'
+ '',
initData: function () {
return {
a: 1,
b: 10,
}
}
})
var myApp = new MyApp();
myApp.attach(document.body);
</script>
通过 initData 来初始化数据,并通过 this.data
来对数据进行处理:
<script>
var MyApp = san.defineComponent({
template: 'Hello {{width}}!
',
initData: function () {
return {
width: 200,
top: [1,2,3,4,5,6],
left: -1000
};
},
attached: function () {
// 获取值
console.log(this.data.get('width')); // 200
// 获取多个值
let {top, left} = this.data.get();
console.log("top=" + top + "," + "left=" + left);
// 设置值
this.data.set('width', 300);
// 类似与 map 操作
this.data.apply('width', function(w) {
return w * 2;
});
// 数组添加数据
this.data.push('top', 4);
// 删除最后一条数据
this.data.pop('top');
// 在开头插入一条数据
this.data.unshift('top', 10);
// 在开头删除一条数据
this.data.shift('top');
// 删除数据
this.data.remove('top', 3);
this.data.removeAt('top', 1);
// 删除 0,2 下标对应的数据
this.data.splice('top', [0, 2]);
console.log("top=" + this.data.get('top'));
}
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
<script>
var MyApp = san.defineComponent({
template: ''
+ ' state = 0
'
+ ' state = 1
'
+ ' state = none
'
+ 'Hello!'
+ 'Offline'
+ '',
initData: function () {
return {
state: 1,
isOnline: false,
};
},
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
<script>
var MyApp = san.defineComponent({
template: ''
+ '{{ v }}
'
+ '',
initData: function () {
return {
persons: [
"lefe",
"wsy",
"sy"
]
};
},
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
事件是开发中最常用的行为管理方式。通过 on- 前缀,可以将事件的处理绑定到组件的方法上。在 San 中,无论是 DOM 事件还是组件的自定义事件,都通过 on- 前缀绑定,没有语法区分。
<script>
var MyApp = san.defineComponent({
template: ''
+ '{{ v }}
'
+ ' custom event
'
+ '',
initData: function () {
return {
persons: [
"lefe",
"wsy",
"sy"
]
};
},
// 点击事件、携带参数
clickAction: function(name) {
console.log("click = " + name);
},
// $event 将引用到 DOM Event 对象
clicker: function(event) {
console.log(event);
},
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
组件可以通过外部来创建:
<script>
// 定义一个插槽 slot 组件
var Pannel = san.defineComponent({
// 最终渲染顺序按 slot 定义的顺序渲染
template: ''
+ 'title'
+ '
'
+ '
'
+ '',
initData: function () {
return { name: 'Panel' };
},
toggle: function () {
console.log("toggle");
},
});
var MyApp = san.defineComponent({
components: {
'ui-pannel': Pannel
},
template: ''
+ 'I am top {{name}}
'
+ 'I am bottom {{name}}
'
+ ' ',
initData: function () {
return {
name: "lefe"
};
},
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
组件是 San 的基本单位,是独立的数据、逻辑、视图的封装单元。从页面的角度看,组件是 HTML 元素的扩展。从功能模式的角度看,组件是一个 ViewModel。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="../san-sdk/dist/san.dev.js">script>
head>
<body>
<script>
var Label = san.defineComponent({
template: 'hello world
',
});
var MyApp = san.defineComponent({
template: '' // + ' '
+ '- {{item}}
'
+ 'name : {{name}}
'
+ '
',
// 使用其它组件必须申明
components: {
'my_label': Label,
},
// 计算属性
computed: {
name: function () {
return this.data.get('fName') + ' ' + this.data.get('sName');
}
},
initData: function () {
return {
fName: "suyan",
sName: "wang",
}
},
compiled: function () {
console.log("compiled 组件视图模板编译完成");
},
inited: function () {
console.log("inited 组件实例初始化完成");
},
created: function () {
console.log("created 组件元素创建完成");
},
attached: function () {
console.log("attached 组件已被附加到页面中");
},
detached: function () {
console.log("detached 组件从页面中移除");
},
disposed: function () {
console.log("disposed 组件卸载完成");
},
})
var myApp = new MyApp({
// 初始化的时候携带数据
data: {
list: ['lefe', 'suyan', 'sy', 'wsy', 'lefex']
}
});
myApp.attach(document.body);
script>
body>
html>
推荐阅读:
Yog2 中的模块化思想
前端