Vue3计算属性怎么实现
计算属性
Vue3的官方文档中,对于计算属性有这样的描述:
对于任何包含响应式数据的复杂逻辑,我们都应该使用计算属性。
计算属性只会在相关响应式依赖发生改变时重新求值。
从上面的描述可以明确计算属性的需求,计算属性计算的是响应式数据(满足描述1),且计算结果应当被缓存(满足描述2)。让我们一个一个来实现,先使用computed
创建一个计算属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
在(1)处,我们简单写了一个计算属性的功能,为了实现通过sumRes.value
读取计算属性值功能,在实现计算属性时,需要返回一个对象,通过对象内的get
触发副作用函数。
1 2 3 4 5 6 7 8 9 |
|
但这个函数显然是无法执行的,这是因为前面我们在实现effect
时,需要直接执行副作用函数,不需要提供返回值。没有返回值,computed
自然无法获取到effect
的执行结果。因此,当在计算属性中使用effect
时,需要将副作用函数返回给计算属性,由计算属性决定何时执行,而不再由effect
立即执行(即懒执行)。
为了实现这点,就需要向effect
中添加一个开关lazy
,考虑到我们可能将来还需要对effect
配置其它特性,我们使用一个对象options
来封装这个开关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
我们在(4)处放置了lazy
开关,不需要懒执行的副作用函数同样会自动执行。在(1)(2)(5)处返回了副作用函数的结果,供懒执行使用。同时在(3)处向下传递了options
,保证在effect
发生嵌套时,也使得副作用函数执行预期的行为。基于上述effect
的修改,我们在computed
中设置lazy
开关。
1 2 3 4 5 6 7 8 9 10 |
|
从上图中可以看出,我们已经实现了描述1,即使用计算属性进行响应式数据的计算,当响应式数据的值发生变化时,计算属性的值也会随之改变。但观察上文代码的(6)处,不难发现,无论什么情况下,只要读取sumRes.value
的值,就会触发一次副作用函数,使其重新进行可能不必要的执行。所以接着,我们尝试实现描述2,缓存计算属性的结果。
先从最简单的入手,我们用一个变量value
来缓存上次计算的值,并添加一个dirty
开关,记录是否需要重新触发副作用函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
修改之后,缓存的值就能生效了。但这样做产生了一个明显的BUG,当dirty
的值被置为false
时,无法再变为true
,这也就意味着,无论响应式数据obj.bar
与obj.foo
如何变化,计算属性的值永远都只能是缓存的值value
,如下图所示。
为了解决这个问题,我们需要一种方式,能够在obj.bar
或obj.foo
的值变化时,在获取sumRes.value
之前,将dirty
开关的值置为true
。受前面懒加载的启发,我们尝试能不能通过配置options
来实现这个功能。
1 2 3 4 5 6 7 8 9 10 11 |
|
再来回忆一下响应式对象的整个流程,当响应式对象中的数据被修改时,执行了trigger
去触发收集的副作用函数。而在计算属性中,我们不再需要自动的触发副作用函数。所以自然会想到,能否在这个地方将dirty
置为true
呢?按照这个思路,我们先对trigger
进行修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
按照前文的思路,我们在(7)处增加了一个判断,如果副作用函数fn
的配置项options
中含有scheduler
函数,我们就执行scheduler
而非副作用函数fn
。我们称这里的scheduler
为调度器。相应的,我们在计算属性中添加调度器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
在(8)处我们添加了调度器。添加调度器后,让我们再来串一下整个流程,当响应式数据被修改时,才会执行trigger
函数。由于我们传入了调度器,因此trigger
函数在执行时不再触发副作用函数,转而执行调度器,此时dirty
开关的值变为了true
。当程序再往下执行时,由于dirty
已经变为true
,副作用函数就可以正常被手动触发。效果如下图所示。
到这里为止,计算属性在功能上已经实现完毕了,让我们尝试完善它。在Vue中,当计算属性中的响应式数据被修改时,计算属性值会同步更改,进而重新渲染,并在页面上更新。渲染函数也会执行effect
,为了说明问题,让我们使用effect
简单的模拟一下。
1 2 |
|
这里我们的预期是当obj.foo
或obj.bar
改变时,effect
会自动重新执行。这里存在的问题是,正常的effect
嵌套可以被自动触发(这点我们在上一篇博客中已经实现了),但sumRes
包含的effect
仅会在被读取value
时才会被get
触发执行,这就导致响应式数据obj.foo
与obj.bar
无法收集到外部的effect
,收集不到的副作用函数,自然无法被自动触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
在(10)处我们手动收集了副作用函数,并当响应式数据被修改时,触发它们。
使用微任务优化调度器
在设计调度器时,我们忽略了一个潜在的问题。
还是先来看一个例子:
1 2 3 4 |
|
随着响应式数据obj.foo
被不停修改,副作用函数也被不断重复执行,在明显的延迟之后,控制台打印出了大量的数据。但在前端的实际开发中,我们往往只关心最终结果,拿到结果显示在页面上。在这种条件下,我们如何避免副作用函数被重复执行呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
这里我们的思路是使用微任务队列来进行优化。(11)处我们定义了一个Set
作为任务队列,(12)处我们定义了一个Promise
来使用微任务。精彩的部分来了,我们的思路是将副作用函数放入任务队列中,由于任务队列是基于Set
实现的,因此,重复的副作用函数仅会保留一个,这是第一点。接着,我们执行flushJob()
,这里我们巧妙的设置了一个isFlushing
开关,这个开关保证了被微任务包裹的任务队列在一次事件循环中只会执行一次,而执行的这一次会在宏任务完成之后触发任务队列中所有不重复的副作用函数,从而直接拿到最终结果,这是第二点。按照这个思路,我们在effect
中添加调度器。
1 2 3 4 5 6 |
|
需要注意的是,浏览器在执行微任务时,会依次处理微任务队列中的所有微任务。因此,微任务在执行时会阻塞页面的渲染。因此,在实践中需避免在微任务队列中放置过于繁重的任务,以免导致性能问题。
以上是Vue3计算属性怎么实现的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

