首页天道酬勤Vue双向绑定原理,vue的数据双向绑定

Vue双向绑定原理,vue的数据双向绑定

张世龙 05-04 02:19 10次浏览

当我们学习angular和vue时,其双向绑定给我们的开发带来了很多便利。 今天我们来分析一下vue双向绑定的原理。

简易vue源地址: https://github.com/jang zhenfei/simple-vue

1.vue双向绑定原理

vue.js采用将数据劫持与发布-订阅模型相结合的方式(Object.defineProperty ) )劫持每个属性的setter、getter,从而更改数据首先,让我们看一下名为Object.defineProperty ()的方法。

var obj={}; object.defineproperty(obj,' name ',{ get: function ) { console.log ) '我获取了) return val; },set:function(newval ) { console.log ) (我已设置) } ) obj.name='fei ); 在//obj中设置name属性时,名为set的方法var val=obj.name; 得到//obj的name属性后,将触发get方法

已知vue通过数据劫持进行数据绑定,其中最核心的方法是通过Object.defineProperty ) )实现属性侵占,并在get或set方法中加入其他触发函数

2.实现最简单的双向绑定

我们知道可以在Object.defineProperty ()中劫持数据。 是的,属性在赋值时触发set方法

! doctypehtmlhtmllang=' en ' headmetacharset=' utf-8 ' title document/title/headbodydivid=' demo '/div input type=' demo ) ) var InP=document.query selector (# InP ) ) object.defineproperty ) ),set 333666 demo.innerHTML=newVal; }}InP.addeventlistener('input ',function(e ) e ) )为obj的name属性赋值并触发该属性的set方法obj.name=e.target.varget . ); obj.name='fei '; 在//obj中设置name属性时,会触发名为set的方法/script/body/html

当然这么粗暴的话,绝对不行。 性能会出现很多问题。

3.讲解vue如何实现

先看原理图

3.1观察器用于实现每个vue中由数据定义的属性循环(Object.defineProperty ) )中的数据劫持,并使用其中的setter和getter通知读者。 读者触发其update方法来更新视图。

3.2为什么介绍读者? 在vue中,v-model、v-name、{ }等可以显示数据。 也就是说,如果一个属性通过这三个指令,则每次该属性改变时,相应三个指令的html视图也必须改变。 因此,每当vue中存在可用于这种双向绑定的指令时,都需要一个

l='name'和{{name}}有两个对应的订阅者,各自管理自己的地方。每当属性的set方法触发,就循环更新Dep中的订阅者。

4.vue代码实现

4.1 observer实现,主要是给每个vue的属性用Object.defineProperty(),代码如下:

function defineReactive (obj, key, val) { var dep = new Dep(); Object.defineProperty(obj, key, { get: function() { //添加订阅者watcher到主题对象Dep if(Dep.target) { // JS的浏览器单线程特性,保证这个全局变量在同一时间内,只会有同一个监听器使用 dep.addSub(Dep.target); } return val; }, set: function (newVal) { if(newVal === val) return; val = newVal; console.log(val); // 作为发布者发出通知 dep.notify();//通知后dep会循环调用各自的update方法更新视图 } })} function observe(obj, vm) { Object.keys(obj).forEach(function(key) { defineReactive(vm, key, obj[key]); }) }

4.2实现compile:

compile的目的就是解析各种指令称真正的html。

function Compile(node, vm) { if(node) { this.$frag = this.nodeToFragment(node, vm); return this.$frag; }}Compile.prototype = { nodeToFragment: function(node, vm) { var self = this; var frag = document.createDocumentFragment(); var child; while(child = node.firstChild) { console.log([child]) self.compileElement(child, vm); frag.append(child); // 将所有子节点添加到fragment中 } return frag; }, compileElement: function(node, vm) { var reg = /\{\{(.*)\}\}/; //节点类型为元素(input元素这里) if(node.nodeType === 1) { var attr = node.attributes; // 解析属性 for(var i = 0; i < attr.length; i++ ) { if(attr[i].nodeName == 'v-model') {//遍历属性节点找到v-model的属性 var name = attr[i].nodeValue; // 获取v-model绑定的属性名 node.addEventListener('input', function(e) { // 给相应的data属性赋值,进而触发该属性的set方法 vm[name]= e.target.value; }); new Watcher(vm, node, name, 'value');//创建新的watcher,会触发函数向对应属性的dep数组中添加订阅者, } }; } //节点类型为text if(node.nodeType === 3) { if(reg.test(node.nodeValue)) { var name = RegExp.$1; // 获取匹配到的字符串 name = name.trim(); new Watcher(vm, node, name, 'nodeValue'); } } }}

4.3 watcher实现

function Watcher(vm, node, name, type) { Dep.target = this; this.name = name; this.node = node; this.vm = vm; this.type = type; this.update(); Dep.target = null;}Watcher.prototype = { update: function() { this.get(); this.node[this.type] = this.value; // 订阅者执行相应操作 }, // 获取data的属性值 get: function() { console.log(1) this.value = this.vm[this.name]; //触发相应属性的get }}

4.4 实现Dep来为每个属性添加订阅者

function Dep() { this.subs = [];}Dep.prototype = { addSub: function(sub) { this.subs.push(sub); }, notify: function() { this.subs.forEach(function(sub) { sub.update(); }) }}

这样一来整个数据的双向绑定就完成了。

5.梳理

首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会,接着为input会添加监听事件,修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。

前端面试vue项目中遇到的难题,双向数据绑定原理面试