一起学Vue3源码,实现最简Vue3【07】 - 实现 isReactive 和 isReadonly

2023-11-14

实现 isReactive 和 isReadonly

什么是isReactive,isReadonly?
即判断一个对象是否是reactive或者readonly

reactive.spec.ts

import { isReactive, reactive } from "../src/reactive";

describe("reactive", () => {
  it("happy path", () => {
    const original = { foo: 1 };
    const observed = reactive(original);
    expect(observed).not.toBe(original);

    expect(isReactive(observed)).toBe(true);
    expect(isReactive(original)).toBe(false);

    expect(observed.foo).toBe(1);
  });
  ...
});

readonly.spec.ts

import { isReadonly, readonly } from "../src/reactive";

describe("readonly", () => {
  it("happy path", () => {
    // set not be triggered
    const original = { prop: 1, bar: { age: 10 } };
    const wrapped = readonly(original);
    expect(wrapped).not.toBe(original);

    expect(isReadonly(wrapped)).toBe(true);
    expect(isReadonly(original)).toBe(false);

    expect(wrapped.prop).toBe(1);
  });
  ...
});

reactive.ts

// 创建一个枚举标识
export const enum ReactiveFlags {
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
}

// 1、只要取值就会触发 get,然后在 get 中根据 内置 isReadonly 属性去判断是 Reactive or Readonly
// 2、方法只返回 true or false, 不关心是否是响应式对象,强制转换成 Boolean 类型
export function isReactive(value) {
  return !!value[ReactiveFlags.IS_REACTIVE];
}

export function isReadonly(value) {
  return !!value[ReactiveFlags.IS_READONLY];
}

baseHandlers.ts

import { extend, isObject } from "../../shared";
import { track, trigger } from "./effect";
import { reactive, ReactiveFlags, readonly } from "./reactive";

// 缓存一下get, set 后面始终用同一个
const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(true);

function createGetter(isReadonly: Boolean = false, shallow: Boolean = false) {
  return function get(target, key) {
    // 调用 isReadonly、isReactive触发get,通过key和固有的枚举值比较,继而看是reactive 还是 readonly
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly;
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly;
    }

    const res = Reflect.get(target, key);

    // 判断内部属性是否是 Object,是的话继续转换成 Proxy
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res);
    }

    if (!isReadonly) {
      // 收集依赖
      track(target, key);
    }
    return res;
  };
}

function createSetter() {
  return function set(target, key, value) {
    const res = Reflect.set(target, key, value);

    // 触发依赖
    trigger(target, key);
    return res;
  };
}

export const mutableHandlers = {
  get,
  set,
};

export const readonlyHandlers = {
  get: readonlyGet,
  set(target, key, value) {
    // 警告函数
    console.warn(
      `${String(key)} cannot be set, because target is readonly, ${target}`
    );
    return true;
  },
};

测试一下,ok,通过

最后

