深入理解 Vue Computed 计算属性
Computed 计算属性是 Vue 中常用的一个功能,我们今天来说一下他的执行过长
拿官网简单的例子来看一下:
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div> var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// a computed getter
reversedMessage: function () {
// `this` points to the vm instance
return this.message.split('').reverse().join('')
}
}
})
Vue 源码分析 Computed 的实现原理
data 属性初始化 getter setter:
// src/observer/index.js // 这里开始转换 data 的 getter setter,原始值已存入到 __ob__ 属性中
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
// 判断是否处于依赖收集状态
if (Dep.target) {
// 建立依赖关系
dep.depend()
...
}
return value
},
set: function reactiveSetter (newVal) {
...
// 依赖发生变化,通知到计算属性重新计算
dep.notify()
}
})
computed 计算属性初始化
// src/core/instance/state.js // 初始化计算属性
function initComputed (vm: Component, computed: Object) {
...
// 遍历 computed 计算属性
for (const key in computed) {
...
// 创建 Watcher 实例
// create internal watcher for the computed property.
watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions) // 创建属性 vm.reversedMessage,并将提供的函数将用作属性 vm.reversedMessage 的 getter,
// 最终 computed 与 data 会一起混合到 vm 下,所以当 computed 与 data 存在重名属性时会抛出警告
defineComputed(vm, key, userDef)
...
}
} export function defineComputed (target: any, key: string, userDef: Object | Function) {
...
// 创建 get set 方法
sharedPropertyDefinition.get = createComputedGetter(key)
sharedPropertyDefinition.set = noop
...
// 创建属性 vm.reversedMessage,并初始化 getter setter
Object.defineProperty(target, key, sharedPropertyDefinition)
} function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
// watcher 暴露 evaluate 方法用于取值操作
watcher.evaluate()
}
// 同第1步,判断是否处于依赖收集状态
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
无论是属性还是计算属性,都会生成一个对应的 watcher 实例。
// src/core/observer/watcher.js // 当通过 vm.reversedMessage 获取计算属性时,就会进到这个 getter 方法
get () {
// this 指的是 watcher 实例
// 将当前 watcher 实例暂存到 Dep.target,这就表示开启了依赖收集任务
pushTarget(this)
let value
const vm = this.vm
try {
// 在执行 vm.reversedMessage 的函调函数时,会触发属性(步骤1)和计算属性(步骤2)的 getter
// 在这个执行过程中,就可以收集到 vm.reversedMessage 的依赖了
value = this.getter.call(vm, vm)
} catch (e) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
if (this.deep) {
traverse(value)
}
// 结束依赖收集任务
popTarget()
this.cleanupDeps()
}
return value
}
上面多出提到了 dep.depend, dep.notify, Dep.target,那么 Dep 究竟是什么呢?
Dep 的代码短小精悍,但却承担着非常重要的依赖收集环节。
// src/core/observer/dep.js
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
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++) {
// 更新 watcher 的值,与 watcher.evaluate() 类似,
// 但 update 是给依赖变化时使用的,包含对 watch 的处理
subs[i].update()
}
}
}
// 当首次计算 computed 属性的值时,Dep 将会在计算期间对依赖进行收集
Dep.target = null
const targetStack = []
export function pushTarget (_target: Watcher) {
// 在一次依赖收集期间,如果有其他依赖收集任务开始(比如:当前 computed 计算属性嵌套其他 computed 计算属性),
// 那么将会把当前 target 暂存到 targetStack,先进行其他 target 的依赖收集,
if (Dep.target) targetStack.push(Dep.target)
Dep.target = _target
}
export function popTarget () {
// 当嵌套的依赖收集任务完成后,将 target 恢复为上一层的 Watcher,并继续做依赖收集
Dep.target = targetStack.pop()
}
总结
1. data 属性初始化 getter setter
2. computed 计算属性初始化,提供的函数将用作属性 vm.reversedMessage 的 getter
3. 当首次获取 reversedMessage 计算属性的值时,Dep 开始依赖收集
4. 在执行 message getter 方法时,如果 Dep 处于依赖收集状态,则判定 message 为 reversedMessage 的依赖,并建立依赖关系
5. 当 message 发生变化时,根据依赖关系,触发 reverseMessage 的重新计算
深入理解 Vue Computed 计算属性的更多相关文章
- vue computed计算属性 watch监听
计算属性 computed:{ 变量:function(){ return 计算好的值 } } 这时候计算好的值 就付给了你的变量 在实例中可以this.使用 注意 声明的变量的data中不可以重复声 ...
- vue computed计算属性和watch监听属性解疑答惑
computed计算属性 计算属性类似于方法,用于输出data中定义的属性数据的结果,data数据变化时,计算属性的结果会同步变化,需要注意的是计算属性不可与data定义的属性同名. 相比于方 ...
- 理解Vue的计算属性
计算属性是一个很邪门的东西,只要在它的函数里引用了 data 中的某个属性,当这个属性发生变化时,函数仿佛可以嗅探到这个变化,并自动重新执行. 上述代码会源源不断的打印出 b 的值.如果希望 a 依赖 ...
- Vue(七):computed计算属性
简介 计算属性关键词: computed. 计算属性在处理一些复杂逻辑时是很有用的. 实例1 可以看下以下反转字符串的例子: <div id="app"> {{ mes ...
- 浅谈Vue中计算属性computed的实现原理
虽然目前的技术栈已由Vue转到了React,但从之前使用Vue开发的多个项目实际经历来看还是非常愉悦的,Vue文档清晰规范,api设计简洁高效,对前端开发人员友好,上手快,甚至个人认为在很多场景使用V ...
- 浅谈Vue中计算属性(computed)和方法(methods)的差别
浅谈Vue中计算属性(computed)和方法(methods)的差别 源码地址 methods方法和computed计算属性,两种方式的最终结果确实是完全相同 计算属性是基于它们的响应式依赖进行缓存 ...
- vue中计算属性computed方法内传参
vue中computed计算属性无法直接进行传参 如果有传参数的需求比如说做数据筛选功能可以使用闭包函数(也叫匿名函数)实现 例如: 在上篇博客vue安装使用最后的成绩表练习中的过滤功能的实现: &l ...
- vue的计算属性computed和监听器watch
<template> <div> this is A.vue <br> <!--计算属性--> <label for="msg" ...
- Vue之computed计算属性
demo.html <!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3.org/19 ...
随机推荐
- cxf动态调用外部web service 报告异常java.lang.NoSuchFieldError: QUALIFIED
原因:cxf 依赖的xmlschema-core 与xfire-all依赖的xmlschema冲突.(百度搜索亦得知:cxf 依赖的xmlschema-core 与axis2-kernel依赖的xml ...
- Django中的缓存基础知识
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5 ...
- 前端-----html(1)
基本结构 Doctype Doctype告诉浏览器使用什么样的html或xhtml规范来解析html文档 <!DOCTYPE html> bead标签 Meta 提供有关页面的元信息,例: ...
- flask 连接数据库
FLASK 连接mysql 数据库 1 # -*- encoding: utf-8 -*- 2 3 from flask import Flask 4 #导入第三方连接库 5 from flask_s ...
- java ArrayList、Vector、LinkedList区别
- android 组建添加透明度
给TextView添加透明度,起初用的方法是android:alpha = "0.3" 添加后,文字显示也有点透明发虚,后来改成设置background,然后 backgroun ...
- ASP.NET MVC 4 从示例代码展开,连接默认SQL Server数据库
VS2013里面,点击菜单[视图]-[SQL server对象资源管理器],右键点击[SQL Server]节点,选择[添加SQL Server]自动生成. 这只是开始,可以让网上下载下来的例子运行出 ...
- UML类图表达
什么是UML类图? 类图显示了一组类.接口.协作以及他们之间的关系.在UML中问题域最终要被逐步转化,通过类来建模,通过编程语言构建这些类从而实现系统.类加上他们之间的关系就构成了类图,类图中还可以包 ...
- [转] 图 + 文 + 公式 理解LSTM
转自公号“机器之心” LSTM入门必读:从入门基础到工作方式详解 长短期记忆(LSTM)是一种非常重要的神经网络技术,其在语音识别和自然语言处理等许多领域都得到了广泛的应用..在这篇文章中,Edwin ...
- 2017-2018-2 20165231 实验三 敏捷开发与XP实践
实验报告封面 课程:Java程序设计 班级:1652班 姓名:王杨鸿永 学号:20165231 指导教师:娄嘉鹏 实验日期:2018年4月28日 实验时间:15:25 - 17:15 实验序号:实验三 ...