经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
vue3响应式实现readonly从零开始教程
来源:jb51  时间:2023/3/8 10:58:57  对本文有异议

前言

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

readonly的实现

  1. it("happy path", () => {
  2. console.warn = vi.fn();
  3. const original = {
  4. foo: 1,
  5. };
  6. const observed = readonly({
  7. foo: 1,
  8. });
  9. expect(original).not.toBe(observed);
  10. expect(observed.foo).toBe(1);
  11. // set不起作用
  12. observed.foo = 2;
  13. expect(observed.foo).toBe(1);
  14. // 当被set的时候,发出一个警告
  15. expect(console.warn).toBeCalled();
  16. });

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

  1. export function readonly(raw) {
  2. return new Proxy(raw, {
  3. get(target, key) {
  4. const res = Reflect.get(target, key);
  5. return res;
  6. },
  7. set(target, key, newValue, receiver) {
  8. console.warn(
  9. `property: ${String(key)} can't be set, beacase ${target} is readonly.`
  10. );
  11. return true;
  12. },
  13. });
  14. }
  15. export function reactive(raw) {
  16. return new Proxy(raw, {
  17. get(target, key) {
  18. const res = Reflect.get(target, key);
  19. // 依赖收集
  20. track(target, key);
  21. return res;
  22. },
  23. set(target, key, value) {
  24. const res = Reflect.set(target, key, value);
  25. // 触发依赖
  26. trigger(target, key);
  27. return res;
  28. },
  29. });
  30. }

重构

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

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

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

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

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

  1. // baseHanlder.ts
  2. import { track, trigger } from "./effect";
  3. // get的工厂函数
  4. function createGetter(isReadonly = false) {
  5. return function get(target, key) {
  6. const res = Reflect.get(target, key);
  7. if (!isReadonly) {
  8. track(target, key);
  9. }
  10. return res;
  11. };
  12. }
  13. function createSetter() {
  14. return function set(target, key, newValue, receiver) {
  15. const res = Reflect.set(target, key, newValue, receiver);
  16. trigger(target, key, type, newValue);
  17. return res;
  18. };
  19. }
  20. export const mutableHandler = {
  21. get: createGetter(),
  22. set: createSetter(),
  23. };
  24. export const readonlyHandler = {
  25. get: createGetter(),
  26. set(target, key, newValue, receiver) {
  27. console.warn(
  28. `property: ${String(key)} can't be set, beacase ${target} is readonly.`
  29. );
  30. return true;
  31. };

然后是我们的reactive.ts

  1. // reactive.ts
  2. import {
  3. mutableHandler,
  4. readonlyHandler,
  5. } from "./baseHandlers";
  6. // proxy的工厂函数
  7. function createReactiveObject(
  8. target,
  9. baseHandlers: ProxyHandler<any>
  10. ) {
  11. return new Proxy(target, baseHandlers);
  12. }
  13. export function reactive(target) {
  14. return createReactiveObject(target, mutableHandler);
  15. }
  16. export function readonly(target) {
  17. return createReactiveObject(target, readonlyHandler);
  18. }

结束

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

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号