附上全部文章代码地址:mini-vue,欢迎交流探讨。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一起学Vue3源码,实现最简Vue3【07】 - 实现 isReactive 和 isReadonly 的相关文章

  • 从 HTML 表单发送数据到 Node.js 服务器

    我正在学习 Node js 我的服务器中有这个 var http require http var url require url http createServer function request response response w
  • 如何使用nodeJS SFTP客户端列出所有子目录?

    有趣的节点 JS ssh2 sftp client 我想列出给定路径中的所有目录及其子目录 let sftp new ssh2SftpClient console log sftp sftp connect host xx xxx xxx
  • Jqplot 中两个系列数据的不同颜色条

    我想知道如何在 Jqplot 中为两个系列制作不同的颜色条 如果我只有一个系列数据 它的工作原理如下图所示 红色和绿色基于其值 但是 如果我有两个系列数据 我无法为每个系列数据配置两个系列颜色 目前我只能做这个图 我希望两个系列图可以根据其
  • 全局 JavaScript 变量作用域:为什么这不起作用?

    所以我在玩 JavaScript 时遇到了我认为奇怪的事情 有谁能解释一下以下内容吗 我已将警报值作为评论包含在内 为什么 foo 中的第一个警报 msg 返回不明确的并不是outside var msg outside function
  • 使用 word_number 值对 javascript 数组进行排序

    如何对数组进行排序 var arr new Array word 12 word 59 word 17 这样我得到 word 12 word 17 word 59 Thanks 您需要编写一个排序方法 您可以编写任何您喜欢的方法 该方法在
  • 过滤器返回 true 或 false

    我正在使用过滤器在 data it 返回对象中查找 id 它返回的对象不是 true 或 false 如果我怎样才能返回 true 或 falseval recoredId valueId var hasMatch data filter
  • TypeScript 中类和命名空间的区别

    到底有什么区别classes and namespaces在打字稿中 我知道 如果您创建一个带有静态方法的类 您可以在不实例化该类的情况下访问它们 这正是我猜想的命名空间的要点之一 我还知道你可以创建多个同名的命名空间 并且它们的方法在编译
  • Ember.js 数组作为模型的属性

    干杯 我有一些模型 它的一个属性是一个数组 但由于某些原因 我在服务器上使用 mongoDB 并且它是嵌入式模型和 ember data 的问题 我不能做这样的事情 App Foo DS Model extend numbers DS ha
  • contenteditable 在 safari 中不起作用,但在 chrome 中起作用

    我有一个奇怪的问题 这在 chrome 中按预期工作 但在 safari 中它只会发光 但不会对按键输入做出反应 这是触发文本版本的方法 var namebloc event currentTarget find column filena
  • 如何防止在达到一定字符数后向文本区域输入内容?

    使用下面的代码 任何超过指定最大值的输入都将被删除 但这会产生一种效果 即键入字符后立即将其删除 我宁愿简单地阻止输入字符
  • 表单提交不起作用

    我有一张桌子 可以打印出所有可用的相机 它使用表单来更改这些设置 问题在于该表单仅更新条目中的最后一个摄像机 换句话说 如果我更改表单并为列表中的最后一个摄像机点击 应用 它将起作用 如果我更改此列表中任何其他摄像机的表单 它会将其更改为与
  • JQuery _renderItem 没有被调用

    我正在尝试使用 renderItem 函数创建自定义 ui menu item 元素 但经过可能尝试后 我什至无法调用该函数 自动完成功能正在工作 但就像 renderItem 函数不存在一样 这是我的脚本部分
  • 从 UnityWebGL jslib 返回字符串

    我想使用 jslib 来获取网址参数 像这样的代码 jslib GetUrl function var s var strUrl window location search var getSearch strUrl split var g
  • 如何在Vuejs中动态管理页面标题?

    我构建一个应用程序 我有一个带有页面标题的标题 目前 我使用视图路由器来定义我的标题 path events name events component Events meta title Liste des v nements 在我的刀片
  • jQuery 面板滑块通过单击按钮打开但不会关闭

    我的页面上有一个按钮 可以使用 jquery 和 Modernizr 框架打开右侧面板 按钮位于屏幕最右侧 单击时 它会向左滑动并打开打开的面板 问题是 再次单击时它不会滑回到原来的位置 HTML div class cd panel fr
  • Jest - 语法错误:无法在模块外部使用 import 语句

    我在用jest 24 9 0无需任何配置 从 create react app 全局安装 在这些文件中我使用 es6 模块 使用时没有报错 test react scripts test 但是当我开始使用时jest with test je
  • html输入数字,min + step,使step忽略min?

    是否有可能使step忽略min属性
  • router.navigate 使用查询参数 Angular 5

    我在使用查询参数路由到路由时遇到问题我有一个像这样的函数 goToLink link this router navigate link split 0 queryParams this sortParams link 和这个功能 sort
  • 为什么对于整数键,“Map”操作比 JavaScript (v8) 中的“Object”慢得多?

    我很高兴使用Map对于在我的 JavaScript 代码库中随处访问的索引 但我刚刚偶然发现了这个基准 https stackoverflow com a 54385459 365104 https stackoverflow com a
  • ES6解构对象赋值函数参数默认值

    您好 我正在查看在传递函数参数时使用对象解构的示例对象解构演示 https developer mozilla org en US docs Web JavaScript Reference Operators Destructuring

随机推荐