Vue响应式实现流程
- 模板被解析成render函数,绑定数据依赖
- 设置data中数据的getter和setter特性。属性变化,重新触发render
Vue的模板解析
Vue的模板就是HTML加上内置的模板语法,如v-if
,v-bind
等等
- 模板最终会转换成JS代码(render函数)
- 模板具有逻辑,如
v-if
和v-for
,必须通过JS实现
模板
<input id="item" v-model="content"/>
解析
// 只列出了主要内容
with(this){
return _c{
'input',
{
// 属性名
attrs:{'id':'item'},
// 指令
directives:[
{
name:'model',
rawName:'v-model',
// 对应的数据
value:(content)
}
],
// 绑定的事件
on:{
"input" function($event){
title = $event.target.value
}
}
}
}
}
with(this)
指的是该Vue实例_c (createElement)
就是render函数,用来创建虚拟DOM
在模板转化为JS代码的阶段,如果使用了v-model
,就会在生成的虚拟DOM里面自动监听input
事件。如果元素的内容被修改,那么就会触发input
事件,该事件会修改Vue组件实例中对应的数据的值。这样一来,页面->数据这个方向的数据绑定就实现了。
Object.defineProperty
给属性设置getter和setter
var obj = {};
// 设置一个中间变量,避免递归
var _content = '0';
Object.defineProperty(obj,'content',{
get(){
console.log('get success')
return _content;
},
set(value){
console.log('set success')
_content = value;
}
})
// obj.content
// get success
// 0
// obj.content = 1
// get success
// 1
// obj.content
// get success
// 1
所以Vue数据->页面的数据绑定就是在属性的setter里面调用render函数。这样每次数据更新,都会重新渲染页面
模拟实现
<div>
<input id="item"/>
</div>
var item = document.getElementById('item')
var obj = {};
var _content = 0;
Object.defineProperty(obj,'content',{
get(){
return _content;
},
set(value){
_content = value;
// 数据变化,重新渲染页面数据
item.value = _content;
}
})
//渲染初始值
item.value = obj.content
//页面内容变化,修改绑定数据
item.addEventListener('input',(event)=>{
obj.content = event.target.value
})
注:由于Object.defineProperty
的性能问题,Vue2.x并没有实现对数组和对象属性变化的检查。Vue3.x已经使用Proxy
来进行双向绑定,具体可参考
vue3.0 尝鲜 -- 摒弃 Object.defineProperty,基于 Proxy 的观察者机制探索