Vue2.x 的响应式/模版解析/渲染
date
Jan 3, 2019
slug
vue2.x-reactivity-template-render
status
Published
tags
Vue2.x
Vuejs
summary
Vue2.x 的响应式/模版解析/渲染
type
Post
对于使用 vue 的同学来说,响应式、模版解析/渲染应该都很熟悉,vue 开发离不开这些特性。用了 vue 挺长时间了,写写总结。
响应式
创建一个普通的对象:
let t = {
name: 't1'
age: 19
}
console.log(t.age) // 19 那么如何监听age属性的访问与设置呢?
在es5中加入了一个api叫 Object.defineProperty,该api允许创建对象的属性自定义 get/set 函数。修改上面代码:
let t = {}
let name = 't1'
Object.defineProperty(obj, 'name', {
get() {
console.log('get')
return name
},
set(newVal) {
console.log('set')
name = newVal
}
})
console.log(obj.name)
// get
// t1
obj.name = 'george'
// set
在vue中我们可以模拟一下,大概过程如下:
let vm = {}
let data = {
name: 'lyn',
age: 20
}
for(let v in data) {
(function(v) {
Object.defineProperty(vm, v, {
get() {
return data[v]
},
set(newVal) {
data[v] = newVal
}
})
})(v)
}
为什么要将data的值全部代理至vm对象上?vue中就是这么写的,而且模版解析的时候会用到。
模板解析
我们经常在vue中使用vue模板,比如:
<div id="app">
<input v-model="title">
<button @click="add"></button>
</div>
<ul>
<li v-for="v in list">{{ v }}</li>
</ul>
模板的本质是字符串,其特点有:
- 包含 if/for 等逻辑指令
- 动态的(动态生成 dom )
- 最终转换为 html
综上,要将模板转换为 html ,只能使用 js 来操作。在 html/css/js 中,只有js能处理逻辑和渲染页面。
渲染
在当前文件引入vue文件,打开控制台,在源码中搜索code.render 在 return 前打印 code.render,观察模板被解析后生成的render函数,render函数最终返回vnode。如下:
with(this){ // this 就是 vm
return _c(
'div',
{
attrs:{"id":"app"}
},
[_c(
'div',
[
_c(
'input',
{
directives:[
{
name:"model",
rawName:"v-model",
value:(title), // vm.title 在这里就将model中的的值通过vm与view中的input数据联系了起来
expression:"title"
}
],
domProps:{
"value":(title)
},
on:{
"input":function($event){
// 在这里可以发现v-model 其实在vue模版内被封装了进去
if($event.target.composing)return;
title=$event.target.value
在这里就将view中的input中的值通过vm与model中的数据联系了起来与上面相对应实现双向绑定
}
}
}
),
_v(" "),
// 因为html中俩个标签间我们换行所以这里是空
_c(
'button',
{
on:{
"click":add
// 相当于 vm.add这里直接调用methods中定义的add函数
}
},
[_v("submit")]
)
]),
_v(" "),
_c('div',
[
_c(
'ul',
//v-for这里使用内部定义的 _l函数来处理
_l((list),function(item){return _c('li',[_v(_s(item))])})
)
]
)]
)
}
_c 创建标签,_v创建文本元素,_s toString(),这个_c 和 snabbdom 中的 h 函数很像,那vue中更新的操作会不会和 snabbdom 中的逻辑页相似呢?
vue 中的更新函数:
vm._update(vnode) {
const prevNode = vm._vnode
vm._node = vnode
if(!prevNode) {
vm.$el = vm.__patch___(vm.$el, vnode)
}else{
vm.$el = vm.__patch__(prevNode, vnode)
}
}
function updateComponent() {
vm._update(vm._render())
// vm._render 即生成上面包含 _c/_v/_s的模板解析函数
}
也就是说每次改变data,通过Object.defineProperty监听修改操作,一旦修改就调用 updateComponent 函数,利用vm._render方法生成新的 vnode。通过 vm.patch将vnode转化为html并更新至原生dom中。
总结
- 解析模板生成render函数
- 响应式开始监听
- 首次渲染,显示页面且绑定依赖
- data属性变化,再次渲染