vue双向绑定和观察者模式详解
在Vue中,使用了Object.defineProterty()函数来实现双向绑定,这也是Vue不兼容IE8的原因。
1响应原则
先从对应原理说起。我们可以通过Object.defineProterty()自定义Object的getter和setter来实现我们的目标。
代码如下
功能观察(值,cb) {对象.键(值)。forEach((key)=definere active(value,key,value[key],CB))} function definere active(obj,key,val,CB){ object . definepreproperty(obj,key,{ enumerable: true,configurable: true,Get: ()={ /*.从属集合等.*//* github:https://github.com/answershuto */returnval },set:newval={ val=newvalCB();/*订阅者收到消息*/}})的回调} class vue { constructor(options){ this。_ data=options.data观察(这个。_data,options . render)} }让app=new Vue({ el: '#app ',data: { text: 'text ',text2: 'text2' },render(){ console . log(' render ');}})通过observe函数为app.data上的每个键和值设置getter和setter。当值改变时触发setter将触发渲染函数。达到了响应的目的。如果是视图更新,我们会通过监控dom的输入事件来触发数据更新,但是现在我们只会在更改vue时触发它们的setter。_data.text,但是我想偷懒。只需更改vue.text即可触发setter。我能怎么做呢?
我们使用代理方法
_proxy.call(this,options . data);/*在构造函数中*//* proxy */function _ proxy(data){ const即=thisObject.keys(数据)。forEach(key={ object . definepreproperty(即key,{ configurable: true,enumerable: true,get : function proxyGetter(){ return that。_ data[key];},set:函数proxySetter (val) { that。_ data[key]=val;} }) });}依赖集合
让我们再看看下面的代码
new Vue({ template : ` div span text 1:/span { { text 1 } } span text 2:/span { { text 2 } } div `,data: { text1: 'text1 ',text2: 'text2 ',text : ' text 3 ' });当你的text3发生变化的时候,其实text3是不会渲染的,但是会触发渲染功能一次,这显然是错误的。所以我们需要收集依赖关系。
我们在初始化的时候只需要渲染一次,渲染所依赖的所有数据都会被getter触发。这个时候,我们只需要把这些数据整理成一个列表!
让我们首先了解Dep(依赖项)类。下图是最简单的Dep类。我们可以把他理解为一个出版商(这很重要!)
class Dep { constructor(){ this . subs=[];} addSub(sub : Watcher){ this . subs . push(sub)} removeSub(sub : Watcher){ remove(this . subs,sub)}/* github 3360https://github.com/answershuto */notify(){//稳定订阅者列表第一个const subs=this.subs.slice() for(让i=0,l=subs.lengthI l;我){subs [I]。update ()}}}函数remove (arr,item) {if (arr。长度){const index=arr。(项目)的索引if (index-1) {return arr。split (index,1)}}每次我们触发getter,但现在的问题是,我们用什么来安装这个触发的‘对象’,也可以说是订阅者?
我们使用观察者类
类Watcher {构造函数(vm,expOrFn,cb,options){ this . CB=CB;this.vm=vm/*这里观察者本身被分配给全局目标,只有被目标标记的才会进行依赖集合*/dep . target=this;/* github:https://github.com/answershuto *//*触发依赖项集合的呈现操作*/this . CB . call(this . VM);} update(){ this . CB . call(this . VM);}}vm是vue实例,expOrFn是{{a b}}中的a b,CB是回调函数或返回ab,选项是一些配置项。
如果Vue在第一次呈现列表时遇到类似{{xxx}}的表达式,它将是新的Watcher()。解析内部函数,然后将当前观察器实例分配给Dep.target(Dep.target是全局的,一次只能存在一个,因为Vue一次只处理一个依赖项)。然后执行回调函数。(这看起来是一个回调函数呈现,但实际上它再次触发了一个getter,然后当前的依赖项将被添加到sub中)
接下来,我们开始依靠收藏
类Vue {构造函数(选项){ this。_ data=options.data观察者(这个。_data,options . render);让守望者=新守望者(this,}}函数定义reactive (obj,key,val,CB) {/*在闭包中存储一个Dep对象*/const Dep=new Dep();Object.defineproperty (obj,key,{enumerable:true,configurable:true,get :()={ if(Dep . target){/* watcher对象存在于全局Dep . target */Dep . addSub(Dep . target)中;} },set:newVal={/*只有addSub中的函数才会触发*/dep . notify();} })} Dep . target=null;//防止重复添加依赖项。这里我们将通过例子来解释
模板div { { a b } }/div div { { a-c } }/div/templates script let app=new vue({ data : { a : 1,b: 2,c: 3}})我们编译为{{a b}},并将实例化相应的瓦特
this . CB . call(this . VM);This.cb引用function(){ return a b };这个. vm引用这个vue对象,它将触发vue.a和vue.b的getter方法,A和B都有自己的dep对象。我们通过Dep.target将这个Watcher对象添加到dep的subs数组中,当我们改变A或B时,它会触发setter,然后触发subs数组中的更新方法,视图中的A和B也会被更新
有一个小知识点:我们创建一个新的属性对象时,一定要通过Vue.set来实现,而不能直接通过=,这样是不会被检测到的,因为我们在初始化的时候通过defineProperty重构了这个对象属性的getter和setter方法,但是新创建的属性是不会被检测到的。
下图是数据初始化中使用观察者模式的Vue框架示意图:
以上就是边肖介绍的vue双向绑定和观察者模式的详细讲解和整合,希望对大家有所帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!
版权声明:vue双向绑定和观察者模式详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。