1.$set
用法:this.$set(Object, key, value)或 Vue.$set(Object, key, value)
原理:当data数据中存在某个属性时,我们对他进行更改,页面会更新;但是如果data数据中没有某个属性值,我们对其更改,页面不会显示此属性;那是因为再vue初始化的时候,已经将data中定义的数据处理成响应式的了,所以数据的改变会触发页面的更新;但是新增的属性却不是响应式的,虽然打印可以看到对象确实多了此属性,但是由于不是响应式的,所以不会体现在页面上
##源码 位置:src/core/observer/index.js //set接收三个参数,target/key/val target的类型为对象或者数组 export function set (target: Array<any> | Object, key: any, val: any): any { //当前环境为生产环境并且 target为null/undefined 或者 || target为string/number/symbol/boolean的一种时 抛出警告 if (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target)) ) { warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`) } //数组 //判断是数组并且key为有效的数组索引 if (Array.isArray(target) && isValidArrayIndex(key)) { //将target数组的长度设置为target.length和key中的最大值 target.length = Math.max(target.length, key) //做替换操作 target.splice(key, 1, val) return val } //对象 //key为数据中的一个属性并且不是Object原型上的属性 if (key in target && !(key in Object.prototype)) { //已经存在说明是响应式的,直接更新 target[key] = val return val } //之前不存在的情况 //定义ob的值为target._ob_ const ob = (target: any).__ob__ //target对象是vue实例对象或者根数据对象,就会抛出错误 if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.' ) return val } //ob不存在,target不是响应式的 if (!ob) { target[key] = val return val } //对新添加的key或者val做响应式----defineReactive主要实现Object.defineProperty()方法 defineReactive(ob.value, key, val) //通知更新 --notify主要调用了update方法 ob.dep.notify() return val }
参考链接:https://www.cnblogs.com/heavenYJJ/p/9559439.html
总结:$set()修改数组主要是调用了splice();此方法主要是调用defineReactive()对新添加的key或者val做响应式;然后通过notify()方法来通知更新
2.$watch
用法:this.$watch(this.a.b,fn,{deep:true,immediate:true})
##源码 位置:src/core/instance/state.js Vue.prototype.$watch = function ( expOrFn: string | Function, //对象属性 cb: any, //回调函数或者纯对象 options?: Object //可选配置对象 ): Function { const vm: Component = this //如果回调cb是对象 if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, options) } options = options || {} //表明这是一个用户的watcher,而不是组件的render watcher options.user = true //创建watcher实例 const watcher = new Watcher(vm, expOrFn, cb, options) //设置immediate立即执行一次cb if (options.immediate) { try { cb.call(vm, watcher.value) } catch (error) { handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`) } } //返回取消监听的函数 return function unwatchFn () { watcher.teardown() } } }
所以他的重点在于获得Watcher实例
##源码 位置:src/core/observer/watcher.js export default class Watcher { vm: Component; expression: string; cb: Function; id: number; deep: boolean; user: boolean; lazy: boolean; sync: boolean; dirty: boolean; active: boolean; deps: Array<Dep>; newDeps: Array<Dep>; depIds: SimpleSet; newDepIds: SimpleSet; before: ?Function; getter: Function; value: any; constructor ( vm: Component, //组件实例对象 expOrFn: string | Function, //观察的属性 cb: Function, //回调 options?: ?Object, //可配置对象 isRenderWatcher?: boolean //标识观察者是否是渲染函数的观察者 ) { this.vm = vm if (isRenderWatcher) { vm._watcher = this } vm._watchers.push(this) // options if (options) { this.deep = !!options.deep this.user = !!options.user this.lazy = !!options.lazy this.sync = !!options.sync this.before = options.before } else { this.deep = this.user = this.lazy = this.sync = false } this.cb = cb this.id = ++uid // uid for batching this.active = true this.dirty = this.lazy // for lazy watchers this.deps = [] this.newDeps = [] this.depIds = new Set() this.newDepIds = new Set() this.expression = process.env.NODE_ENV !== 'production' ? expOrFn.toString() : '' // parse expression for getter if (typeof expOrFn === 'function') { this.getter = expOrFn } else { this.getter = parsePath(expOrFn) if (!this.getter) { this.getter = noop process.env.NODE_ENV !== 'production' && warn( `Failed watching path: "${expOrFn}" ` + 'Watcher only accepts simple dot-delimited paths. ' + 'For full control, use a function instead.', vm ) } } this.value = this.lazy ? undefined : this.get() } /** * Evaluate the getter, and re-collect dependencies. */ get () { pushTarget(this) //将当前Watcher的实例指定到Dep.target上 /* src/core/observer/dep.js pushTarget(target){ Dep.targer = target } */ //然后立即调render函数,render函数访问相关的key, let value const vm = this.vm try { value = this.getter.call(vm, vm) } catch (e) { if (this.user) { handleError(e, vm, `getter for watcher "${this.expression}"`) } else { throw e } } finally { // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { //深层遍历,递归读取被观察属性的所有子属性的值 traverse(value) } popTarget() this.cleanupDeps() } return value } /** * Add a dependency to this directive. */ //记录自己都订阅过哪些Dep addDep (dep: Dep) { const id = dep.id //去重 if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { //把自己订阅到dep dep.addSub(this) } } } /** * Clean up for dependency collection. */ cleanupDeps () { let i = this.deps.length while (i--) { const dep = this.deps[i] if (!this.newDepIds.has(dep.id)) { dep.removeSub(this) } } let tmp = this.depIds this.depIds = this.newDepIds this.newDepIds = tmp this.newDepIds.clear() tmp = this.deps this.deps = this.newDeps this.newDeps = tmp this.newDeps.length = 0 } /** * Subscriber interface. * Will be called when a dependency changes. */ update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { //指定同步更新 this.run() } else { //异步更新队列 queueWatcher(this) } } /** * Scheduler job interface. * Will be called by the scheduler. */ run () { if (this.active) { const value = this.get() //对比新值value和旧值this.value是否相等 if ( value !== this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep ) { // set new value const oldValue = this.value this.value = value if (this.user) { //通过watch选项或者$watch定义的 try { //执行回调 this.cb.call(this.vm, value, oldValue) } catch (e) { handleError(e, this.vm, `callback for watcher "${this.expression}"`) } } else { this.cb.call(this.vm, value, oldValue) } } } }