返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue3computed初始化获取设置值实现示例
  • 257
分享到

Vue3computed初始化获取设置值实现示例

Vue3computed值设置获取Vue3computed初始化 2022-11-13 18:11:24 257人浏览 独家记忆
摘要

目录computed 用法computed 实现computed 初始化computed 获取值的实现值的展示缓存功能computed 设置值实现computed 用法 本文给大家带

computed 用法

本文给大家带来的是vue3 中 computed api的实现。

大家看过Vue3的官网,应该都知道,在vue3 的组合式API中,computed这个功能与以往的有所不同了。

以往vue2 的 computed 用法:

export default {
    components: {
    },
    computed: {
        people() {
            return '这个人' + this.age + '岁'
        }
    },
    watch() {
    },
    data() {
        return {
            age: 1
        }
    }
} 

现在我们大多数情况使用的是组合式 API,可以直接使用computed函数来进行属性的监听。
比如官网的这种案例写法:

const count = ref(1) const plusOne = computed(() => count.value + 1) // 只读属性
console.log(plusOne.value) // 2

vue3 中的 computed 接受一个 getter 函数,返回一个响应式的ref对象,并且该对象是只读属性。读取该属性通过 .value 的形式来对外暴露该属性的值。
此外,如果想要修改该对象,可以传入一个带有 get 和 set 函数的对象,通过get 和 set 分别读取和设置值。
我们来看看这样的一个简单的computed用法:

 <div id="app"></div>
<!-- <script src="./Reactivity.global.js"></script> -->
    <script src="../../../../node_modules/@vue/reactivity/dist//reactivity.global.js"></script>
 <script>
     const { reactive, effect, computed } = VueReactivity
     const author = reactive({
         name: 'clying',
         sex: '女'
     })
     // 用法一:
     // const introduction = computed({ // 调用的是defineProperty 中的 
     //     // getter
     //     get() {
     //         console.log('run get');
     //         return author.name + '是个' + author.sex + '程序员!'
     //     },
     //     // setter
     //     set(newValue) {
     //         // console.log(newValue);
     //         // 注意:我们这里使用的是解构赋值语法
     //         [author.name, author.sex] = newValue.split(' ')
     //     }
     // })
     // 用法二:
     // 一个计算属性 ref
     const introduction = computed(() => {
         console.log('run get');
         return author.name + '是个' + author.sex + '程序员!'
     })
     effect(() => {
         app.innerhtml = introduction.value // computed通过 .value 读取
     })
     introduction.value
     introduction.value
     introduction.value
 </script>

在上述案例中,我们可以很容易就知道,不管读取introduction几次,只要值未发生改变,始终只会输出一次run get

computed 实现

那么既然使用vue可以实现,是不是我们也可以自己去搞一个叻?

其实,computed 也是基于effect这个功能函数来实现的。我们可以在我们完成的effect功能上继续扩展。

computed 初始化

一步步来,我们先来实现computed的读取值功能。

const introduction = computed({ // 调用的是defineProperty 中的 
    // getter
    get() {
        console.log('run get');
        return author.name + '是个' + author.sex + '程序员!'
    },
    // setter
    set(newValue) {
        console.log(newValue);
        // 注意:我们这里使用的是解构赋值语法
        [author.name, author.sex] = newValue.split(' ')
    }
})

使用原有的 computed API可以发现,计算属性introduction其实是一个ComputedRefImpl类。

那么我们要做的就是先初始化一个类,然后对外暴露其实例。computed可以接收两种写法,那么我们在接收参数的时候,需要判断下用户传入的是对象还是一个回调函数。

  • 如果接收的是一个回调函数,那么就只存在取值的get,无法设置值;
  • 如果接收的是一个对象,那么就应该存在取值的get和设置值的set
export const computed = (getterOrOptions) => {
    let onlyGetter = isFunction(getterOrOptions) // 用户传入的回调
    let getter
    let setter
    if (onlyGetter) {
        getter = getterOrOptions
        setter = () => {
            console.warn('no set');
        }
    } else { // 用户传入的包含get set函数的对象
        getter = getterOrOptions.get
        setter = getterOrOptions.set
    }
    // ref 引用类型 
    return new ComputedRefImpl(getter, setter)
}

