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基本原理(三)

posted @ 2022-05-12 10:42  南风轻语、  阅读(69)  评论(0)    收藏  举报