前端Vue架构

news2024/11/25 15:47:22

1

理解:

  • 创建视图的函数(render)和数据之间的关联;

  • 当数据发生变化的时候,希望render重新执行;

  • 监听数据的读取和修改;

    • defineProperty:监听范围比较窄,只能通过属性描述符去监听已有属性的读取和赋值;兼容性更好;(要求监听数据是对象)
    • proxy:监听范围更广;兼容性较差,只能兼容支持ES6 的浏览器(要求监听数据是对象)

如何知晓数据对应的函数;

function track(target, key) {
  console.log(`依赖收集:${key}`, 'color: #f00');
}

function trigger(target, key) {
  console.log(`派发更新:${key}`, 'color: #f00');
}

function isObject(value) {
  return typeof value === 'object' && value !== null;
}
const handlers = {
  get(target, key) {
    // 依赖收集
    track(target, key);
    return target[key]; // 返回对象的相应属性值
  },

  set(target, key, value) {
    // 派发更新
    trigger(target, key);

    // target[key] = value; // 设置对象的相应属性值
    // return true;
    // 赋值成功返回true,赋值失败返回false;这里可以使用try catch
    return Reflect.set(target, key, value)
    // 也可以使用Reflect.set(target, key, value)。它会直接返回true或者false
  },
}
// 同一个对象,调用两次reactive,会生成不一样的Proxy对象,没有意义
const targetMap = new WeakMap();
function reactive(target) {
  if (!isObject(target)) {
    return target; // 如果不是对象,直接返回
  }
  if (targetMap.has(target)) {
    return targetMap.get(target);// 如果已经代理过了,直接返回;
  }

  const proxy = new Proxy(target, handlers);

  targetMap.set(target, proxy);
  return proxy;
}
const state = reactive({
  a: 1,
  b: 2,
});

// fn函数中用到了state数据
function fn() {
  state.a;
  state.b;
}

fn();
state.a++; // 先读取,再赋值

依赖收集:a color: #f00
依赖收集:b color: #f00
依赖收集:a color: #f00
派发更新:a color: #f00

const obj = {
  a: 1,
  b: 2,
  get c() {
    return this.a + this.b;
  }
};

const state = reactive(obj);
state.c;

这样写的话,依赖收集只能收集到属性c;因为this指向obj;
可以这样操作:

get(target, key, receiver) {
  // 依赖收集
  track(target, key);
  // receiver指的是代理对象
  return Reflect.get(target, key, receiver) ; 
  // 改变this指向,将this指向为代理对象
  // return target[key]; // 返回对象的相应属性值
},

下面的用法,只能收集到c,收集不到c1

const obj = {
  a: 1,
  b: 2,
  c: {
    c1: 1,
  },
};

const state = reactive(obj);
state.c.c1;

可以这样操作
如果访问的属性值还是一个对象,对属性值再次进行代理;

