当前位置: 首页>关注 >
【Vue2.x源码系列06】计算属性computed原理-环球聚看点
2023-04-18 18:29:58 来源:博客园
上一章 Vue2异步更新和nextTick原理,我们介绍了 JavaScript 执行机制是什么?nextTick源码是如何实现的?以及Vue是如何异步更新渲染的?
(资料图片仅供参考)
本章目标
计算属性是如何实现的?计算属性缓存原理 - 带有dirty属性的watcher洋葱模型的应用初始化在 Vue初始化实例的过程中,如果用户 options选项中存在计算属性时,则初始化计算属性
// 初始化状态export function initState(vm) { const opts = vm.$options // 获取所有的选项 // 初始化数据 if (opts.data) { initData(vm) } // 初始化计算属性 if (opts.computed) { initComputed(vm) }}
我们给每个计算属性都创建了一个 Watcher实例,标识为lazy:true
, 在初始化watcher时不会立即执行 get方法(计算属性方法)
并将计算属性watcher 都保存到了 Vue实例上,让我们可以在后续的 getter方法中通过 vm获取到当前的计算属性watcher
然后使用Object.defineProperty
去劫持计算属性
// 初始化计算属性function initComputed(vm) { const computed = vm.$options.computed const watchers = (vm._computedWatchers = {}) // 将每个计算属性对应的watcher 都保存到 vm上 for (let key in computed) { let userDef = computed[key] // 兼容不同写法 函数方式 和 对象getter/setter方式 let fn = typeof userDef === "function" ? userDef : userDef.get // 给每个计算属性都创建一个 watcher,并标识为 lazy,不会立即执行 get-fn watchers[key] = new Watcher(vm, fn, { lazy: true }) // 劫持计算属性getter/setter defineComputed(vm, key, userDef) }}// 劫持计算属性function defineComputed(target, key, userDef) { const setter = userDef.set || (() => {}) Object.defineProperty(target, key, { get: createComputedGetter(key), set: setter, })}
当我们劫持到计算属性被访问时,根据 dirty 值去决定是否更新 watcher缓存值
然后让自己依赖的属性(准确来说是订阅的所有dep)都去收集上层watcher,即 Dep.target(可能是计算属性watcher,也可能是渲染watcher)
// 劫持计算属性的访问function createComputedGetter(key) { return function () { const watcher = this._computedWatchers[key] // this就是 defineProperty 劫持的targer。获取到计算属性对应的watcher // 如果是脏的,就去执行用户传入的函数 if (watcher.dirty) { watcher.evaluate() // 重新求值后 dirty变为false,下次就不求值了,走缓存值 } // 当前计算属性watcher 出栈后,还有渲染watcher 或者其他计算属性watcher,我们应该让当前计算属性watcher 订阅的 dep,也去收集上一层的watcher 即Dep.target(可能是计算属性watcher,也可能是渲染watcher) if (Dep.target) { watcher.depend() } // 返回watcher上的值 return watcher.value }
DepDep.target
:当前渲染的 watcher,静态变量stack
:存放 watcher 的栈。 利用 pushTarget、popTarget 这两个方法做入栈出栈操作// 当前渲染的 watcherDep.target = null// 存放 watcher 的栈let stack = []// 当前 watcher 入栈, Dep.target 指向 当前 watcherexport function pushTarget(watcher) { stack.push(watcher) Dep.target = watcher}// 栈中最后一个 watcher 出栈,Dep.target指向栈中 最后一个 watcher,若栈为空,则为 undefinedexport function popTarget() { stack.pop() Dep.target = stack[stack.length - 1]}
计算属性Watcher在初始化Vue实例时,我们会给每个计算属性都创建一个对应watcher(我们称之为计算属性watcher,除此之外还有 渲染watcher 和 侦听器watcher ),他有一个 value 属性用于缓存计算属性方法的返回值。
默认标识 lazy: true,懒的,代表计算属性watcher,创建时不会立即执行 get方法
默认标识 dirty: true,脏的,当我们劫持到计算属性访问时,如果是脏的,我们会通过watcher.evaluate
重新计算 watcher 的 value值 并将其标识为干净的;如果是干净的,则直接取 watcher 缓存值
depend 方法,会让计算属性watcher 订阅的dep去收集上层watcher,可能是渲染watcher,也可能是计算属性watcher(计算属性嵌套的情况),实现洋葱模型的核心方法
update 方法,当计算属性依赖的对象发生变化时,会触发dep.notify
派发更新 并 调用 update 方法,只需更新 dirty 为 true即可。我们会在后续的渲染watcher 更新时,劫持到计算属性的访问操作,并通过 watcher.evaluate
重新计算其 value值
class Watcher { constructor(vm, fn, options) { // 计算属性watcher 用到的属性 this.vm = vm this.lazy = options.lazy // 懒的,不会立即执行get方法 this.dirty = this.lazy // 脏的,决定重新读取get返回值 还是 读取缓存值 this.value = this.lazy ? undefined : this.get() // 存储 get返回值 } // 重新渲染 update() { console.log("watcher-update") if (this.lazy) { // 计算属性依赖的值发生改变,触发 setter 通知 watcher 更新,将计算属性watcher 标识为脏值即可 // 后面还会触发渲染watcher,会走 evaluate 重新读取返回值 this.dirty = true } else { queueWatcher(this) // 把当前的watcher 暂存起来,异步队列渲染 // this.get(); // 重新渲染 } } // 计算属性watcher为脏时,执行 evaluate,并将其标识为干净的 evaluate() { this.value = this.get() // 重新获取到用户函数的返回值 this.dirty = false } // 用于洋葱模型中计算属性watcher 订阅的dep去 depend收集上层watcher 即Dep.target(可能是计算属性watcher,也可能是渲染watcher) depend() { let i = this.deps.length while (i--) { this.deps[i].depend() } }}
缓存原理计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。 缓存原理如下:
在初始化计算属性时,我们使用Object.defineProperty
劫持了计算属性,并做了一些 getter/setter操作
计算属性watcher 有一个 dirty脏值属性,默认为 true
当我们劫持到计算属性被访问时,如果 dirty 为 true,则执行 evaluate 更新 watcher 的 value值 并 将 dirty 标识为 false;如果为 false,则直接取 watcher 的缓存值
当计算属性依赖的属性变化时,会通知 watcher 调用 update方法,此时我们将 dirty 标识为 true。这样再次取值时会重新进行计算
洋葱模型在初始化Vue实例时,我们会给每个计算属性都创建一个对应的懒的watcher,不会立即调用计算属性方法
当我们访问计算属性时,会通过watcher.evaluate()
让其直接依赖的属性去收集当前的计算属性watcher,并且还会通过watcher.depend()
让其订阅的所有 dep都去收集上层watcher,可能是渲染watcher,也可能是计算属性watcher(如果存在计算属性嵌套计算属性的话)。这样依赖的属性发生变化也可以让视图进行更新
让我们一起来分析下计算属性嵌套的例子
{{fullName}}
computed: { fullAge() { return "今年" + this.age }, fullName() { console.log("run") return this.firstName + " " + this.lastName + " " + this.fullAge },}
初始化组件时,渲染watcher 入栈stack:[渲染watcher]
当执行 render方法并初次访问 fullName时,执行computed watcher1.evaluate()
,watcher1
入栈stack:[渲染watcher, watcher1]
当执行watcher1
的 get方法时,其直接依赖的 firstName 和 lastName 会去收集当前的 watcher1;然后又访问 fullAge 并执行computed watcher2.evaluate()
,watcher2
入栈watcher1:[firstName, lastName]
stack:[渲染watcher, watcher1, watcher2]
执行watcher2
的 get方法时,其直接依赖的 age 会去收集当前的 watcher2watcher2:[age]
watcher2
出栈,并执行watcher2.depend()
,让watcher2
订阅的 dep再去收集当前watcher1stack:[渲染watcher, watcher1]
watcher1:[firstName, lastName, age]
watcher1
出栈,执行watcher1.depend()
,让watcher1
订阅的 dep再去收集当前的渲染watcherstack:[渲染watcher]
渲染watcher:[firstName, lastName, age]
关键词:
为你推荐
-
【Vue2.x源码系列06】计算属性computed原理-环球聚看点
-
中国科大举办月壤科研成果主题艺术展
-
前沿资讯!携手刘慈欣共同打造 超智驾轿跑SUV小鹏G6首发亮相
-
环球速看:国家考古遗址公园五年累计“打卡”人次达1.46亿
-
农业农村部:今年全国农作物种子市场总体供大于求|环球微动态
-
乌鲁木齐市水磨沟区:让居民拥有更多获得感和幸福感
-
环球快消息!大熊猫暖暖被“打”了?园方通报:涉事饲养员立即停止工作,永不允许饲养大熊猫!网友们吵翻了
-
“女性综艺”展现女性群像
-
富临运业(002357)4月18日主力资金净买入204.42万元
-
湖南郴州文旅招商推介活动现场签约10个项目 总投资60.6亿 今日要闻
-
刷刷手环官网app下载_刷刷手环
-
动态焦点:濮阳市2家企业荣膺省长质量奖提名奖
-
杭州女童坠亡案将择期宣判
-
亏损超48亿,OATLY背后的「素食主义」不香了|氪金 · 大消费 世界新要闻
-
天天播报:您可以在陶罐上使用哪种类型的油漆?
-
【环球新视野】美国和日本签署合作开发地热能源的备忘录
-
阅见|影响全球5000万读者的成长之书《苏菲的世界》带你开启哲学奇妙之旅
-
环球热推荐:中环海陆:4月17日融资净买入75.78万元,连续3日累计净买入524.34万元
-
当前焦点!有一项工程要铺设一条电缆线怎么办_有一项工程要铺设一条电缆线
-
神马股份:2023年4月20日可转债上市
推荐内容
- 【Vue2.x源码系列06】计算属性computed原理-环球聚看点
- 中国科大举办月壤科研成果主题艺术展
- 前沿资讯!携手刘慈欣共同打造 超智驾轿跑SUV小鹏
- 环球速看:国家考古遗址公园五年累计“打卡”人次
- 农业农村部:今年全国农作物种子市场总体供大于求
- 乌鲁木齐市水磨沟区:让居民拥有更多获得感和幸福
- 环球快消息!大熊猫暖暖被“打”了?园方通报:涉
- “女性综艺”展现女性群像
- 富临运业(002357)4月18日主力资金净买入204.42万元
- 湖南郴州文旅招商推介活动现场签约10个项目 总投
- 刷刷手环官网app下载_刷刷手环
- 动态焦点:濮阳市2家企业荣膺省长质量奖提名奖
- 杭州女童坠亡案将择期宣判
- 亏损超48亿,OATLY背后的「素食主义」不香了|氪
- 天天播报:您可以在陶罐上使用哪种类型的油漆?
- 【环球新视野】美国和日本签署合作开发地热能源的
- 阅见|影响全球5000万读者的成长之书《苏菲的世界
- 环球热推荐:中环海陆:4月17日融资净买入75.78万
- 当前焦点!有一项工程要铺设一条电缆线怎么办_有一
- 神马股份:2023年4月20日可转债上市
- 安徽棚户区改造项目扎实推进 全省新开工8.02万套
- 环球热头条丨分式方程的解法及应用教学设计(分式
- 长安马自达发布电动化占率 开启第二个200万时代
- 特斯拉中国工厂普通工人月薪1万块
- 硝烟中的苏丹:枪声响彻全城,动荡引发“停摆”_
- 今亮点!转发
- [快讯]华联股份发布解除质押及再质押公告|百事通
- 超能外星人迅雷下载_超能外星人
- 环球微头条丨中国银河给予有色金属推荐评级:美联
- 世界快资讯丨华为发布新品nova 11 Ultra搭载双
- 淄博市中心医院耳科成功完成一例高难度、复杂中耳
- 万豪计划未来两年在印度新开100多家酒店
- 全球微动态丨媒体人:虽认定为消极比赛 处罚却基
- 公租房承租权可以作为遗产继承吗
- 环球快报:总投资6亿元!年产6500吨高纯锂电材料项
- 【环球时快讯】国家能源局:预计2030年海上风电开
- 升级智能锁安全防线!德施曼智能锁引领行业进入全
- 每日快讯!勤视察、集民声,人大代表助力乡村振兴
- 男孩名字寓意好的字豁达开朗_男孩名字寓意好的字_
- 花式出招 变“颜值”为产值 “春游石家庄”助力
油气
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
经济
-
中新网通辽10月18日电 (记者 张林虎)18日,记者从内蒙古自治区通辽市奈曼旗公安局获悉,国家一级保护动物--梅花鹿误入当地村民羊群,
-
中新网杭州10月18日电 (王题题 胡燕婕)云天收夏色,浅秋正渐浓。10月18日,浙江杭州市西湖游船有限公司推出的惠民多站点“西湖环湖游
-
中新网福州10月18日电 (记者 龙敏 王东明)福州市晋安区官方18日晚间通报,18日14时47分,晋安区岳峰镇化工路爱摩轮商业广场项目摩天
-
中新网兰州10月18日电 (闫姣 艾庆龙 吉翔)“红山白土头,黄河向西流。”不少人疑问,天下黄河向东流,为何甘肃永靖县这段黄河却向西
-
中新网北京10月18日电 《清华城市健康设施指数》18日在北京发布。报告成果显示,城市健康设施指数领先城市以中心城市和东部沿海城市