对外暴露的是ComputedRefImpl实例,那么我们初始化还需要在创建一个ComputedRefImpl的类。
ComputedRefImpl类中应该是一个effect,并且应该可以设置个读取相关的属性。在属性访问器get和set中,它们操作的需要是同一个值,所以我们还需要两个属性访问器操作的同一个值_value,且是私有属性。
ps:类中的属性访问器get和set,底层调用的是Object.defineProperty

class ComputedRefImpl {
    public readonly effect
    private _value// get和set需要使用的同一个值
    constructor(getter, private readonly _setter) {
    }
    get value() {
        return this._value
    }
    set value(newValue) {
        this._setter(newValue)
    }
}

computed 获取值的实现

值的展示

在我们完成初始化之后,页面上其实是不会存在我们期望的内容的。它其实是这样的一个页面:

必然。我们还没有将用户传入的逻辑进行处理,所以根本看不到呀!

那我们现在要做的就是先将数据展示到页面上。那我们就先将其effect出来,在读取值的时候执行effect。

class ComputedRefImpl {
    public readonly effect
    private _value// get和set需要使用的同一个值
    constructor(getter, private readonly _setter) {
        this.effect = new ReactiveEffect(getter, () => { })
    }
    get value() {
        this._value = this.effect.run() // 执行
        return this._value
    }
    set value(newValue) {
        this._setter(newValue)
    }
}

这样页面的数据就可以正常展示了。

but,我们却忽略了一个问题,在多次读取时,其实并没有computed的缓存功能,只是effect正常获取某个值,获取一次执行一次。

缓存功能

那么,我们要做的就是继续完善computed的缓存功能。

既然computed也是响应式的,那么它是一个effect,同时肯定也需要有一个缓存标识,控制它的缓存特性。如果依赖的属性发生变化,那么这个缓存标识会更新,重新执行get,没有就不重新执行。

定义一个_dirty,默认应该取值的时候进行计算。一开始为true,默认为新值,更新。当执行完更新的时候应该将其置称false,为false时就默认不执行更新。

那么除了需要在get读取值的时候进行判断,我们还需要依赖属性发生变化的时候,再去进行判断重新渲染,即在构造函数传入我们的一个调度回调scheduler(我们上篇文章实现的调度函数,在依赖属性发生变化的时候,会执行我们传入的回调函数)。

class ComputedRefImpl {
    public readonly effect
    private _value// get和set需要使用的同一个值
    public _dirty = true // 默认应该取值的时候进行计算
    constructor(getter, private readonly _setter) {
        this.effect = new ReactiveEffect(getter, () => {
            //稍后依赖的属性变化 就会执行此调度函数
            if (!this._dirty) {
                this._dirty = true
            }
        })
    }
    get value() {
        if (this._dirty) {
            // 脏数据
            this._dirty = false
            this._value = this.effect.run() // 执行
        }
        return this._value
    }
    set value(newValue) {
        this._setter(newValue)
    }
}

我们可以看到,此时introduction值未发生变化,就只会执行一次effet。

computed 设置值实现

修改值,我们可以通过上述用法一来传入一个带有set和get函数的对象,在设置值的时候通过解构的方式进行赋值。

在 computed 中的set时,我们可以拿到computed的新值,但并没有重新渲染更新页面。此时我们需要做的就是将computed相关的属性进行依赖的收集,并在发生变化的时候进行相应的依赖触发。与effect类似。

既然类似effect,那我们就需要一个存放依赖的dep(在类中定义public dep = undefined),get时收集,属性变化时触发。

// ComputedRefImpl 类
get value() {
    trackEffects(this.dep || (this.dep = new Set())) // 收集
    if (this._dirty) {
        // 脏数据
        this._dirty = false
        this._value = this.effect.run() // 执行
    }
    return this._value
}
// ComputedRefImpl 类
constructor(getter, private readonly _setter) {
    this.effect = new ReactiveEffect(getter, () => {
        //稍后依赖的属性变化 就会执行此调度函数
        if (!this._dirty) {
            this._dirty = true
            // 触发更新
            triggerEffects(this.dep)
        }
    })
}

