vue生命周期:
init:初始化props,methods,data,computed和watch;
1.this是如何直接访问data、props、methods里的属性和方法的?
注:为避免冲突和覆盖,data、props、methods里不能定义相同名字的属性,
优先级:props> methods> data(即假如一个 key 在 props 中有定义了那么就不能在 data 和 methods 中出现了;假如一个 key 在 data 中出现了那么就不能在 methods 中出现了)
源码中的initState函数:
export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) }}
由以上源码可以看到 initState 其实是很多选项初始化的汇总,包括:props、methods、data、computed 和 watch;
2.实现一个简易的响应式系统
Object.defineProperty:在对象上定义或者修改属性
Object.observe:观察对象属性的更改
Vue通过Object.defineProperty的 getter/setter 对收集的依赖项进行监听,在属性被访问和修改时通知变化,进而升级视图数据;
即数据观测、依赖收集、视图升级。
observer的实现
让object的对象都用Object.defineProperty来定义,以达到获取或者修改的时候能够调用属性里的get和set
class Observer { constructor(value) { this.value = value this.walk(this.value) } walk (value) { // 递归遍历value的属性 Object.keys(value).forEach((key) = > { defineReactive(value, key, value[key]) }) }}// 将对象的每一个属性都增加get和set方法function defineReactive(obj, key ,val) { let childOb = observe(val) Obeject.defineProperty(obj, key, { enumerable: true, configurable: true, get() { console.log('') return val }, // 对象改变时,新对象的每个属性也增加get和set方法 set(newVal) { console.log('set') val = newVal childOb = observe(val) } })}// 属性类型观测判断function observe (value) { if (typeof value === 'object' && !Array.isArray(value)) { value = new Observer(value); return value; }}
以上代码可以看出,我们是通过console来判断能否执行get和set的,为了方便,我们使用消息订阅来实现通知变化
Dep的实现
收集属性值的变化,一旦set触发就升级视图
class Dep { constructor(){ //订阅的信息 this.subs = []; } addSub(sub){ this.subs.push(sub); } removeSub (sub) { remove(this.subs, sub); } //升级收集 depend(){ if (Dep.target) { Dep.target.addDep(this); } } //派发通知 notify(){ const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update(); } } } // 订阅完成后将target清掉 Dep.target = null;
Watcher的实现
状态发生改变时升级视图
const watcher = { addDep:function (dep) { dep.addSub(this); }, update:function(){ html(); } }
以上,我们完成来Observe、Dep、Watcher的简单实现,我们看到Observe和Dep里都调用里Watcher里的update,为了让其各司其职,我们将其改为Observe里一旦触发set就通知Dep并调用notify派发升级任务。
将以上实现进行简单修改和串联,以实现响应式,一律代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body></body><script> class Observer { constructor(value) { this.value = value; this.walk(this.value); } walk (value) { // 递归遍历value的属性 Object.keys(value).forEach((key) => { defineReactive(value, key, value[key]); }) } } // 将对象的每一个属性都增加get和set方法 function defineReactive(obj, key ,val) { // new一个Dep对象,后面要调用dep的方法 const dep = new Dep(); let val = observe(val); Object.defineProperty(obj,key,{ enumerable: true, configurable: true, get: function () { if(Dep.target){ //收集依赖 dep.depend() } return val; }, set: function (newVal) { if(newVal === val || (newVal !== newVal && val !== val)){ return ; } val = observe(newVal); //发布改变 dep.notify(); } }); } // 属性类型观测判断 function observe (value) { if (typeof value === 'object' && !Array.isArray(value)) { value = new Observer(value); return value; } } class Dep { constructor(){ //订阅的信息 this.subs = []; } addSub(sub){ this.subs.push(sub); } removeSub (sub) { remove(this.subs, sub); } //此方法的作用等同于 this.subs.push(Watcher); depend(){ if (Dep.target) { Dep.target.addDep(this); } } //这个方法就是发布通知了 告诉你 有改变啦 notify(){ const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update(); } } } // 订阅完成后将target清掉 Dep.target = null; const watcher = { addDep:function (dep) { dep.addSub(this); }, update:function(){ html(); } } let obj = { a: '仔细看' }; function html () { document.querySelector('body').innerHTML = obj.a; } Dep.target = watcher; html();//第一次渲染界面 Dep.target = null; setTimeout(function() { obj.a = '我变了'; html(); },5000) </script>
注:主要修改都在defineReactive里,以上代码可直接运行。