Vue中computed分析

Vuecomputed是计算属性,其会根据所依赖的数据动态显示新的计算结果,虽然使用{{}}模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的,在模板中放入太多的逻辑会让模板过重且难以维护,所以对于任何复杂逻辑,都应当使用计算属性。计算属性是基于数据的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值,也就是说只要计算属性依赖的数据还没有发生改变,多次访问计算属性会立即返回之前的计算结果,而不必再次执行函数,当然如果不希望使用缓存可以使用方法属性并返回值即可,computed计算属性非常适用于一个数据受多个数据影响以及需要对数据进行预处理的条件下使用。

描述

computed计算属性可以定义两种方式的参数,{ [key: string]: Function | { get: Function, set: Function } },计算属性直接定义在Vue实例中,所有gettersetterthis上下文自动地绑定为Vue实例,此外如果为一个计算属性使用了箭头函数,则this不会指向这个组件的实例,不过仍然可以将其实例作为函数的第一个参数来访问,计算属性的结果会被缓存,除非依赖的响应式property变化才会重新计算,注意如果某个依赖例如非响应式property在该实例范畴之外,则计算属性是不会被更新的。事实上computed会拥有自己的watcher,其内部有个属性dirty开关来决定computed的值是需要重新计算还是直接复用之前的值。