想要实现页面的局部刷新,我们只需要实现局部组件(dom)的重新渲染。在Vue中,想要实现这一效果最简便的方式方法就是使用v-if指令。在Vue2中我们除了使用v-if指令让局部dom的重新渲染,也可以新建一个空白组件,需要刷新局部页面时跳转至这个空白组件页面,然后在空白组件内的beforeRouteEnter守卫中又跳转回原来的页面。如下图所示,如何在Vue3.X中实现点击刷新按钮实现红框范围内的dom重新加载,并展示对应的加载状态。由于Vue3.X中scriptsetup语法中组件内守卫只有o

tinymce是一个功能齐全的富文本编辑器插件,但在vue中引入tinymce并不像别的Vue富文本插件一样那么顺利,tinymce本身并不适配Vue,还需要引入@tinymce/tinymce-vue,并且它是国外的富文本插件,没有通过中文版本,需要在其官网下载翻译包(可能需要翻墙)。1、安装相关依赖npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下载中文包3.引入皮肤和汉化包在项目public文件夹下新建tinymce文件夹,将下载的

vue3+vite:src使用require动态导入图片报错和解决方法vue3+vite动态的导入多张图片vue3如果使用的是typescript开发,就会出现require引入图片报错,requireisnotdefined不能像使用vue2这样imgUrl:require(’…/assets/test.png’)导入,是因为typescript不支持require所以用import导入,下面介绍如何解决:使用awaitimport

Vue实现博客前端,需要实现markdown的解析,如果有代码则需要实现代码的高亮。Vue的markdown解析库有很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。这些库都大同小异。这里选用的是marked,代码高亮的库选用的是highlight.js。具体实现步骤如下:一、安装依赖库在vue项目下打开命令窗口,并输入以下命令npminstallmarked-save//marked用于将markdown转换成htmlnpmins

前言无论是vue还是react,当遇到多处重复代码的时候,我们都会想着如何复用这些代码,而不是一个文件里充斥着一堆冗余代码。实际上,vue和react都可以通过抽组件的方式来达到复用,但如果遇到一些很小的代码片段,你又不想抽到另外一个文件的情况下,相比而言,react可以在相同文件里面声明对应的小组件,或者通过renderfunction来实现,如:constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

最终效果安装VueCropper组件yarnaddvue-cropper@next上面的安装值针对Vue3的,如果时Vue2或者想使用其他的方式引用,请访问它的npm官方地址:官方教程。在组件中引用使用时也很简单,只需要引入对应的组件和它的样式文件,我这里没有在全局引用,只在我的组件文件中引入import{userInfoByRequest}from'../js/api'import{VueCropper}from'vue-cropper&

vue3+ts+axios+pinia实现无感刷新1.先在项目中下载aiXos和pinianpmipinia--savenpminstallaxios--save2.封装axios请求-----下载js-cookienpmiJS-cookie-s//引入aixosimporttype{AxiosRequestConfig,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess

vue3项目打包发布到服务器后访问页面显示空白1、处理vue.config.js文件中的publicPath处理如下:const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({publicPath:process.env.NODE_ENV==='production'?'./':'/&