其中收集trackEffects和触发triggerEffects就是将effect中的tracktrigger中部分共用功能提取出来了。

比如收集的这个功能trackEffects。与effect类似,我们都需要将记录相应的依赖属性和依赖属性的effect,那我们就可以将其独立成一个新的功能函数,降低耦合度。

export function track(target, type, key) {
    // 收集effect中 属性对应的effect
    if (!activeEffect) return
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) { depsMap.set(key, (dep = new Set())) }
    // 判断去重 set中是否存在activeEffect
    trackEffects(dep)
}
// 将set存起来
export function trackEffects(dep) {
    if (!activeEffect) return
    let shouldTrack = !dep.has(activeEffect) // 不存在
    if (shouldTrack) {
        dep.add(activeEffect) // 属性记录effect 
        // 反向记录 effect记录哪些属性收集过
        activeEffect.deps.push(dep) // 让activeEffect 记录住对应的dep 稍后清理会用到
    }
}

triggerEffects也是如此:

export function trigger(target, type, key, value, oldValue) {
    // 判断targetMap是否存在target
    // 不存在 直接返回 不需要收集
    // 存在 取depsMap中对应key的effect 执行run
    const depsMap = targetMap.get(target)
    if (!depsMap) return
    let effects = depsMap.get(key)
    if (effects) {
        triggerEffects(effects)
    }
}
export function triggerEffects(effects) {
    effects = [...effects] // effects 中 set结构删除再添加会导致死循环
    effects.forEach(effect => {
        // 在执行effect时,又要执行自己,需要屏蔽自己的effect
        if (effect !== activeEffect) {
            if (effect.scheduler)
                effect.scheduler() // 如果存在自己的调度函数就执行自己的scheduler
            else effect.run() // 否则就执行run
        }
    });
}

上述只是vue3中computed的简版实现方式,源码中比我们实现的考虑的要多很多,有兴趣的可以自己去看看(源码路径:packages/reactivity/src/computed.ts):

最后,我们也可以看到,页面上过了1秒之后,相应的属性变化,页面也同样发生了变化???。

其实,computed的核心就是effect + 缓存 + 依赖收集、触发

以上就是Vue3 computed初始化获取设置值实现示例的详细内容,更多关于Vue3 computed值设置获取的资料请关注编程网其它相关文章!

--结束END--

本文标题: Vue3computed初始化获取设置值实现示例

