Vue基本原理 (二)
Vue双向绑定的实现
1、简易双绑
首先,我们把注意力集中在这个属性上:Object.defineProperty。
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
语法:Object.defineProperty(obj, prop, descriptor)
什么叫做,定义或修改一个对象的新属性,并返回这个对象呢?
var obj = {};
Object.defineProperty(obj,'hello',{
get:function(){
//我们在这里拦截到了数据
console.log("get方法被调用");
},
set:function(newValue){
//改变数据的值,拦截下来额
console.log("set方法被调用");
}
});
obj.hello//输出为“get方法被调用”,输出了值。
obj.hello = 'new Hello';//输出为set方法被调用,修改了新值
通过以上方法可以看出,获取对象属性值触发get、设置对象属性值触发set,因此我们可以想象到数据模型对象的属性设置和读取可以驱动view层的数据变化,view的数据变化传递给数据模型对象,在set里面可以做很多事情。
在这基础上,我们可以做到数据的双向绑定:
let obj = {};
Object.defineProperty(obj, 'name', {
set: function(newValue){
console.log('触发setter');
document.querySelector('.text-box').innerHTML = newValue;
document.querySelector('.inp-text').value = newValue;
},
get: function(){
console.log('触发getter');
}
});
document.querySelector('.inp-text').addEventListener('keyup', function(e){
obj.name = e.target.value;
}, false);
html
<input class="inp-text" type="text">
<div class="text-box"></div>

**********************************************(两张图片)********************************************************************

以上只是vue的核心思想,通过对象底层属性的set和get进行数据拦截,vue的虚拟dom又是怎么实现的,且看以下分解。
2、虚拟DOM树
创建虚拟DOM:
var frag = document.createDocumentFragment();
view层的{{msg}}和v-model的编译规则如下:
html:
<div id="container">
{{ msg }}<br>
<input class="inp-text" type="text" v-model="inpText">
<div class="text-box">
<p class="show-text">{{ msg }}</p>
</div>
</div>
(半路发现可以设置彩色代码 (✪ω✪) )
view层做了多层嵌套,这样测试更多出现错误的可能性。
var container = document.getElementById('container');
//这里我们把vue实例中的data提取出来,更加直观
var data = {
msg: 'Hello world!',
inpText: 'Input text'
};
var fragment = virtualDom(container, data);
container.appendChild(fragment);
//虚拟dom创建方法
function virtualDom(node, data){
let frag = document.createDocumentFragment();
let child;
// 遍历dom节点
while(child = node.firstChild){
compile(child, data);
frag.appendChild(child);
}
return frag;
}
//编译规则
function compile(node, data){
let reg = /\{\{(.*)\}\}/g;
if(node.nodeType === 1){ // 标签
let attr = node.attributes;
for(let i = 0, len = attr.length; i < len; i++){
// console.log(attr[i].nodeName, attr[i].nodeValue);
if(attr[i].nodeName === 'v-model'){
let name = attr[i].nodeValue;
node.value = data[name];
}
}
if(node.hasChildNodes()){
node.childNodes.forEach((item) => {
compile(item, data); // 递归
});
}
}
if(node.nodeType === 3){ // 文本节点
if(reg.test(node.nodeValue)){
let name = RegExp.$1;
name = name.trim();
node.nodeValue = data[name];
}
}
}
解释:
1、通过virtualDom创建虚拟节点,将目标盒子内所有子节点添加到其内部,注意这里只是子节点;
2、子节点通过compile进行编译,a:如果节点为元素,其nodeType = 1,b:如果节点为文本,其nodeType = 3,
3、如果第二步子节点仍有子节点,通过hasChildNodes()来确认,如果有递归调用compile方法。

点击这里=》 vue基本原理(三)

浙公网安备 33010602011771号