模板引擎-vue中的模板如何被解析,指令如何处理
模板是什么
<div id='app'> <div> <input v-model="title"/> <button v-on:click="add">submit</button> </div> <ul> <li v-for="item in list"> {{item}} </li> </ul> </div>
这是模板,那么模板是什么呢?
1、本质是字符串,是以字符串存在的,只不过像html
2、有逻辑,比如判断,循环这些,如v-if,v-for等,怎么会有逻辑呢,之前写html就没逻辑
3、与html格式很像,但有很大区别。首先html在语法上是不认识v-if,v-for这些的。第二个是html是静态的,没有逻辑,vue是动态的,有逻辑的。它们只是格式很像
4、但是最终模板还是要转换为html来显示的。那么他是怎么做到的呢?
首先模板最终必须转换成js代码,因为:
第一个模板有逻辑,有v-if,v-for。必须用js才能实现(图灵完备的语言)
第二个模板要转化为html渲染页面,必须用js才能实现
因此,模板最终要转换成一个js函数(render函数,也就是渲染函数)
首先了解下with
var obj = { name: 'zhangsan', age: 20, getAddress: function(){ alert('beijing'); } } // 不用with function fn(){ alert(obj.name); alert(obj.age); obj.getAddress(); } fn(); // 使用width function fn1(){ with(obj){ alert(name); alert(age); getAddress(); } } fn1();
在实际开发中,尽量不要使用with。fn是我们正常的使用。fn1使用with的情况。两是同样的效果,用with的里面,都不写是谁的属性,是谁统一的用with包起来。这个可读性可能没那么强。
我们看最简单的一段模板
<div id="app"> <p>{{price}}</p> </div>
这个模板最终生成的函数是下面这个
/** * _c : 创建dom标签 * _v : 创建文本节点 * _s : toString */ function render(){ with(this){ // this 就是 vm return _c( '<div>', { attrs:{"id":"app"} }, [ _c('p',[_v(_s(price))]) ] ) } }
这个this就是vm这个实例,这个_c就是vm._c,用来创建dom标签的。第一个参数是个div。第二个参数是个对象,对象里面有属性。第三个参数是个数组,数组里面只有一个元素,这个_c肯定也是vm._c。这个_c第一个参数是p,第二个参数是个数组,里面也是一个数组,_v(_s(price)),这里面的price肯定是vm.price,就是data.price。然后前面的_s就是vm._s,就是toString函数。_v也是vm._v,用来创建文本节点的。
总结:
模板中所有信息都包含在render函数中。
this即vm
price即this.price即vm.price即data.price。
_c即this._c即vm._c
render函数
从哪里可以看到render函数?
看一下todo-list demo的render函数
<!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>Document</title> </head> <body> <!-- 模板 --> <div id="app"> <input v-model='title'/> <button v-on:click='add'>submit</button> <ul v-for='item in list'> {{item}} </ul> </div> <!-- 源码 --> <script src="./vue-2.6.10.js"></script> <script> var data = { title: '', list: [] } // 初始化 vue 实例 var vm = new Vue({ el: '#app', data: data, methods: { add: function(){ this.list.push(this.title); this.title = '' } } }) </script> </body> </html>
然后通过这个例子,看看vue的render函数是什么样子的,在源码搜索code.render,然后打印出来
with(this) { return _c('div',
{ attrs: { "id": "app" } }, [ _c('input',
{ directives: [{ // 当title发生变化,会赋值给value,从而响应到input name: "model", rawName: "v-model", value: (title), expression: "title" }], domProps: { "value": (title) }, on: { // input里面的数据改变,事件监听到,会赋值给 title "input": function($event) { if ($event.target.composing) return; title = $event.target.value } } }), _v(" "), _c('button', { on: { "click": add } }, [_v("submit")] ), _v(" "), _l( (list), function(item) { return _c('ul', [_v("\n " + _s(item) + "\n ")]) } ) ], 2) }
是这个样子的。这就是这个demo所对应的render函数。在创建input的时候第二个参数有个directives。叫做指令,指令名字是model。value是title,也就是vm.title。后面_v(''),_v表示创建文本节点,主要是input和button有个换行,如果不换行,就不会有_v去创建一个空的文本节点。_l就是一个数组,针对list返回li的标签。