Vue双向绑定原理

博客围绕Vue双向绑定框架展开,Vue是前端开发常用框架,双向绑定是其重要特性,在Web开发中应用广泛,能有效提升开发效率和用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vue双向绑定原理

// An highlighted block
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>双向绑定实现原理</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model='msg'>
        <input type="text" v-model='name'>
        {{msg}} {{name}}
        <div>双向绑定</div>
        <div>{{name}}</div>
    </div>

     <script>
        // ===数据层===
        // 3.将数据和节点挂载到一起
            // 分析:一个数据可能对应多个节点
        let obj = {msg:new ViewModel('欢迎你'),name:new ViewModel('彩聪') }

        // 3.1 写个方法把数据都传入
        function ViewModel(data){
            this.data = data ; //this.data 代表当前的值
            this.nodes = []; //放节点的盒子
        }
        // 3.2这是将节点和数据邦在一起,传入节点node
        ViewModel.prototype.bindNode =function(node){
            // 把节点和数据放入父盒子中
            this.nodes.push(node)
            console.log(this.nodes)
        }

        // 3.3 创建更新原型方法,可以将节点一次渲染成想要的结果
        ViewModel.prototype.update= function(){
            // 遍历存放节点的盒子
            this.nodes.forEach(node =>{
                // 判断类型
                if(node.nodeType ===1){
                    // 把v-model添加进去
                    node.value =this.data;
                }else{  // {{name}}{{msg}}
                    // 先匹配 到文本内容    4.1node.my ,原textContent
                    node.textContent = node.my.replace(/\{\{([^}]*)\}\}/g,function(){
                        // 这里需要返回的是obj的实例对象所以内容,在把data的值返回
                        return obj[arguments[1]].data
                    })
                }
               
            })
        }
        // 4输入框修改,同步数据方法
        ViewModel.prototype.setVal=function(newVal){
            if(newVal !==this.data){  //判断设置的值和老值相同
                // 把数据的值改成最新,然后调用update更新
                this.data =newVal
                this.update()

            }
        }
        // 5. 创建获方法
        ViewModel.prototype.getVal =function(){
            return this.data
        }

        // ====试图层====
        //  1.将数据和节点挂载到一起
                // 1.1把需要的变化的标签的内容都取出来#app,遍历所有
        function compile(el){
            // 接收#app\,我们不要直接操作节点,可能会导致页面回流
            // 我们操作dom,不停的循环,添加dom,页面重复渲染,会导致回流
            let ele = document.querySelector(el)
            // 创建文档碎片,用于把数据放入里面
            let fragement = document.createDocumentFragment()
            // 去ele下的第一个元素,知道取完为止并且放到文档内容中
            let child;
            while(child = ele.firstChild){
                // 存入文档碎片中
                fragement.appendChild(child)
            }

            // 因为fragement.childNodes,只查找到第一个子元素,
            // 后面的没有查到,创建查到所有子元素的方法
        function reolace(fragement){  //2. 用递归判断是否有我们想要的标签
                       //获取所有的属性
            // fragement 是一个nodeList类数组:特点Array.prototype.slice.call
            // 用Es6 语法获取到把fragement转换成数组,用childNodes获取到子元素
            Array.from(fragement.childNodes).forEach(node=>{
                // 判断node节点是标签,还是文本
                console.log(node)
                if(node.nodeType ===1){  //元素节点,标签
                    //还是nodeList,用attributes取到节点上的所以属性,type= "text" v-model="a"
                    Array.from(node.attributes).forEach(attr=>{
                        console.dir(attr)
                        // 打印出来后,查看到name,value对应我们的元素所以我们获取
                        let {name,value} = attr; //value包含 v-model='name' ,'msg'
                        // 用E6 includes判断name是否包含v-这个开头的元素
                        if(name.includes('v-')){
                            console.log(node)
                            // 3.2调用bindNode的原型方法,把节点node传入,
                            obj[value].bindNode(node) //把输入框放入
                            // 4.2监听到输入框的变化,传一个值修改
                            node.addEventListener('input',function(e){
                                // 调用输入框的name,然后调用修改方法,更新数据
                                obj[value].setVal(e.target.value)
                            })
                        }
                    })
                }


                let reg = /\{\{([^}]*)\}\}/g;   //判断当前有没有{{}}的元素
                let text = node.textContent;  //输出看到的内容节,包含{{name}} {{msg}}
                if(node.nodeType === 3&&reg.test(text)){  //文本节点,{{name}} {{msg}}
                    // 4.1自定义属性,保留原有的内容。这样替换就不会破坏原有的属性
                    node.my = text
                    console.log(text)
                    // 放入{{name}} {{msg}},用reg和text
                    text.replace(reg,function(){
                        // 3.2嗲用bindNode,把数据关联起来
                         obj[arguments[1]].bindNode(node)

                    })
                }

                // 2.1把前面的代发放入回调后,用长度判断
                if(node.childNodes.length){
                    reolace(node) //如果有div---div嵌套关系,继续查找
                }
            })
        }

            //todo 这里干些事,是不会导致页面回流的
            // 这是把存入的文档碎片内容提出放回原来位置
            reolace(fragement) //编译后要调用updata方法
            // 3.4将一个对象转换成数组
            Object.keys(obj).forEach(key=>{ //{msg:new ViewMode(),name} =>[msg,name]
                obj[key].update()
            })
            ele.appendChild(fragement)
        }   
        // 通过调用来传id标签来实现
        compile('#app')


     </script>   
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值