本文链接: https://lsjlt.com/news/169715.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Vue3computed初始化获取设置值实现示例
    目录computed 用法computed 实现computed 初始化computed 获取值的实现值的展示缓存功能computed 设置值实现computed 用法 本文给大家带...
    99+
    2022-11-13
    Vue3 computed值设置获取 Vue3 computed初始化
  • Vue实例初始化的示例分析
    这篇文章将为大家详细讲解有关Vue实例初始化的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。以下正式开始:Vue官网的生命周期图示表重点说一下 new Vue()...
    99+
    2024-04-02
  • java实体类如何设置初始值
    在Java实体类中,可以使用构造方法或者默认值初始化属性的初始值。1. 使用构造方法初始化属性的初始值:可以在实体类中定义包含所有属...
    99+
    2023-09-11
    java
  • go map初始化赋值怎么实现
    在Go语言中,可以使用字面量的方式对map进行初始化赋值。以下是几种常见的map初始化赋值方法: 使用make函数创建一个空的m...
    99+
    2023-10-23
    go
  • 阿里云服务器实例初始化怎么设置
    确定服务器实例类型:在购买阿里云服务器时,需要确定服务器的类型,例如单节点服务器、多节点服务器、负载均衡器等。不同类型的服务器在初始化时需要考虑的因素也不同。 选择硬件配置:在确定服务器类型后,需要选择合适的硬件配置。阿里云服务器通常需要...
    99+
    2023-10-28
    阿里 初始化 实例
  • 阿里云服务器初始化实例设置方法
    1. 简介 阿里云服务器是一种基于云计算技术的虚拟服务器,用户可以通过阿里云控制台创建、管理自己的服务器实例。在创建服务器实例之前,需要进行一些初始化设置,以确保服务器能够正常运行。2. 初始化实例设置方法2.1 创建实例在阿里云控制台中,...
    99+
    2024-01-14
    阿里 初始化 实例
  • Python如何实现MySQL实例初始化详解
    前言 相信每位程序员对mysql应该都不陌生,MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。我们在日常开发中少不了要接触mysql。 腾讯云上的m...
    99+
    2022-06-04
    初始化 如何实现 详解
  • Redis连接池配置及初始化实现
    加入db选择后的redis连接池配置代码 public class RedisPoolConfigure { //Redis服务器IP private String ADD...
    99+
    2024-04-02
  • percona 5.7.11中root初始密码设置的示例分析
    这篇文章主要介绍percona 5.7.11中root初始密码设置的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! 第一次安装完percona5....
    99+
    2024-04-02
  • Vue实例初始化为渲染函数设置检查源码剖析
    目录引言_renderProxy是干什么的initProxy方法总结引言 之前的文章提到,Vue实例化时_init方法做了很多处理,其中就有这么一段: if (__DEV__) { ...
    99+
    2024-04-02
  • Java数组实现动态初始化的实例详解
    概念 1、数组动态初始化只给定数组长度,系统默认初始化值。 2、格式 数据类型[] 数组名 = new 数据类型[数组长度]; int[] arr = new int[3];...
    99+
    2024-04-02
  • 如何在Gluon中实现模型的初始化和参数设置
    在Gluon中,可以通过initialize()方法来对模型进行初始化,并通过collect_params()方法来获取模型的所有参...
    99+
    2024-04-02
  • C#怎么实现计算器页面布局和数值初始化
    本篇内容介绍了“C#怎么实现计算器页面布局和数值初始化”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!实现页面布局和数值初始化using&nb...
    99+
    2023-06-29
  • Android 获取IP和UA实现示例详解
    目录获取IP判断网络连接类型获取手机卡联网 IP获取WIFI联网 IP获取UA示例获取IP 最近接入了一个新的SDK,初始化接口需要传入当前设备的IP和UA作为参数。本文介绍如何获...
    99+
    2023-03-19
    Android 获取IP和UA Android IP UA
  • layui如何实现表格初始化时加载提示信息
    这篇文章将为大家详细讲解有关layui如何实现表格初始化时加载提示信息,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。layui的表格在进行初始化时可以添加一个友好提示,增...
    99+
    2024-04-02
  • 如何实现Visual Basic 10中的集合与数组初始值设定
    这篇文章给大家介绍如何实现Visual Basic 10中的集合与数组初始值设定,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。伴随.NET 4.0和Visual Studio 2010的发布,Visual Basic ...
    99+
    2023-06-17
  • echarts动态获取Django数据的实现示例
    目录一、后端二、前端三、页面效果四、总结在开发过程中我们需要将我们的数据通过图标的形式展现出来,接下来我为大家介绍一个有趣的框架:Echarts。这是一个使用JavaScript实现...
    99+
    2024-04-02
  • C#实现获取机器码的示例详解
    目录实践过程效果代码实践过程 效果 代码 public partial class Form1 : Form { public Form1() { ...
    99+
    2022-12-30
    C#获取机器码 C# 机器码
  • PerformanceObserver自动获取首屏时间实现示例
    目录介绍构造函数提供的方法重点我们看看observer.observe(options);实例实际使用 介绍 PerformanceObserver 可用于获取性能相关的数据,例如首...
    99+
    2024-04-02
  • wasm+js实现文件获取md5示例详解
    目录引言本文重点准备工作测试代码纯js测试代码wasm(go)源码js+wasm测试代码测试条件测试目标chrome (版本:103.0.5060.114)firefox (版本号:...
    99+
    2022-11-13
    wasm js获取md5 wasm js
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作