vue3响应式实现readonly从零开始教程

目录
  • 前言
  • readonly的实现
  • 重构
  • 结束

前言

前面的章节我们把 effect 部分大致讲完了,这部分我们来讲 readonly以及重构一下reactive

readonly的实现

it("happy path", () => {
    console.warn = vi.fn();
    const original = {
      foo: 1,
    };
    const observed = readonly({
      foo: 1,
    });
    expect(original).not.toBe(observed);
    expect(observed.foo).toBe(1);
    //  set不起作用
    observed.foo = 2;
    expect(observed.foo).toBe(1);
    //  当被set的时候,发出一个警告
    expect(console.warn).toBeCalled();
  });

其实与我们之前实现 reactive 十分的类似,区别只不过是set 的时候不要触发trigger,而是警告。当然既然是不会被改变的,track 也是不必要的。

export function readonly(raw) {
    return new Proxy(raw, {
        get(target, key) {
            const res = Reflect.get(target, key);
            return res;
        },
        set(target, key, newValue, receiver) {
            console.warn(
              `property: ${String(key)} can't be set, beacase ${target} is readonly.`
            );
            return true;
      },
    });
}
export function reactive(raw) {
    return new Proxy(raw, {
        get(target, key) {
            const res = Reflect.get(target, key);
            // 依赖收集
            track(target, key);
            return res;
        },
        set(target, key, value) {
            const res = Reflect.set(target, key, value);
            // 触发依赖
            trigger(target, key);
            return res;
        },
    });
}

重构

可以看到,readonlyreactive 实现其实很类似,那我们可以重构一下,增强后续的拓展性。

至于我说的类似,指的是 new Proxy(target, handlers) 中的handlers(处理器对象)中的一些traps(捕获器)。即get, set 这些方法。

我们可以通过工厂函数来创建那些traps函数,来简化我们的代码,提高可维护性。

另外,我们假定traps可以有工厂可以生产了,即handlers这部分相当于被定下来了,new Proxy 这部分也理应可以通过工厂函数创造出来。

我们先抽出一个公共的文件 baseHandler.ts