<!DOCTYPE html>
<html>
<head><title>Vue</title>
</head>
<body><div id="app"></div>
</body>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
<script type="text/javascript">var vm = new Vue({el: "#app",data: {a: 1,b: 2},template:`<div><div>{{multiplication}}</div><div>{{multiplication}}</div><div>{{multiplication}}</div><div>{{multiplicationArrow}}</div><button @click="updateSetting">updateSetting</button></div>`,computed:{multiplication: function(){console.log("a * b"); // 初始只打印一次 返回值被缓存return this.a * this.b;},multiplicationArrow: vm => vm.a * vm.b * 3, // 箭头函数可以通过传入的参数获取当前实例setting: {get: function(){console.log("a * b * 6");return this.a * this.b * 6;},set: function(v){console.log(`${v} -> a`);this.a = v;}}},methods:{updateSetting: function(){ // 点击按钮后console.log(this.setting); // 12this.setting = 3; // 3 -> aconsole.log(this.setting); // 36}},})
</script>
</html>

分析

首先在Vue中完成双向绑定是通过Object.defineProperty()实现的,Vue的双向数据绑定,简单点来说分为以下三个部分:

  • Observer: 这里的主要工作是递归地监听对象上的所有属性,在属性值改变的时候,触发相应的Watcher
  • Watcher: 观察者,当监听的数据值修改时,执行响应的回调函数,在Vue里面的更新模板内容。
  • Dep: 链接ObserverWatcher的桥梁,每一个Observer对应一个Dep,它内部维护一个数组,保存与该Observer相关的Watcher

Vue源码的实现比较复杂,会处理各种兼容问题与异常以及各种条件分支,文章分析比较核心的代码部分,精简过后的版本,重要部分做出注释,commit id0664cb0
首先在dev/src/core/instance/state.js中定义了初始化computed以及initComputed函数的实现,现在暂不考虑SSR服务端渲染的computed实现。

// dev/src/core/instance/state.js line 47
export function initState (vm: Component) {vm._watchers = []const opts = vm.$options // 获取组件定义的选项if (opts.props) initProps(vm, opts.props)if (opts.methods) initMethods(vm, opts.methods)if (opts.data) {initData(vm)} else {observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed) // 定义computed属性则进行初始化if (opts.watch && opts.watch !== nativeWatch) {initWatch(vm, opts.watch)}
}// dev/src/core/instance/state.js line 169
function initComputed (vm: Component, computed: Object) {// $flow-disable-lineconst watchers = vm._computedWatchers = Object.create(null) // 创建一个没有原型链指向的对象// computed properties are just getters during SSRconst isSSR = isServerRendering()for (const key in computed) {const userDef = computed[key] // 获取计算属性的key值定义const getter = typeof userDef === 'function' ? userDef : userDef.get // 由于计算属性接受两种类型的参数 此处判断用以获取getterif (process.env.NODE_ENV !== 'production' && getter == null) {warn(`Getter is missing for computed property "${key}".`,vm)}if (!isSSR) {// create internal watcher for the computed property.// 生成computed watcher(vm, getter, noop, { lazy: true })watchers[key] = new Watcher( // 计算属性创建观察者watcher和消息订阅器depvm,getter || noop,noop,computedWatcherOptions)}// component-defined computed properties are already defined on the// component prototype. We only need to define computed properties defined// at instantiation here.if (!(key in vm)) { // 检查重名属性defineComputed(vm, key, userDef) // 定义属性} else if (process.env.NODE_ENV !== 'production') {if (key in vm.$data) {warn(`The computed property "${key}" is already defined in data.`, vm)} else if (vm.$options.props && key in vm.$options.props) {warn(`The computed property "${key}" is already defined as a prop.`, vm)}}}
}

defineComputed传入了三个参数,vm实例、计算属性的key以及userDef计算属性的定义,属性描述符sharedPropertyDefinition在初始化定义之后经过userDefshouldCache等多重判断后被重写,进而通过Object.defineProperty(target, key, sharedPropertyDefinition)进行属性的定义。

// dev/src/core/instance/state.js line 31
const sharedPropertyDefinition = {enumerable: true,configurable: true,get: noop,set: noop
}// dev/src/core/instance/state.js line 210
export function defineComputed (target: any,key: string,userDef: Object | Function
) {const shouldCache = !isServerRendering()if (typeof userDef === 'function') {sharedPropertyDefinition.get = shouldCache? createComputedGetter(key): createGetterInvoker(userDef)sharedPropertyDefinition.set = noop} else {sharedPropertyDefinition.get = userDef.get? shouldCache && userDef.cache !== false? createComputedGetter(key): createGetterInvoker(userDef.get): noopsharedPropertyDefinition.set = userDef.set || noop}if (process.env.NODE_ENV !== 'production' &&sharedPropertyDefinition.set === noop) {sharedPropertyDefinition.set = function () {warn(`Computed property "${key}" was assigned to but it has no setter.`,this)}}Object.defineProperty(target, key, sharedPropertyDefinition)
}/**经过重写之后的属性描述符在某条件分支大致呈现如下sharedPropertyDefinition = {enumerable: true,configurable: true,get: function computedGetter () {const watcher = this._computedWatchers && this._computedWatchers[key]if (watcher) {if (watcher.dirty) {watcher.evaluate()}if (Dep.target) {watcher.depend()}return watcher.value}},set: userDef.set || noop} 当计算属性被调用时便会执行 get 访问函数,从而关联上观察者对象 watcher 然后执行 wather.depend() 收集依赖和 watcher.evaluate() 计算求值。
*/

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://cn.vuejs.org/v2/api/#computed
https://juejin.im/post/6844903678533451783
https://juejin.im/post/6844903873925087239
https://cn.vuejs.org/v2/guide/computed.html
https://zheyaoa.github.io/2019/09/07/computed/
https://www.cnblogs.com/tugenhua0707/p/11760466.html

Vue中computed分析相关推荐

  1. 深究vue中computed顺序、watch顺序、响应次数

    文章目录 前言 深究步骤 1.代码 2.输出结果 3.分析过程 总结 前言 深究vue中computed顺序.watch顺序.响应次数 深究步骤 1.代码 <template><di ...

  2. vue中computed(计算属性)和watch在实现父子组件props同步时的实际区分

    vue中computed和watch的对比是一个很有意思的话题. 看过官网教程以后,我们往往更倾向多使用computed.computed优点很多,却在某些时候不太适用. 今天我们就稍微讨论一下,当我 ...

  3. vue方法调用失败后多次调用_浅析Vue中 computed / watch / methods的区别

    思考:请说下Vue中computed 和 watch 的区别( 面试题 ) 构造选项 computed / watch / methods computed ● computed 起初构想 在Vue的 ...

  4. VUE中computed 、created 、mounted 的先后顺序

    VUE中computed .created .mounted 的先后顺序 1.computed .created .mounted 的先后顺序 created => computed => ...

  5. vue中computed的详细讲解

    vue中computed的详细讲解 1.定义 2.用法 3.computed的响应式依赖(缓存) 4.应用场景 1.定义 computed是vue的计算属性,是根据依赖关系进行缓存的计算,只有在它的相 ...

  6. Vue中computed计算属性和data数据获取的问题

    获取到数据(对象.数组),截取一部分显示到页面中,用computed计算属性来实现截取数据然后直接输出到页面. <div class="detailBox"><h ...

  7. Vue 中 computed vs methods的区别

    computed:计算属性 methods:方法 watch:侦听器 computed与methodes区别 1.computed是响应式的,methods并非响应式. 2.调用方式不一样,compu ...

  8. vue中computed和watched的区别

    computed computed在vue中起到计算属性作用. <p>firstName: <input type="text" v-model="fi ...

  9. Vue中computed原理

    1.computed大致流程 computed是vue中的计算属性,在依赖的值发生变化的时候进行重新计算,否则使用缓存. 而在面试中常被问及computed原理,这篇文章主要作为我学习computed ...

最新文章

  1. linux个性化定制登录信息
  2. python 虚拟环境就两句话整那么多没用的
  3. (How to)Windows Live Writer使用技巧
  4. 【知识星球】有三AI编程与开源框架正式开通
  5. intellij2019.1 破jie不了的解决办法
  6. Lisenter笔记
  7. 每次都需要解释大量指令?使用 PolarDB-X 向量化引擎
  8. Python学习秘籍 这些窍门就连老司机都不一定知道 值得学习
  9. Quick Dicom batch editor(DICOM标签浏览编辑器)
  10. 佳点集java_java实现遗传算法实例分享(打印城市信息)
  11. html堆叠柱状图脚本,Highcharts 堆叠组柱形图
  12. 使用Tika进行文本抽取
  13. ae效果英文版翻译对照表_用AE设计动态海报教程
  14. 杰控组态西门子PLC
  15. 常用积分类型(积分公式)
  16. 开心网程炳皓:早期创业公司应该做一根针
  17. 软考2022下半年上午题真题和知识点整理
  18. 2021强网杯 Web赌徒 WP
  19. sesame芝麻开门operrdf研究2
  20. STM32定时器中断时间计算

热门文章

  1. 什么是ZooKeeper
  2. springboot集成与使用Sentinel
  3. eureka注册中心搭建与使用
  4. 006-虚拟机中centos7实现nat静态ip上网
  5. Spring面试,IoC和AOP的理解
  6. Maximum Subarray leetcode java
  7. 移动电子商务,“移动”至生活
  8. 关于搞技术的一点思考
  9. 数据不平衡问题及解决方案
  10. php7++linux安装,安装PHP5和PHP7