前端框架 san 使用
前端的框架作用其实都是为了让你更快速、更好地完成前端开发,一个需求如果单纯使用 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>
<!-- <script ></script> -->
</head>
<body>
<script>
var MyApp = san.defineComponent({
template: `<p>Hello {{name}}!</p>`,
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,复制一份到开发目录,这样方便看源码。看一个例子:
<!DOCTYPE html>
<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>day1</title>
// 必须要加入
<script src="../san-sdk/dist/san.dev.js"></script>
</head>
<body>
<script>
var MyApp = san.defineComponent({
template: '<p>Hello {{name}}!</p>',
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: '<p>Hello {{name}}!</p>',
initData: function () {
return {
name: 'San'
};
}
});
例2:过滤器
<!-- 过滤器,name 是 upper 的输入 -->
<script>
var MyApp = san.defineComponent({
template: '<p>Hello {{name | upper}}!</p>',
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: ''
+ '<div>'
+ '<input value="{= name =}" placeholder="please input">'
+ '<p>Hello {{name}}!</p>'
+ '</div>'
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
例4:方法调用
<!-- 方法调用 -->
<script>
var MyApp = san.defineComponent({
template: '<u>{{sum(a, b)}}</u>',
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: ''
+ '<div>'
+ '<p>a+b = {{ a+b }}</p>'
+ '<p>LiLei & HanMeiMei are a couple.</p>'
+ '<p>1 + 1 < 3</p>'
+ '<p>a+100 = {{ a+ 100 }}</p>'
+ '</div>',
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: '<p>Hello {{width}}!</p>',
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: '<div>'
+ '<p s-if="state === 0"> state = 0 </p>'
+ '<p s-elif="state === 1"> state = 1 </p>'
+ '<p s-else> state = none </p>'
+ '<span s-if="isOnline">Hello!</span>'
+ '<span s-else>Offline</span>'
+ '</div>',
initData: function () {
return {
state: 1,
isOnline: false,
};
},
});
var myApp = new MyApp();
myApp.attach(document.body);
</script>
循环
<script>
var MyApp = san.defineComponent({
template: '<div>'
+ '<p s-for="v, index in persons">{{ v }}</p>'
+ '</div>',
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: '<div>'
+ '<p s-for="v, index in persons" on-click="clickAction(v)">{{ v }}</p>'
+ '<p on-click="clicker($event)"> custom event </p>'
+ '</div>',
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: '<div>'
+ '<div class="head" on-click="toggle">title</div>'
+ '<p><slot name="bottom"></slot></p>'
+ '<p><slot name="top"></slot></p>'
+ '</div>',
initData: function () {
return { name: 'Panel' };
},
toggle: function () {
console.log("toggle");
},
});
var MyApp = san.defineComponent({
components: {
'ui-pannel': Pannel
},
template: '<div><ui-pannel>'
+ '<h1 slot="top">I am top {{name}}</h1>'
+ '<h4 slot="bottom">I am bottom {{name}}</h4>'
+ '</ui-pannel></div>',
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>Document</title>
<script src="../san-sdk/dist/san.dev.js"></script>
</head>
<body>
<script>
var Label = san.defineComponent({
template: '<p>hello world</p>',
});
var MyApp = san.defineComponent({
template: '<div>'
// + '<my_label s-ref="label"></my_label>'
+ '<ul><li s-for="item in list">{{item}}</li></ul>'
+ '<p>name : {{name}}</p>'
+ '</div>',
// 使用其它组件必须申明
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>
推荐阅读:
前端