深入理解 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 ...
随机推荐
- EL知识点总结
EL知识点总结 - canger - 博客园http://www.cnblogs.com/canger/p/6675799.html 1 只可以获取内置对象的属性值,不可以获取JSP页面中局部java ...
- 对象及变量的并发访问(同步方法、同步代码块、对class进行加锁、线程死锁)&内部类的基本用法
主要学习多线程的并发访问,也就是使得线程安全. 同步的单词为synchronized,异步的单词为asynchronized 同步主要就是通过锁的方式实现,一种就是隐式锁,另一种是显示锁Lock,本节 ...
- HardNet解读
论文:Working hard to know your neighbor’s margins: Local descriptor learning loss 为什么介绍此文:这篇2018cvpr文 ...
- Shell-find . -type f -name "*.log" -print0 | xargs -0 rm -f
用 rm 删除太多的文件时候,可能得到一个错误信息:/bin/rm Argument list too long.
- Python运维开发基础09-函数基础【转】
上节作业回顾 #!/usr/bin/env python3 # -*- coding:utf-8 -*- # author:Mr.chen # 实现简单的shell命令sed的替换功能 import ...
- python3+selenium入门01-环境搭建
作为一个测试,在最近两年应该有明显的感觉.那就是工作变的难找,要求变的高了,自动化测试,性能测试等.没有自动化测试能力,只会点点点工作难找不说,工资也不高.所以还是要学习一些技术.首先要学习一门编程语 ...
- 关于session,cookie,Cache
昨天看了<ASP.NET 页面之间传值的几种方式>之后,对session,cookie,Cache有了更近一步的了解,以下是相关的内容 一.Session 1.Session基本操作 a. ...
- VS2015 代码片段整理
1.什么是代码段? 将一段代码行提取出来,可以多次重复的使用.VS IDE提供对代码段的完整支持.使代码编写更快.更容易.更可靠. 2.系统默认代码段 对于开发人员的例行任务,Visual Studi ...
- 【转】细说new与malloc的10点区别
1.申请的内存所在位置 new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存.自由存储区是C++基于new操作符的一个抽象概念,凡是通过new ...
- spring mvc数据绑定与表单标签库
Book类为 package org.shaoxiu; public class Book { private String id; private String name; public Book( ...