const obj = {
includes: () => {},
indexOf: () => {},
};
const handlers = {
  get(target, key, receiver) {
    // 依赖收集
    track(target, key);
    // 对于数组来说,无法在代理对象中找到时,去原始数组中重新找一次
    // const obj = {};
    // const arr = [1, obj, 3];
    // const state = reactive(arr);
    // state.includes(obj);
    if ((obj.hasOwnProperty(key) && Array.isArray(target)) {
    	return obj[key];
    }
    // receiver指的是代理对象
    const result = Reflect.get(target, key, receiver) ; 
    if (isObject(result)) {
      return reactive(result);
    }
  },
}

简易的模型已经写好;

2 读信息 进行依赖升级

Object.keys和let i in obj用的都是ownKeys;
‘a’ in obj; 用的是has;
obj.a 用的是get;

这里的读不光是通过state.a来读取a属性
还可能通过’e’ in state;来查看’e’属性在不在state中;

const obj = {};
const state = reactive(obj);
'e' in state;

解决办法:新增has方法

const TrackOpTypes = {
  GET: 'get', // 读取属性值
  HAS: 'has', // 判断属性是否存在
  ITERATE: 'iterate', // 迭代对象
};

function track(target, trackOpType, key) {
  console.log(`依赖收集:${key},收集方法:${trackOpType}`, 'color: #f00');
}
const handlers = {
  get(target, key, receiver) {
    // 依赖收集
    track(target, TrackOpTypes.GET, key);
	// ...
  },
  set(target, key, value, receiver) {},
  has(target, key) {
    track(target, TrackOpTypes.HAS, key);
    return Reflect.has(target, key); // 判断对象是否有key属性
  }
}

在这里插入图片描述

同理

const TriggerOpTypes = {
  SET: 'set', // 设置属性值
  ADD: 'add', // 添加属性值
  DELETE: 'delete', // 删除属性
}

function trigger(target, triggerOpType, key) {
  console.log(`派发更新:${key}, 更改方法:${triggerOpType}`, 'color: #f00');
}

还有一种情况

const handlers = {
  get,
  set,
  has,
  ownKeys(target) {
    track(target, TrackOpTypes.ITERATE);
    return Reflect.ownKeys(target); // 返回对象的所有属性名
  },
}
function track(target, trackOpType, key) {
  if (trackOpType === TrackOpTypes.ITERATE) {
    console.log(`依赖收集方法:${trackOpType}`, 'color: #f00');
    return;
  }
  console.log(`依赖收集:${key},收集方法:${trackOpType}`, 'color: #f00');
}
const obj = { a: '1'};
const obj2 = {}
const state = reactive(obj);
const state2 = reactive(obj2);
for (let i in state) {}
Object.keys(state2);

Object.keys和let i in obj用的都是ownKeys;

依赖收集方法:iterate color: #f00
依赖收集方法:iterate color: #f00

3 新增属性

 set(target, key, value, receiver) {
    // 派发更新
    const type = target.hasOwnProperty(key)
    ? TriggerOpTypes.SET
    : TriggerOpTypes.ADD;
    trigger(target, type, key);
    return Reflect.set(target, key, value, receiver);
  },

在这里插入图片描述

4 删除属性

 deleteProperty(target, key) {
    trigger(target, TriggerOpTypes.DELETE, key);
    return Reflect.deleteProperty(target, key); // 删除对象的相应属性
  }
delete state.a;

5 数据和函数的内在联系

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1669562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM的计算机课程实验管理系统的设计与实现(源码)

| 博主介绍:✌程序员徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅&#x1f44…

架构每日一学 5:拼多多如何通过洞察人性脱颖而出?

本文首发于公众平台:腐烂的橘子 上一篇文章,我们讲到架构活动一定要顺应人性,今天我们就来聊一聊,拼多多如何通过洞察人性在电商行业脱颖而出。 拼多多从诞生到现在,可以说是颠覆了整个互联网的认知。 2015 年&#…

JSON 转为json串后出现 “$ref“

问题描述 转为JSON 串时出现 "$ref":"$.RequestParam.list[0]" $ref: fastjson数据重复的部分会用引用代替,当一个对象包含另一个对象时,fastjson就会把该对象解析成引用 “$ref”:”..” 上一级 “$ref”:”” 当前对…

SpringBoot自动配置源码解析+自定义Spring Boot Starter

SpringBootApplication Spring Boot应用标注 SpringBootApplication 注解的类说明该类是Spring Boot 的主配置类,需要运行该类的main方法进行启动 Spring Boot 应用 SpringBootConfiguration 该注解标注表示标注的类是个配置类 EnableAutoConfiguration 直译&#…

日本率先研发成功6G设备,刺痛了谁?为何日本能率先突破?

日本率先研发成功6G设备,无线数据速率是5G的百倍,这让日本方面兴奋莫名,毕竟日本在科技方面从1990年代以来太缺少突破的创新了,那么日本为何如今在6G技术上能率先突破呢? 日本在1980年代末期达到顶峰,它的科…

华为OD机试 - 求幸存数之和(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷C卷)》。 刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试…

python将图片以及标注信息按类别分开

目录 需求: 思路: 原数据集结构: 代码1(效率低,但不用提前知道需要分多少个类别): 代码2(效率相对高点,但类别数量如果超过设定的11个,则需要改下代码&a…

MySQL·索引

目录 索引的意义 索引的理解 为何IO交互要是 Page 理解Page 其他数据结构为何不行? 聚簇索引 VS 非聚簇索引 索引操作 主键索引操作 唯一键索引操作 普通索引的创建 总结 全文索引 索引的意义 索引:提高数据库的性能,索引是物美…

[AIGC] 压缩列表了解吗?快速列表 quicklist 了解吗?

文章目录 压缩列表了解吗?快速列表 quicklist 了解吗? 压缩列表了解吗? 压缩列表是 Redis 为了节约内存 而使用的一种数据结构,是由一系列特殊编码的连续内存快组成的顺序型数据结构。 一个压缩列表可以包含任意多个节点&#xf…

ae如何导出mp4格式?图文教程,手把手教您搞定

在创作精彩的视频内容后,将其成功导出为通用的MP4格式是确保作品在不同平台上流畅播放的重要一环。Adobe After Effects作为一款专业的视频后期制作工具,提供了丰富的功能来实现这一目标。在本文中,我们将通过图文教程,手把手地向…

人生是旷野,不是轨道

最近看到一句话,很喜欢,分享一下。"人生是旷野,不是轨道"。人生不是固定的方程式,也没有唯一答案,没有谁生来就应该是什么样。别太被太多世俗观念束缚住手脚,每个人都有权利自由生长,…

【Ubuntu】apt命令安装最新版本Nginx

目录 环境前言添加Nginx仓库步骤1、仓库公钥2、文本公钥转二进制GPG公钥(可选)3、添加apt软件源4、安装新版Nginx 参阅 环境 Ubuntu 22.04 前言 ubuntu官方apt软件仓库(或者叫软件源)的软件版本可能会比较旧,导致无…

Unity 性能优化之GPU Instancing(五)

提示:仅供参考,有误之处,麻烦大佬指出,不胜感激! 文章目录 前言一、GPU Instancing使用方法二、使用GPU Instancing的条件三、GPU Instancing弊端四、注意五、检查是否成功总结 前言 GPU Instancing也是一种Draw call…

xCode升级后: Library ‘iconv2.4.0’ not found

报错信息: targets 选中 xxxNotification: Build Phases ——> Link Binary With Libraries 中,移除 libiconv.2.4.0.tbd libiconv.2.4.0.dylib 这两个库(只有一个的移除一个就好)。 然后重新添加 libiconv.tbd 修改完…

如何将Git仓库中的文件打包成zip文件?

要将Git仓库中的文件打包成zip文件,您可以使用git archive命令。这个命令允许您将任何git可访问的树或提交导出成一个归档文件。以下是一些基本的步骤: 打开命令行或终端。切换到您的Git仓库的目录。执行git archive命令。 git archive --formatzip --o…

【ARMv8/v9 系统寄存器 5 -- CPU ID 判断寄存器 MPIDR_EL1 使用详细介绍】

文章目录 寄存器名称: MPIDR_EL1寄存器结构:主要功能和用途亲和级别(Affinity Levels)简介CORE ID 获取函数 在ARMv8-A架构中, MPIDR_EL1寄存器是一个非常重要的系统寄存器,它提供了关于处理器在其物理和逻辑配置中的位置的信息。…

力扣:48. 旋转图像(Java)

目录 题目描述:输入:输出:代码实现: 题目描述: 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使…

STM32快速入门(定时器之输出PWM波形)

STM32快速入门(定时器之输出PWM波形) 前言 本节主要讲解STM32利用通用定时器,利用CCR和CNT寄存器,输出指定占空比和频率的PWM波形。其功能的应用有:实现LED呼吸灯的效果、控制步进电机、控制直流电机转速等。 导航 …

GUI Pro - Survival Clean

通过开发生存游戏的经验,我们制作了一个带有科幻概念的GUI包。我们希望这个包对你的项目有所帮助。 主要特征 - 2560x1440分辨率图形 - GUI皮肤,包含布局演示场景和预制件 - 提供各种象形图标和项目图标 - 切片元素和白色元素,可定制尺寸和颜色 - 不包括编码和动画 资产 - 1…

栈和队列OJ练习题及解答

前言 上一篇博客已经讲到了栈和队列的数据结构,概括一下:栈后进先出(Last In First Out)、队列先进先出(First In First Out)。那么,接下来就来讲讲,关于栈和队列的相关练习题&#…