vue双向绑定的简化代码原理

为了能够快速读懂代码,首先要先弄明白以下三个概念:

1、观察者(observer):也就是数据监听器,负责数据对象的所有属性进行监听劫持,并将消息发送给订阅者进行数据更新
2、订阅者(watcher):负责接收数据的变化,更新视图(view),数据与订阅者是一对多的关系。
3、解析器(compile):负责对你的每个节点元素指令进行扫描和解析,负责相关指令的数据初始化及创造订阅者

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="myApp">
        <input type="button" value="加个!" z-on:click="fn">
        <input type="text" style="width:400px" z-model="site">
        <div z-text="site"></div>
        <div z-html="site"></div>
    </div>
</body>
<script>
function Zhou (options) {
  this.$el = document.querySelector(options.el) // 指定挂载的元素
  this.$data = options.data // 存放的数据
  this.$methods = options.methods // 存放你的方法
  this.binding = {} // 存放所有数据的订阅者, 最终结果为{数据属性:[订阅者对象, 订阅者对象]}
  this.observe() // 调用观察者,对数据进行劫持
  this.compile(this.$el) // 对元素指令进行解析,订阅者也是在这里创建的
}

Zhou.prototype.observe = function () {
  var value="";// 定义用于存放数据属性值的变量value
    for(var key in this.$data){ // 遍历数据对象
        value=this.$data[key];// 对象属性值
        this.binding[key]=[];// 数据订阅者初始化,是一个数组,
        let that = this
        Object.defineProperty(this.$data,key,{// 开始设置劫持
            get(){
                return value;// 读取值为value
            },
            set(v){// v为设置的值
                if(v!==value){// 当设置的值与当前值不相等时
                    value=v;// 将读取值更新为v
                    that.binding[key].forEach(watcher=>{
                        watcher.update();// 通知与本数据相关的订阅者们进行视图更新
                    })
                }
            }
        })
    }
}
// 定义解析器,解析指令,创建订阅者
Zhou.prototype.compile = function (el) {
  var nodes=el.children;// 获得所有子节点
    for(var i=0;i<nodes.length;i++){// 对子节点进行遍历
        var node=nodes[i];// 具体节点
        if(node.children.length>0)// 判断是否具有子节点
            this.compile(node);// 如果有子点进行递归操作
        if(node.hasAttribute("z-on:click")){// 该节点是否拥有z-on指令
            var attrVal=node.getAttribute("z-on:click");// 得到指令对应的方法名
            // 为元素绑定click事件,事件方法为$methods下的方法,并将其this指向this.$data
            node.addEventListener("click",this.$methods[attrVal].bind(this.$data))
        }
        if(node.hasAttribute("z-model")){// 该节点是否拥有z-model指令
            var attrVal=node.getAttribute("z-model");// 获得指令对应的数据属性
            node.addEventListener("input",((i)=>{// 为指令添加input事件
                this.binding[attrVal].push(new Watcher(node,"value",this,attrVal));// 为该数据添加订阅者
                return ()=>{
                    this.$data[attrVal]=nodes[i].value;// 更新$data的属性值,会在观察者中进行劫持
                }
            })(i))
        }
        if(node.hasAttribute("z-html")){// 该节点是否拥有z-html指令
            var attrVal=node.getAttribute("z-html");// 获得指令对应的数据属性
            this.binding[attrVal].push(new Watcher(node,"innerHTML",this,attrVal));
        }
        if(node.hasAttribute("z-text")){// 该节点是否拥有z-text指令
            var attrVal=node.getAttribute("z-text");// 获得指令对应的数据属性
            this.binding[attrVal].push(new Watcher(node,"innerText",this,attrVal));
        }
    }
}

function Watcher (el, attr, vm, val) {
  this.el = el // 指令所在元素
  this.attr = attr // 绑定的属性名
  this.vm = vm // 指令所在实例
  this.val = val // 指令的值
  this.update()
}
Watcher.prototype.update = function () {
  this.el[this.attr] = this.vm.$data[this.val]
}

var vm = new Zhou({
      el: "#myApp",
      data: {
          site: "<a href='http://www.baidu.com'>zhouyajing</a>"
      },
      methods: {
          fn() {
              this.site += "!";
          }
      }
  })
</script>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue2 双向绑定原理涉及到的代码比较多,涉及到 Vue 实例的初始化、数据劫持、依赖收集、更新等过程,下面是一个简化后的代码示例,仅供参考: ```javascript // 定义一个 Observer 类,用于对数据对象进行劫持 class Observer { constructor(data) { this.data = data; this.walk(data); } walk(data) { Object.keys(data).forEach(key => { this.defineReactive(data, key, data[key]); }); } defineReactive(data, key, value) { const dep = new Dep(); Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { dep.depend(); return value; }, set(newValue) { if (newValue === value) { return; } value = newValue; dep.notify(); } }); } } // 定义一个 Watcher 类,用于进行依赖收集和更新 DOM 的操作 class Watcher { constructor(vm, expOrFn, cb) { this.vm = vm; this.getter = parsePath(expOrFn); this.cb = cb; this.value = this.get(); } get() { Dep.target = this; const value = this.getter.call(this.vm, this.vm); Dep.target = null; return value; } update() { const oldValue = this.value; this.value = this.get(); this.cb.call(this.vm, this.value, oldValue); } } // 定义一个 Dep 类,用于存储 Watcher 对象并在数据变化时通知 Watcher 进行更新 class Dep { constructor() { this.subs = []; } depend() { if (Dep.target) { this.subs.push(Dep.target); } } notify() { this.subs.forEach(watcher => { watcher.update(); }); } } Dep.target = null; // 解析表达式路径 function parsePath(path) { const segments = path.split('.'); return function(obj) { for (let i = 0; i < segments.length; i++) { if (!obj) return; obj = obj[segments[i]]; } return obj; }; } // 初始化 Vue 实例时进行数据劫持和依赖收集 function observe(value) { if (!value || typeof value !== 'object') { return; } return new Observer(value); } // 将 Vue 实例中的数据对象进行响应式处理 function defineReactive(obj, key, val) { const dep = new Dep(); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { dep.depend(); return val; }, set(newValue) { if (newValue === val) { return; } val = newValue; dep.notify(); } }); } // 创建 Vue 实例时进行数据初始化 class Vue { constructor(options) { this.$data = options.data; observe(this.$data); new Watcher(this, options.render, () => { console.log('DOM updated'); }); } } // 使用示例 const vm = new Vue({ data: { message: 'Hello, Vue!' }, render() { console.log('render'); } }); vm.$data.message = 'Hello, World!'; // 触发 DOM 更新 ``` 上述代码仅为 Vue 双向绑定原理代码示例,实际实现中还需要考虑更多的细节问题,例如模板编译、指令解析等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值