Vue模版解析
首先我们来写一个Vue的todo-list
<!-- HTML -->
<div id="app">
<div>
<input type="text" v-model="title">
<button @click="add">submit</button>
</div>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
</div>
// JavaScript
<script src="./vue2.5.13.js"></script>
<script>
var data = {
title: '',
list: []
}
var app = new Vue({
el: "#app",
data: data,
methods: {
add: function() {
if(this.title){
this.list.push(this.title)
}
this.title = ''
}
}
})
</script>
看到这段代码大家肯定不陌生,这些v-for、v-model、@click命令是vue定义的。但是HTML根本不认识这些玩意啊,从来没见过这些。而且最后我们打开浏览器控制台,生成的HTML结构里面也没有这些,那么这之间是发生了事情呢?
解析模版成render函数
其实todo-list所写的HTML代码,看似是HTML代码,但是实际上并不是,它会被Vue
模版解析成一段JavaScript代码,
那我们在哪里能看到render函数呢?
Vue-2.5.13源代码
搜索code.render,并且在其函数内添加alert(code.render)来获取整个code.render返回的东西
//10677code.render
alert(code.render);
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
还是以上面的todo-list为例,我们最终生成得到的是编译后的数据:
with(this){return _c('div',{attrs:{"id":"app"}},[_c('div',[_c('input',{directives:[{name:"model",rawName:"v-model",value:(title),expression:"title"}],attrs:{"type":"text"},domProps:{"value":(title)},on:{"input":function($event){if($event.target.composing)return;title=$event.target.value}}}),_v(" "),_c('button',{on:{"click":add}},[_v("submit")])]),_v(" "),_c('ul',_l((list),function(item){return _c('li',[_v(_s(item))])}))])}
让我们手动格式化一下:
with(this){
return _c(
// 创建节点 _c create
'div',
{
attrs:{"id":"app"}
},
[
_c(
'div',
[
_c(
'input',
{
// v-model
directives:[
{
name:"model",
rawName:"v-model",
value:(title),
expression:"title"
}
],
attrs:{"type":"text"},
domProps:{"value":(title)},
on:{
"input":function($event){
if($event.target.composing)return;
title=$event.target.value
}
}
}
),
// 模版的换行符
_v(" "),
_c(
'button',
{
on:{
"click":add
}
},
// _v 创建文本节点
[_v("submit")]
)
]
),
_v(" "),
_c(
'ul',
// _l for循环
_l(
(list),
function(item){
return _c(
'li',
[
_v(
_s(item)
// _s toString
)
]
)
}
)
)
]
)
}
这和snabbdom的vdom结构非常相似。
注意: with 这是Vue在内部使用的,平时开发不推荐使用,这里 with(this)
中的this
指向了Vue的实例vm
,在其后面value:(title)
访问的title
可以理解为vm.title
JavaScript查找某个未使用命名空间的变量时,会通过作用域链来查找,作用域链是跟执行代码的context或者包含这个变量的函数有关,具体查看MDN-with
这段代码我们看到了什么?
-
模版中所有的信息都被render函数包裹着了
-
模版中所有的data中的属性,都变成了JS的变量
-
模版中所有的v-model v-for v-on 都变成了JS的逻辑
-
最终render函数返回了一个vnode
最后说一句话:其实在最初Vue模版其实就是通过Vue中的render函数编译解析,返回一个vnode对象,之后会通过响应式来监听数据变化更新视图
render函数和vdom
vm._update(vnode) {
// 上一次更新的vnode
const prevVnode = vm._vnode
// 下一次对比的vnode
vm._vnode = vnode
// 第一次执行
if(!prevVnode) {
vm.$el = vm.__patch__(vm.$el,vnode)
} else {
vm.$el = vm.__patch__(prevVnode, vnode)
}
}
function updateComponent() {
// 将生成的JS模版当参数传递
vm._update(vm._render())
}