Vue深入浅出computed(进阶必看系列)
2020/4/28 11:03:58
本文主要是介绍Vue深入浅出computed(进阶必看系列),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
简介
大家好,我是六六。在我们开发当中,当然少不了使用computed计算属性,都知道结果会被缓存起来,那是怎么做到的呢。当然了,在面试中也经常会问到与watch到底有什么不同,那儿接下来,我们将走进computed的内部,详细的去了解一下。
目录
- 你知道的computed特性
- computed原理是什么
- computed原理具体实现
- 详说整个computed过程
- 面试题与扩展
- 总结
1.你知道的computed特性:
- 计算属性在使用的时候,要当做普通属性使用就好,不需要加()
- 只要计算属性这个function内部所用到的data中的数据发生了变化,就会立即重新计算这个计算属性的值
- 计算属性的求值结果,会被缓存起来,方便下次继续使用;如果计算属性方法中,所依赖的任何数据,都没有发生过变化,则不会重新对计算属性求值
- 可以为函数或者对象
2.computed原理是什么:
学习中最常见听到的一句话就是,computed就是一个特殊的getter方法。在代理函数可以结合watcher实现缓存与收集依赖。
计算属性具有缓存性,如何知道计算属性的返回值发生变化呢?
这其实就是结合了watcher的dirty属性来分辨的,当dirty为true时,说明需要重新计算,当为false时,计算属性没有改变,不需要重新计算,直接读取缓存值就好。
模拟一下计算属性内容发生改变后:
- 计算属性的watcher和组件内的watcher都会得到通知
- 计算属性的watcher将自己的属性dirty设置为true
- 下次读取计算属性时,因为dirty为true重新计算一次值
- 组件watcher得到通知,从而执行render函数进行重新渲染
3.computed原理具体实现:
3.1 initComputed初始化
const computedWatcherOptions={lazy:true} function initComputed(vm, computed) { const watchers = vm._computedWatchers = Object.create(null) const isSSR = isServerRendering() for (const key in computed) { // userDef我们定义的计算属性 const userDef = computed[key] // 获取getter const getter = typeof userDef === 'function' ? userDef : userDef.get // 非SSR环境 if (!isSSR) { // 创建watcher实例 watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions) } if (!(key in vm)) { defineComputed(vm, key, userDef) } } } 复制代码
具体讲解:
- initComputed函数接受两个参数:vm实例和computed对象
- 声明了一个变量watchers,并保存在vm._computedWatchers
- 声明变量isSSR用于判断是否在SSR环境
- 使用for..in循环computed对象,以此初始化每个计算属性
- 用getter保存计算结果的值
- 创建watcher实例
- 判断计算属性的值是否已经在vm实例上定义了,如果没有,执行defineComputed函数
到这里,我们就明白了,最后我们会执行defineComputed函数,对每个计算属性进行定义和初始化。下面我们来看看这个函数。
3.2 defineComputed定义计算属性
const sharePropertyDefinition={ enumerable:true, configurable:true, get:noop, set:noop } function defineComputed(target,key,userDef){ // 判断环境 const shouldCache=!isServerRendering() // 设置的为函数 if(typeof userDef==='function'){ sharePropertyDefinition.get=shouldCache?createComputedGetter(key):userDef sharePropertyDefinition.set=noop } // 设置的为对象 else{ sharePropertyDefinition.get=userDef.get?shouldCache&&userDef.cache!=false?createComputedGetter(key):userDef.get:noop sharePropertyDefinition.set=userDef.set?userDef.set:noop } Object.defineProperty(target,key,sharePropertyDefinition) } 复制代码
具体讲解:
- 首先定义sharePropertyDefinition变量,用于配合
Object.defineProperty
使用。 - 函数defineComputed接受target,key,userDef三个参数
- 使用shouldCache变量确认是否在SSR环境
- 判断userDef是否为函数,因为我们知道我们传入的computed支持函数或者对象
- 如果是函数,判断shouldCache,为true时执行createComputedGetter函数,并赋值给 sharePropertyDefinition.get
- 如果不是函数,是否写了get函数,判断shouldCache,为true时执行createComputedGetter函数,并赋值给 sharePropertyDefinition.get
- 在判断用户是否写了set函数,写了就赋值给sharePropertyDefinition,noop为空函数。
- 在最后,执行Object.defineProperty向实例挂载属性
所以,计算属性的缓存和响应式主要在于是否将getter方法设置为createComputedGetter。因为最终挂载到get方法的就是createComputedGetter函数。
3.3createComputedGetter缓存与响应式的关键
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { if (watcher.dirty) { watcher.evaluate() } if (Dep.target) { watcher.depend() } return watcher.value } } } 复制代码
具体讲解:
- 在initComputed函数中我们定义了_computedWatchers属性,通过_computedWatchers[key]拿到我们定义的watcher,所以变量watcher就是每个watcher的实例
- 判断watcher.dirty值是否为真,为真就重新计算一下,也就是执行watcher.evaluate()
- 判断Dep.target的值,如果有,就执行 watcher.depend(),最后返回watcher的value值。
代码是非常简单的,但是理解起来是需要配合watcher和dep的,如果不了解可以参考我这篇文章: 深入浅出Vue变化侦测
3.4 与watcher密不可分
class Watcher{ constructor(vm,expOrFn,cb,options){ // 无关代码 if(options){ this.lazy=!!options.lazy } this.dirty = this.lazy } evaluate () { this.value = this.get() this.dirty = false } /** * Depend on all deps collected by this watcher. */ depend () { let i = this.deps.length while (i--) { this.deps[i].depend() } } } 复制代码
evaluate方法:就是从watcher重新获取一次表达式的值
depend方法:
- this.deps[i]就是计算属性所依赖的状态
- 调用depend方法可以将组件的watcher实例添加到dep实例中,意思就是计算属性所依赖的状态改变也会通知组件,更具体说,就是组件watcher也会观察计算属性所依赖的状态。(组件watcher是如何拿到的呢)
4.详说整个computed过程:
- 使用watcher读取计算属性
- 读取计算属性函数中的数据,定义响应式时,get读取的就是watcher.value
- 计算属性和组件watcher同时观察数据的变化
- 当数据改变后,计算属性和组件watcher都会收到通知
- 组件watcher会重新渲染组件
- 计算属性watcher因为数据改变,dirty属性为true,将重新计算
- 计算属性计算的结果用于本次渲染,并缓存起来
5.面试题:watch和computed的区别是什么?
其实我觉得这两个作用是完全不一样,不知道为什么总拿来比较。
- watch是一种行为,在状态改变之后需要做什么。
- computed就是一种状态,也可以说多种状态初始化后的结果。
我认为把,computed与filter作为比较不是更好一些吗?都是用来初始化状态用的。
- computed更适用于大量数据计算的结果,并且反复使用,而且不常更新。因为有缓存,大大提升性能。
- filter适用于少量数据进行初始化处理,计算量不能太大,因为每次渲染都会计算,并且可以频繁更新。
6.总结
一开始学这个知识点确实很懵,一个知识点看了很多遍,到最后才领悟过来,不过也有很多一知半解的地方,所以每天还是要继续努力。
这篇关于Vue深入浅出computed(进阶必看系列)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-04package.json 文件位置在哪?-icode9专业技术文章分享
- 2024-10-01Craco.js学习:从入门到实践指南
- 2024-10-01Create-React-App学习:入门与实践指南
- 2024-10-01CSS-in-JS学习:从入门到实践指南
- 2024-09-30JSX语法学习:从入门到初步掌握
- 2024-09-30Mock.js学习:入门教程与实战演练
- 2024-09-30React Hooks学习:从入门到实践
- 2024-09-30受控组件学习:React中的基础入门教程
- 2024-09-29JS定时器教程:初学者必看指南
- 2024-09-29JS对象教程:初学者的全面指南