Vue的绑定原理(注释版)

包含:选择器属性、闭包、遍历对象、虚拟DOM树、观察者模式、Object.defineProperties()方法改造data中的变量、递归、匿名函数等

代码(具体看注释):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>welecome</h1>
    <h2>积分:<span>{{score}}</span></h2>
    <h2>用户名:<span>{{uname}}</span></h2>
    <h2>积分:<span>{{score}}</span></h2>
    <script>
        var data = {
            uname: 'dingding',
            score: 3000
        }
        //VUE第一步:改造data
        //ES5新函数:Object.keys(obj) 获取obj对象中的所有属性名的列表
        //获得data对象中所有变量名(属性名)
        var keys = Object.keys(data);
        console.log(keys);
        //遍历data中每个属性
        for(var key of keys) {
            // 每遍历一个属性,就要改造这个属性为访问器属性
            // key变量是循环变量,当循环结束时,key四种指向最后一个属性名
            // 而访问器属性中的get()和set()函数,其中用到了key变量,但是不是立刻调用,而是在将来,有人试图修改属性值时,才发出get()和set(),到那个时候,key变量早就不是当前属性名了
            // 所以,在for of循环内使用匿名函数自调用,并传参的方式,将当前正在遍历的key 值,传入匿名函数中,交给匿名函数的形参变量key(局部变量)来保存,以此形成闭包结构。
            // 结果,每个get()和set()函数,即使将来要访问key,也是访问的闭包中专属的key
            (function(key) {
                // key是局部变量
                //每遍历data中一个变量,就改造这个变量
                Object.defineProperties(data,{
                    //先定义一个以_开头,后跟当前变量名的隐藏变量,实际保存当前属性值。
                    //_uname
                    //`_${key}` //错误:在定义对象时,属性名禁止使用反引号
                    //"_"+key //错误:在定义对象时,属性名禁止拼接
                    [`_`+key]: {//_name
                        value: data[key],//data["uname"] = "dingding"
                        writable: true,
                        enumerable: false,
                    },
                    [key]: {//uname
                        get() {
                            return this[`${key}`];//return this["_uname"]
                        },
                        set(value) {
                            this[`_${key}`] = value; //this["_uname"] = value
                            console.log(`${key}属性变了`);
                            //知道变量变化,就自动调用change()方法,扫描数你DOM树,找到受影响的元素,更新真事DOM树上对应节点的内容
                            change(key);
                        },
                        enumerable:true
                    }
                })(key);
            })
        }
        Object.seal(data);//将每个属性的configurable: false
        console.log(data);

        //Vue第二部:构建虚拟DOM树,并完成首次绑定元素内容
        //准备数组保存所有可能受影响的元素
        var arr = [];
        //递归遍历指定父元素下所有内容为{{***}}的元素
        function getChildren(parent) {
            //获取当前父元素下直接子元素
            var children = parent.children;
            //遍历每个直接子元素
            for(var c of children) {
                //如果子元素还有子元素
                if(c.children.length > 0) {
                    //就继续递归遍历子元素的下一级
                    arguments.callee(c);
                } else {//否则,子元素没有根子一级元素,说明当前元素的内容了
                    //遍历data中每个变量名
                    for(var key of keys) {
                        //每遍历一个变量名,就用当前元素的内容和变量名作比较
                        //只有内容{{}}中的变量名和data中某个属性名一致,才有必要将元素添加到虚拟DOM树数组中
                        if(c.innerHTML == `{{${key}}}`) {
                            arr.push({
                                elem: c,//虚拟DOM树中每个元素,不但要记录DOM元素对象地址
                                innerHTML: c.innerHTML//而且还要记录这个DOM元素哪个属性发生变化,打算变成哪个变量的值
                            });
                            //在首次扫描是,还需要首次填充页面内容
                            c.innerHTML = data[key];
                        }
                    }
                }
            }
        }
        getChildren(document.body);
        console.log(arr);

        //封装虚拟DOM树并修改内容的方法
        //根据奔驰发生变化的属性,查找虚拟DOM树中受影响的节点,跟真事DOM树中节点的内容
        //  比如       score
        function change(key) {
            //遍历虚拟DOM树
            for(var obj of arr) {
                //只有当前节点的内容收到当前属性key变化影响时
                if(obj.innerHTML == `{{$key}}`) {
                    //才更新真事DOM元素的内容为data中当前key属性的瞬时值
                    obj.elem.innerHTML = data[key]
                }
            }
        }
        //测试响应系统
        setInterval(function() {
            data.score+=100;
        },2000);
    </script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值