前言
前面的章节我们把 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;
- },
- });
- }
重构
可以看到,readonly
和 reactive
实现其实很类似,那我们可以重构一下,增强后续的拓展性。
至于我说的类似,指的是 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的资料请关注w3xue其它相关文章!