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 计算属性的更多相关文章

  1. vue computed计算属性 watch监听

    计算属性 computed:{ 变量:function(){ return 计算好的值 } } 这时候计算好的值 就付给了你的变量 在实例中可以this.使用 注意 声明的变量的data中不可以重复声 ...

  2. vue computed计算属性和watch监听属性解疑答惑

    computed计算属性     计算属性类似于方法,用于输出data中定义的属性数据的结果,data数据变化时,计算属性的结果会同步变化,需要注意的是计算属性不可与data定义的属性同名. 相比于方 ...

  3. 理解Vue的计算属性

    计算属性是一个很邪门的东西,只要在它的函数里引用了 data 中的某个属性,当这个属性发生变化时,函数仿佛可以嗅探到这个变化,并自动重新执行. 上述代码会源源不断的打印出 b 的值.如果希望 a 依赖 ...

  4. Vue(七):computed计算属性

    简介 计算属性关键词: computed. 计算属性在处理一些复杂逻辑时是很有用的. 实例1 可以看下以下反转字符串的例子: <div id="app"> {{ mes ...

  5. 浅谈Vue中计算属性computed的实现原理

    虽然目前的技术栈已由Vue转到了React,但从之前使用Vue开发的多个项目实际经历来看还是非常愉悦的,Vue文档清晰规范,api设计简洁高效,对前端开发人员友好,上手快,甚至个人认为在很多场景使用V ...

  6. 浅谈Vue中计算属性(computed)和方法(methods)的差别

    浅谈Vue中计算属性(computed)和方法(methods)的差别 源码地址 methods方法和computed计算属性,两种方式的最终结果确实是完全相同 计算属性是基于它们的响应式依赖进行缓存 ...

  7. vue中计算属性computed方法内传参

    vue中computed计算属性无法直接进行传参 如果有传参数的需求比如说做数据筛选功能可以使用闭包函数(也叫匿名函数)实现 例如: 在上篇博客vue安装使用最后的成绩表练习中的过滤功能的实现: &l ...

  8. vue的计算属性computed和监听器watch

    <template> <div> this is A.vue <br> <!--计算属性--> <label for="msg" ...

  9. Vue之computed计算属性

    demo.html <!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3.org/19 ...

随机推荐

  1. P1880 [NOI1995]石子合并(区间DP)

    题目链接:https://www.luogu.org/problemnew/show/P1880 题目大意:中文题目 具体思路:和上一篇的思路是差不多的,也是对于每一个小的区间进行处理,然后再归并到大 ...

  2. 《Effective Java中文版(第2版).pdf》-笔记

    1.第6页 ---- Java中Properties类的操作 - bakari - 博客园http://www.cnblogs.com/bakari/p/3562244.html 公有的静态方法返回的 ...

  3. Hibernate注解与JPA

    Hibernate注解与JPA - charming的专栏 - CSDN博客http://blog.csdn.net/zxc123e/article/details/51499652 之前记录的一些东 ...

  4. Deep Neural Networks for Object Detection(翻译)

    0 - Abstract 深度神经网络(DNNs)最近在图像分类任务上表现出了突出的性能.在这篇文章中,我们进一步深入探究使用DNNs进行目标检测的问题,这个问题不仅需要对物体进行分类,并且还需要对各 ...

  5. Linux C++ TCP Socket通信实例

    环境:Linux 语言:C++ 通信方式:TCP 下面用TCP协议编写一个简单的服务器.客户端,其中服务器端一直监听本机的6666号端口.如果收到连接请求,将接收请求并接收客户端发来的消息:客户端与服 ...

  6. vim设置行号

    vim打开文件后在底行模式下输入:set nu可以在文件每行开头显示对应的行数: 若想vim打开文件默认显示行数,执行:vim /etc/vimrc,在打开的文件末尾添上:set nu,保存退出即可!

  7. Pthread:POSIX 多线程程序设计【转】

    转自:http://www.cnblogs.com/mywolrd/archive/2009/02/05/1930707.html#phtread_ref POSIX 多线程程序设计  Blaise ...

  8. Linux查看压缩文件内容【转】

    查看一个归档或者压缩文件的内容而无需解压它 得益于 Linux 社区,有很多命令行工具可以来达成上面的目标.下面就让我们来看看使用它们的一些示例. 1.使用 vim 编辑器 vim 不只是一个编辑器, ...

  9. windows 自带winmm.dll播放音频问题

    同事用的一个录音小程序在他机器上可以用,换了两个电脑不能用,获取音频长度时总是0,检查代码也没有发现具体问题.最后发现是电脑声卡驱动的问题.更新声卡驱动好了. 附上播放音频的代码: 首先,导入dll文 ...

  10. OpenStack实践系列⑥构建虚拟机实例

    OpenStack实践系列⑥构建虚拟机实例 四.创建一台虚拟机图解网络,并创建一个真实的桥接网络 创建一个单一扁平网络(名字:flat),网络类型为flat,网络适共享的(share),网络提供者:p ...