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

发布时间:2023-3-07 10:50

今天我们来讲 readonly以及重构一下reactive。这篇文章主要介绍了SpringBoot之@Value获取application.properties配置无效的解决方案,具有很好的参考价值,希望对大家有所帮助。

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的习惯,搞的现在回来来写文章有点无从下手。

Vue3全局实例上挂载属性方法案例讲解 网站建设

Vue3全局实例上挂载属性方法案例讲解

在大多数开发需求中,我们有时需要将某个数据,或者某个函数方法,挂载到,全局实例身上,以便于,在项目全局的任何位置都能够调用其方法,或读取其数据。 在Vue2 中,我们是在 main.js 中 直...
Vue3异步组件suspense使用详解 网站建设

Vue3异步组件suspense使用详解

vue在解析我们的组件时, 是通过打包成一个 js 文件,当我们的一个组件 引入过多子组件是,页面的首屏加载时间 由最后一个组件决定 优化的一种方式就是采用异步组件 ,先给慢的组件一个提示语或者 骨架...