vue响应式原理

24 篇文章 0 订阅
20 篇文章 0 订阅
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="v">{{ v }}
    </div>
</body>
</html>

<script>

    /*
    *Vue响应式原理
    *在创建Vue对象的时候,将options.data里面变量通过Object.defineProperty代理到Vue实例里面,即可通过this.xxx获取变量
    *然后遍历所有文本节点,根据正则找到mustach语法里面的变量名,然后将Vue实例对应的变量名的值赋值到节点当中
    *双向绑定:遍历监听所有input节点,找到将对应的v-mode属性的变量名,将input框内的值赋值给Vue实例的对应变量名
    *
    */
    class Vue{
        constructor(options){
            this.$options=options
            this.$data=options.data
            this.$el=options.el
            
            //将data添加到响应式系统中
            new Observer(this.$data)

            //代理this.$data数据
            Object.keys(this.$data).forEach(key=>{
                this._proxy(key)
            })
            
            //处理el
            new Compiler(this.$el,this)
        }

        _proxy(key){
            //Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,所以可以通过this.xxx获取Vue实例里面的data的值,详见底部demo
            Object.defineProperty(this,key,{
                configurable : true,
                enumerable : true,
                set(newValue){
                    this.$data[key]=newValue
                    console.log(newValue);
                },
                get(){
                    console.log(this.$data[key]);
                    return this.$data[key]
                }
            })
        }
    }

    class Observer{
        constructor(data){
            this.data=data

            Object.keys(data).forEach(key=>{
                this.defineReactive(this.data,key,data[key])
            })
        }

        defineReactive(data,key,val){
            const dep=new Dep()

            Object.defineProperty(data,key,{
                configurable : true,
                enumerable : true,
                get(){
                    if(Dep.target){
                        dep.addSub(Dep.target)
                    }
                    return val
                },
                set(newVal){
                    if(newVal==val){
                        return
                    }
                    val=newVal
                    dep.notify()
                }
            })
        }
    }

    class Dep{
        constructor(){
            this.subs=[]
        }

        addSub(sub){
            this.subs.push(sub)
        }

        notify(){
            this.subs.forEach(sub=>{
                sub.update()
            })
        }
    }

    class Watcher{
        constructor(node,name,vm){
            this.node=node
            this.name=name
            this.vm=vm
            Dep.target=this
            this.update()
            Dep.target=null
        }

        update(){
            this.node.nodeValue=this.vm[this.name]
        }
    }

    const reg=/\{\{(.+)\}\}/

    class Compiler{
        constructor(el,vm){
            this.el=document.querySelector(el)
            this.vm=vm
            
            this.frag=this._createFragment()
            this.el.appendChild(this.frag)
        }

        _createFragment(){
            //创建片段对象
            const frag=document.createDocumentFragment()

            let child 
            while(child=this.el.firstChild){
                this._compile(child)
                frag.appendChild(child)
            }
            return frag
        }

        _compile(node){
            //遍历所有节点
            if(node.nodeType===1){      //标签节点
                const attrs=node.attributes
                if(attrs.hasOwnProperty("v-model")){
                    const name=attrs['v-model'].nodeValue
                    node.addEventListener("input",e=>{
                        //如果是标签节点,监听标签节点的值,赋值给Vue实例对应的值,
                        //赋值时触发Observer.defineReactive 内部的set方法,更新所有节点的内容
                        this.vm[name]=e.target.value
                    })
                }
            }

            if(node.nodeType===3){      //文本节点
                if(reg.test(node.nodeValue)){
                    const name=RegExp.$1.trim()
                    //如果是文本节点,获取文本节点的变量名,将Vue实例的变量名对应的值赋值到文本节点,
                    new Watcher(node,name,this.vm)
                }
            }
        }
    }

    const app=new Vue({
        el : "#app",
        data : {
            v : "aa",
            v2 : "ccc"
        }
    })

    //Object.defineProperty()demo
    const obj2={
        name : "aaa"
    }

    console.log(obj2);
    Object.defineProperty(obj2,"player",{
        get(){
            return "李连杰"
        }
    })
    console.log(obj2);
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

y_w_x_k

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值