vue双向绑定原理v-model


通过Object.defineProperty劫持数据发生的改变,如果数据发生改变了(在set中进行赋值的),触发update方法
进行更新节点内容(),从而实现数据双向绑定的原理

只保留重要代码
class Vue { //构造器 constructor(options) { this.$options = options; //观察者 this.$watchEvent = {}; //数据劫持 this.proxyData(); //观察者 模板更新 this.observer(); //编译渲染 this.compile(this.$el) }; /** * 编译 * 对模板变量 */ compile(node) { node.childNodes.forEach((item, index) => { //元素节点 if (item.nodeType === 1) { //双向绑定 if (item.hasAttribute("v-model")) { //获取绑定的data属性 let vmKey = item.getAttribute("v-model").trim(); if (this.hasOwnProperty(vmKey)) { //初始值 item.value = this[vmKey]; //监听其input事件 item.addEventListener('input', (event) => { //将值给到this.key this[vmKey] = item.value; }); }; }; //递归 this.compile(item); } //文本节点 if (item.nodeType === 3) { //正则 let reg = /\{\{(.*?)\}\}/g; //textContent属性表示一个节点及其后代的文本内容。 let text = item.textContent; //编写规则match匹配字符 vmkey是匹配到的data属性 item.textContent = text.replace(reg, (match, vmKey) => { //去掉两边空格 vmKey = vmKey.trim(); //给模板匹配到的每一个模板变量创建监听对象 let watch = new Watch(this, vmKey, item, "textContent") //判断有没有这个属性,没有则不需要进行监听 if (this.hasOwnProperty(vmKey)) { if (this.$watchEvent[vmKey]) { this.$watchEvent[vmKey].push(watch); } else { //初始化监听 //由于一个data可能在模板上使用多次,那么应该使用一个数组存储 this.$watchEvent[vmKey] = []; this.$watchEvent[vmKey].push(watch); } } //替换模板 return this.$data[vmKey] }) } }) }; /** * 数据劫持 * 监听this属性的修改和获取,访问this.$data */ proxyData() { for (let key in this.$data) { Object.defineProperty(this, key, { get() { return this.$data[key]; }, set(newVal) { console.log('监听到Vue实例对象的写入'); this.$data[key] = newVal; } }) } }; /** * 观察者 * 对this.$data的变化进行监听,更新模板 */ observer() { for (let key in this.$data) { let value = this.$data[key]; let that = this; Object.defineProperty(this.$data, key, { get() { return value; }, set(newVal) { value = newVal; //判断是否有这个属性的监听对象,意味着这个data属性是否在模板中被使用了 if (that.$watchEvent[key]) { //其value是一个数组,因为data属性可能在模板中使用了不止一次,这是在compile的使用push的 for (let val of that.$watchEvent[key]) { val.update(); } } } }) } } } /** * 监听对象 */ class Watch { constructor(vm, key, node, attr) { //vue实例对象 this.vm = vm; //属性名称 this.key = key; //节点 this.node = node; //节点属性如textContent this.attr = attr; }; //更新模板 update() { //将最新的vm data给到节点属性 this.node[this.attr] = this.vm[this.key]; } }

文章作者: iamfugui
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 iamfugui !
评论
  目录