源码如下:
export function set (target: Array<any> | Object, key: any, val: any): any { 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)) { // 对比数组的key值和数组长度,取较大值设置为数组的长度 target.length = Math.max(target.length, key) // 替换目标值 target.splice(key, 1, val) return val } // 如果目标值是对象,并且key值是目标值存在的有效key值,并且不是原型上的key值 if (key in target && !(key in Object.prototype)) { // 直接更改目标值 target[key] = val return val } const ob = (target: any).__ob__ // 判断目标值是否为响应式的 if (target._isVue || (ob && ob.vmCount)) { // 如果是vue根实例,就警告 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 } if (!ob) { // 如果目标值不是响应式的,那么值需要给对应的key赋值 target[key] = val return val } // 其他情况,目标值是响应式的,就通过Object.defineProperty进行数据监听 defineReactive(ob.value, key, val) // 通知更新dom操作 ob.dep.notify() return val }
大概流程就是:
1.判断目标值是否为有效值,不是有效值直接停止
2.判断是否为数组,并且key值是否为有效的key值
如果是数组,就选择数组的长度和key值取较大值作为数组的新的length值,并且替换目标值
splice方法,重写了,所以执行splice,会双向数据绑定
3.判断目标值是否为响应式的__ob__
如果是vue实例,直接不行
如果不是响应式的数据,就是普通的修改对象操作
如果是响应式数据,那就通过Object.defineProperty进行数据劫持
4.通知dom更新
继续往下挖坑 https://www.qiyuandi.com/zhanzhang/zonghe/11665.html
Array的变化侦测
ue中数组并不是通过监听索引去实现变化侦测的,因为这样做当我们用list.push(1)向list数组中push一个1时,并不会触发get/set。而我们日常编程中会频繁使用Array原型上的方法去操作数组,所以object那种方法就不灵了。
也正因为如此,vue中重写了数组原型上的方法,当我们使用数组原型方法改变数组时,向依赖发送通知即可
源码路径:src\core\observer\array.js
const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto) const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse'] methodsToPatch.forEach(function (method) { // cache original method const original = arrayProto[method] def(arrayMethods, method, function mutator (...args) { const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify() return result }) })
Array. prototype.splice 重写 ---observeArray 法对新增项进行变化侦测。
首先保存了Array的原型,然后创建了arrayMethods的原型指向Array的原型,因为js中只有push、pop、shift、unshift、splice、sort、reverse这七个方法会改变原数组,所以只需要重写这七个方法就可以了。然后给arrayMethods添加这七个方法。在重写的方法里调用原来的方法对数组进行操作,然后手动通知依赖进行更新即可。因为push、unshift、splice可以给数组新增项,而新增项也可能是对象或数组所以调用observeArray方法对新增项进行变化侦测。
上面我们频繁提到依赖、依赖收集、通知依赖更新,那什么是依赖呢?依赖又收集到哪里去呢?我们接着往下看。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端