//  baseHanlder.ts
import { track, trigger } from "./effect";
//  get的工厂函数
function createGetter(isReadonly = false) {
  return function get(target, key) {
    const res = Reflect.get(target, key);
    if (!isReadonly) {
      track(target, key);
    }
    return res;
  };
}
function createSetter() {
  return function set(target, key, newValue, receiver) {
    const res = Reflect.set(target, key, newValue, receiver);
    trigger(target, key, type, newValue);
    return res;
  };
}
export const mutableHandler = {
  get: createGetter(),
  set: createSetter(),
};
export const readonlyHandler = {
  get: createGetter(),
  set(target, key, newValue, receiver) {
    console.warn(
      `property: ${String(key)} can't be set, beacase ${target} is readonly.`
    );
    return true;
};

然后是我们的reactive.ts

//  reactive.ts
import {
  mutableHandler,
  readonlyHandler,
} from "./baseHandlers";
//  proxy的工厂函数
function createReactiveObject(
  target,
  baseHandlers: ProxyHandler<any>
) {
  return new Proxy(target, baseHandlers);
}
export function reactive(target) {
  return createReactiveObject(target, mutableHandler);
}
export function readonly(target) {
  return createReactiveObject(target, readonlyHandler);
}

结束

本篇幅比较短小,但是算是为后续打下了一些基础吧。因为笔者发现边学边写总结文章速度很慢,然后写完 effect 那几篇之后就开始直接一路往下学,中途虽然有提交git,但是没有打tag的习惯,搞的现在回来来写文章有点无从下手。

不过最近应该快把diff那部分搞定了,到时候应该会加快速度更新,更多关于vue3响应式readonly的资料请关注我们其它相关文章!

(0)

相关推荐

  • vue3 reactive响应式依赖收集派发更新原理解析

    目录 proxy 依赖收集 currentEffect 派发更新 总结 proxy vue3的响应式实现依旧是依赖收集与派发更新,本节乃至后面涉及的代码都是经过简化,文章目的是讲解原理,直接贴源码会很枯燥 vue3已经从Object.property更换成Proxy,Proxy相比于前者可以直接监听对象数组,对于深层次的对象和数组,会把触发对应getter,然后去递归进行依赖收集,并不是直接像vue2暴力那样递归,总体而言性能更好 对reactive传进来的对象进行Proxy进行劫持在内部进行依

  • Vue3 源码分析reactive readonly实例

    目录 引言 一.reactive 和 readonly 1. reactive相关类型 2. 相关全局变量与方法 3. reactive函数 4. 造物主createReactiveObject 5. shallowReactive.readonly和shallowReadonly 二.对应的 Handlers 1. baseHandlers 1.1 reactive 1.2 readonly 1.3 shallowReactive 1.4 shallowReadonly 2. cellecti

  • vue3中defineProps传值使用ref响应式失效详解

    子组件接收父组件的传参. 父组件: <template> <Son :data="data"/> </template> <script setup> import { ref } from "vue"; let data = ref('hello') setTimeout(() => { data.value = 'how are you doing' }, 2000) </script> 子组件:

  • vue3 响应式对象如何实现方法的不同点

    目录 vue3响应式对象实现方法的不同点 Vue2和Vue3响应式原理对比 响应式原理实现逻辑 Vue2响应式原理简化 Vue2响应式原理弊端 Vue3响应式原理简化 vue3响应式对象实现方法的不同点 vue 响应式对象是利用 defindeProperty 实现,vue3 则是使用 Proxy 来实现响应式的. 二者,虽然都实现了 响应式的功能,但实现方式不一样,解决问题也有些不同. vue2 中,如果想要实现,数组元素的更新,则是需要 用到检测更新的方法 set 之类的,但 vue3的响应

  • vue3响应式Object代理对象的读取示例详解

    目录 正文 读取属性 xx in obj for ... in 正文 从这一章开始,作者将更新深入的讲解响应式,尤其是vue3响应式的具体的实现.其实在前面一章,如果你仔细阅读,你是可以实现一个简单的响应式函数的,类似于@vue/reactive,当然那只是个demo,是个玩具,我能不能在生产环境上去使用的,它差了太多功能和边界条件. 现在,我们才是真正的深入@vue/reactive. 在vue中,obj.a是一个读取操作,但是仔细想来,读取这个操作很宽泛. obj.a // 访问一个属性 '

  • vue3响应式Proxy与Reflect的理解及基本使用实例详解

    目录 正文 理解Proxy与Reflect Proxy Reflect 实践示例 正文 在第四章中,作者讲述了Vue.js中响应式系统的设计与实现,这一块其实是整个框架的基石,也是MVVM中,ViewModel(VM)的重要组成部分. 其实在上一章中我已经感觉很难了,有一些操作作者也只是几笔带过,却很值得我们思考.这一张中,我们将目光着重于响应式数据本身,来完善上一章中我们的demo. 理解Proxy与Reflect vue3的响应式离不开Proxy,说到Proxy则离不开Reflect.这两个

  • vue3响应式实现readonly从零开始教程

    目录 前言 readonly的实现 重构 结束 前言 前面的章节我们把 effect 部分大致讲完了,这部分我们来讲 readonly以及重构一下reactive. readonly的实现 it("happy path", () => { console.warn = vi.fn(); const original = { foo: 1, }; const observed = readonly({ foo: 1, }); expect(original).not.toBe(ob

  • Vue3响应式方案及ref reactive的区别详解

    目录 一.前言 二.新的方案 1. 缘由 2. Proxy 和 Reflect 1) Proxy 2) Reflect 3. reactive 1) createReactiveObject() 函数 2) mutableHandlers() 函数 -> 对象类型的 handlers 3) mutableInstrumentations() 函数 -> Map Set等类型的 handlers 4. ref 1) createRef() 2) toReactive() 3)proxyRefs(

  • Vue3 响应式侦听与计算的实现

    响应式侦听和计算 有时我们需要依赖于其他状态的状态--在 Vue 中,这是用组件 计算属性 处理的,以直接创建计算值,我们可以使用 computed 方法:它接受 getter 函数并为 getter 返回的值返回一个不可变的响应式 ref 对象. 我们先来看看一个简单的例子,关于计算值的方式,同样我们在 src/TemplateM.vue 写下如下代码: <template> <div class="template-m-wrap"> count --->

  • setup+ref+reactive实现vue3响应式功能

    setup 是用来写组合式 api ,内部的数据和方法需要通过 return 之后,模板才能使用.在之前 vue2 中,data 返回的数据,可以直接进行双向绑定使用,如果我们把 setup 中数据类型直接双向绑定,发现变量并不能实时响应.接下来就看看setup如何实现data的响应式功能? 一.ref setup 内的自定义属性不具备响应式能力,所以引入了 ref ,ref 底层通过代理,把属性包装值包装成一个 proxy ,proxy 内部是一个对象,使得基础类型的数据具备响应式能力,使用之

  • 一文详解Vue3响应式原理

    目录 回顾 vue2.x 的响应式 vue3的响应式 Reflect 回顾 vue2.x 的响应式 实现原理: 对象类型:通过object.defineProperty()对属性的读取.修改进行拦截(数据劫持) 数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹) Object.defineProperty(data,'count ",{ get(){}, set(){} }) 存在问题: 新增属性.删除属性,界面不会更新 直接通过下标修改数组,界面不会自动更新 但是

  • 浅析vue3响应式数据与watch属性

    是Vue3的 composition API中2个最重要的响应式API ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式) 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象 ref内部: 通过给value属性添加getter/setter来实现对数据的劫持 reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据 ref的数据操作: 在js中要.value, 在模板中不需要(内部解析

  • 手写 Vue3 响应式系统(核心就一个数据结构)

    目录 前言 响应式 总结 前言 响应式是 Vue 的特色,如果你简历里写了 Vue 项目,那基本都会问响应式实现原理.而且不只是 Vue,状态管理库 Mobx 也是基于响应式实现的.那响应式是具体怎么实现的呢?与其空谈原理,不如让我们来手写一个简易版吧. 响应式 首先,什么是响应式呢? 响应式就是被观察的数据变化的时候做一系列联动处理.就像一个社会热点事件,当它有消息更新的时候,各方媒体都会跟进做相关报道.这里社会热点事件就是被观察的目标.那在前端框架里,这个被观察的目标是什么呢?很明显,是状态

  • Vue3 响应式系统实现 computed

    目录 前言 实现 computed 总结 前言 上篇文章我们实现了基本的响应式系统,这篇文章继续实现 computed. 首先,我们简单回顾一下: 响应式系统的核心就是一个 WeakMap --- Map --- Set 的数据结构. WeakMap 的 key 是原对象,value 是响应式的 Map.这样当对象销毁的时候,对应的 Map 也会销毁. Map 的 key 就是对象的每个属性,value 是依赖这个对象属性的 effect 函数的集合 Set.然后用 Proxy 代理对象的 ge

  • 详解vue3 响应式的实现原理

    目录 核心设计思想 Vue.js 2.x 响应式 Vue.js 3.x 响应式 依赖收集:get 函数 派发通知:set 函数 总结 源码参考 核心设计思想 除了组件化,Vue.js 另一个核心设计思想就是响应式.它的本质是当数据变化后会自动执行某个函数,映射到组件的实现就是,当数据变化后,会自动触发组件的重新渲染.响应式是 Vue.js 组件化更新渲染的一个核心机制. Vue.js 2.x 响应式 我们先来回顾一下 Vue.js 2.x 响应式实现的部分: 它在内部通过 Object.defi

随机推荐