vue响应式道理及依靠收集的介绍 (附代码)
Vue通过设定对象属性的setter/getter办法来监听数据的转变,通过getter停止依靠收集,而每个setter办法就是一个视察者,在数据变动的时候通知订阅者更新视图。
将数据data变成可视察的(observable)
那么Vue是怎样将所有data下面的属性变成可视察的呢?
function obsever(value,cb){ Object.keys(value).forEach((key)=>defineReactive(value,key,value[key],cb)) } function defineReactive(obj,key,val,cb){ Object.defineProperty(obj,key,{ enumerable;true, configurable:true, get:()=>{ /*依靠收集*/ return val; }, set:newVal=>{ val=newVal; cb(); } }) } class Vue{ constructor(options){ this._data = options.data; obsever(this._data,options.render) } } let app = new Vue({ el:'#app', data:{ text:'text', text2:'text2' }, render(){ console.log('render') } })
为了便于懂得,第一思考一种最简便的状况,不思考数组等状况,代码如上所示。在initData中会调取observe这个函数将Vue的数据设定成observable的。当_data数据发生改动的时候就会触发set,对订阅者停止回调(在这里是render)。
那么问题来了,需要对app._data.text操纵才会触发set。为了偷懒,我们需要一种利便的办法通过app.text直接设定就能触发set对视图停止重绘。那么就需要到代理。
代理
我们可以在Vue的结构函数constructor中为data施行一个代理proxy。这样我们就把data上面的属性代理到了vm实例上。
_proxy.call(this,options.data);//结构函数 //代理 function _proxy(data){ const that = this; Object.keys(data).forEach(key=>{ configurable:true, enumerable:true, get:function proxyGetter(){ return that._data[key] }, set:function proxySetter(val){ that._data[key] = val; } }) }
我们就可以用app.text代替app._data.text了。
为什么要依靠收集
先看下面这段代码
new Vue({ template:`<p> <span>text1:</span>{{text1}} <span>text2:</span>{{tetx2}} </p>`, data:{ text1:'text1', text2:'text2', text3:'text3' } })
依照上面的响应式道理中的办法停止绑定则会显现一个问题--text3在实际模板中并没有被用到,然而当text3的数据被修改时,一样会触发text3的setter致使从新施行渲染,这明显不准确。
先说说Dep
当对data上的对象停止修改值的时候会触发它的setter,那么取值的时候天然会触发getter事件,所以我们只要在最开端停止一次render,那么所有被渲染所依靠的data中的数据就会被getter收集到Dep的subs中去。在对data中的数据停止修改的时候setter只会触发Dep的subs函数。
定义一个依靠收集类的Dep。
class Dep{ constructor(){ this.subs = []; } addSub(sub:Watcher){ this.subs.push(sub) } removeSub(sub:Watcher){ remove(this.subs,sub) } notify(){ const subs = this.subs.slice() for(let i = 0;l=subs.length;i<1;i++){ subs[i].update() } } } function remove(arr,item){ if(arr.length){ const index = arr.indexOf(item) if(index>-1){ return arr.splice(index,1) } } }
Watcher
订阅者,当依靠收集的时候会addSub到sub中,在修改data中数据的时候会触发dep对象的notify,通知所有Watcher对象去修改对应视图。
class Watcher { constructor (vm, expOrFn, cb, options) { this.cb = cb; this.vm = vm; /*在这里将视察者本身赋值给全局的target,只要被target标志过的才会停止依靠收集*/ Dep.target = this; /*Github:https://github.com/answershuto*/ /*触发渲染操纵停止依靠收集*/ this.cb.call(this.vm); } update () { this.cb.call(this.vm); } }
开端依靠收集
class Vue { constructor(options) { this._data = options.data; observer(this._data, options.render); let watcher = new Watcher(this, ); } } function defineReactive (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;
将视察者Watcher实例赋值给全局的Dep.target,然后触发render操纵只要被Dep.target标志过的才会停止依靠收集。有Dep.target的对象会将Watcher的实例push到subs中,在对象被修改触发setter操纵的时候dep会调取subs中的Watcher实例的update办法停止渲染。
本篇文章到这里就已经全部完毕了,更多其他出色内容可以关注PHP中文网的JavaScript视频教程栏目!以上就是vue响应式道理及依靠收集的介绍 (附代码)的具体内容,更多请关注百分百源码网其它相关文章!