「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」
Vue.js 源码分析 - set-源码
定义位置
- Vue.set() -构造函数的中的方法,也就是静态方法
- global-api/index.js
// 静态方法 set/delete/nextTick Vue.set = set Vue.delete = del Vue.nextTick = nextTick - vm.$set() - 实例中的方法
- instance/index.js
// 注册 vm 的 $data/$props/$set/$delete/$watch stateMixin(Vue) - instance/state.js
Vue.prototype.$set = set Vue.prototype.$delete = del
- instance/index.js
从上面的代码进行分析,我们可以看见Vue.set()和 vm.$set()都设置成了 set 方法,接下来,我们就来查看set函数是如何实现的
set 函数的实现 - 解析
-
代码模块1
if (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target)) ) { warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`) }解析:
isUndef(target) || isPrimitive(target)这里的判断数据是否undefinedor原始值进行判断,如果是的话,就抛出错误 -
代码模块2
if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key) target.splice(key, 1, val) return val }解析:
Array.isArray(target) && isValidArrayIndex(key): 判断 target 是否是对象,key 是否是合法的索引target.splice(key, 1, val): 通过 splice 对key位置的元素进行替换,splice 在 array.js 进行了响应化的处理
-
代码模块3
if (key in target && !(key in Object.prototype)) { target[key] = val return val }解析: 如果 key 在对象中已经存在直接赋值,不再做响应式的处理了
-
代码模块4
const ob = (target: any).__ob__解析:之前我们也看过响应式的处理,我们在处理完毕数据之后会给数据添加上
__ob__属性,这个__ob__中存储的值就是observer对象 -
代码模块5
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 }解析: 如果 target 是 vue 实例或者 $data 直接返回
-
代码模块6
if (!ob) { target[key] = val return val } } defineReactive(ob.value, key, val) ob.dep.notify()解析: - 判断 ob 是否存在,如果 ob 不存在,target 不是响应式对象直接赋值 -
defineReactive(ob.value, key, val)把 key 设置为响应式属性 -ob.dep.notify()发送通知
小结
ok,这就是 set 函数内部的实现
- set可以处理数组的响应式:当使用set给数组设置值的时候,会调用
splice方法 - set可以处理对象的响应式: 当给对象设置新的值的时候,会调用
defineReactive方法 最终会调用ob.dep.notify()发送通知