【vue3响应式原理】

# 源码结构

image-20210313232042704

源码位置是在packages文件件内,实际上源码主要分为两部分,编译器和运行时环境

1. 编译器

  • compiler-core 核心编译逻辑
  • compiler-dom 针对浏览器平台编译逻辑
  • compiler-sfc 针对单文件组件编译逻辑
  • compiler-ssr 针对服务端渲染编译逻辑

2. 运行时环境

  • runtime-core 运行时核心

  • runtime-dom 运行时针对浏览器的逻辑

  • runtime-test 浏览器外完成测试环境仿真

  1. reactivity 响应式逻辑

  2. template-explorer 模板浏览器

  3. vue 代码入口,整合编译器和运行时

  4. server-renderer服务器端渲染

  5. share 公用方法

# Vue 3初探

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"><title>hello vue3</title><script src="../dist/vue.global.js"></script></head><body> <div id='app'><h1>{{message}}</h1></div> <script>Vue.createApp({data: { message: 'Hello Vue 3!' }}).mount('#app') </script></body></html>

# Composition API

Composition API字面意思是组合API,它是为了实现基于函数逻辑复用机制而产生的

# 1. 数据响应式

<div>count: {{ state.count }}</div><script> const { createApp, reactive} = Vue;// 声明组件 const App = {// setup是一个新的组件选项,它是组件内使用Composition API的入口 // 调用时刻是初始化属性确定后,beforeCreate之前setup() {// 响应化:接收一个对象,返回一个响应式的代理对象 const state = reactive({ count: 0 })// 返回对象将和渲染函数上下文合并 return { state } }}createApp(App).mount('#app') </script>

# 2. 计算属性

<div>doubleCount: {{doubleCount}}</div>

# 3. 事件处理

<div @click="add">count: {{ state.count }}</div>

# 4. 侦听器

# 5. 引用对象:单个原始值响应化

<div>counter: {{ counter }}</div>

# 6. 体验逻辑组合

const { createApp, reactive, onMounted, onUnmounted, toRefs } = Vue// 鼠标位置侦听 function useMouse() { // 数据响应化 const state = reactive({ x: 0, y: 0 }) const update = e => { state.x = e.pageX state.y = e.pageY } onMounted(() => { window.addEventListener('mousemove', update) }) onUnmounted(() => { window.removeEventListener('mousemove', update) }) // 转换所有key为响应式数据 return toRefs(state)}// 事件监测function useTime() { const state = reactive({ time: new Date() }) onMounted(() => { setInterval(() => { state.time = new Date() }, 1000) }) return toRefs(state)}// 逻辑组合const MyComp = { template: ` <div>x: {{ x }} y: {{ y }}</div> <p>time: {{time}}</p> `, setup() { // 使用鼠标逻辑 const { x, y } = useMouse() // 使用时间逻辑 const { time } = useTime() // 返回使用 return { x, y, time } } }createApp().mount(MyComp, '#app')

对比mixins,好处显而易见:

  • x,y,time来源清晰
  • 不会与dataprops等命名冲突

# Vue3响应式原理

# Vue2响应式原理回顾

// 1.对象响应化:遍历每个key,定义getter、setter// 2.数组响应化:覆盖数组原型方法,额外增加通知逻辑 const originalProto = Array.prototypeconst arrayProto = Object.create(originalProto);['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].forEach(method => { arrayProto[method] = function() { originalProto[method].apply(this, arguments) notifyUpdate() }} )function observe(obj) { if (typeof obj !== 'object' || obj == null) {return}// 增加数组类型判断,若是数组则覆盖其原型 if (Array.isArray(obj)) { Object.setPrototypeOf(obj, arrayProto) } else { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { const key = keys[i] defineReactive(obj, key, obj[key]) } } }function defineReactive(obj, key, val) { observe(val) // 解决嵌套对象问题 Object.defineProperty(obj, key, { get() { return val }, set(newVal) { if (newVal !== val) { observe(newVal) // 新值是对象的情况 val = newVal notifyUpdate() } }}) }function notifyUpdate() { console.log('页面更新!')}

vue2响应式弊端:

  • 响应化过程需要递归遍历,消耗较大
  • 新加或删除属性无法监听
  • 数组响应化需要额外实现
  • Map、Set、Class等无法响应式
  • 修改语法有限制

# Vue3响应式原理剖析

vue3使用ES6的Proxy特性来解决这些问题。

function reactive(obj) { if (typeof obj !== 'object' && obj != null) { return obj }// Proxy相当于在对象外层加拦截// http://es6.ruanyifeng.com/#docs/proxy const observed = new Proxy(obj, { get(target, key, receiver) { // Reflect用于执行对象默认操作,更规范、更友好 // Proxy和Object的方法Reflect都有对应 // http://es6.ruanyifeng.com/#docs/reflect  const res = Reflect.get(target, key, receiver) console.log(`获取${key}:${res}`) return res }, set(target, key, value, receiver) { const res = Reflect.set(target, key, value, receiver) console.log(`设置${key}:${value}`) return res }, deleteProperty(target, key) { const res = Reflect.deleteProperty(target, key) console.log(`删除${key}:${res}`) return res } }) return observed}

测试代码

const state = reactive({ foo: 'foo', bar: { a: 1 } })// 1.获取state.foo // ok// 2.设置已存在属性state.foo = 'fooooooo' // ok // 3.设置不存在属性state.dong = 'dong' // ok // 4.删除属性delete state.dong // ok

1. 嵌套对象响应式

测试:嵌套对象不能响应

// 4.设置嵌套对象属性react.bar.a = 10 // no ok

添加对象类型递归

2. 避免重复代理

重复代理,比如

reactive(data) // 已代理过的纯对象 reactive(react) // 代理对象

解决方式:将之前代理结果缓存,get时直接使用

const toProxy = new WeakMap() // 形如obj:observed const toRaw = new WeakMap() // 形如observed:objfunction reactive(obj) { //... // 查找缓存,避免重复代理 if (toProxy.has(obj)) { return toProxy.get(obj) }if (toRaw.has(obj)) { return obj} const observed = new Proxy(...) // 缓存代理结果 toProxy.set(obj, observed) toRaw.set(observed, obj) return observed } // 测试效果 console.log(reactive(data) === state) console.log(reactive(state) === state)

3. 依赖收集

建立响应数据key和更新函数之间的对应关系。

设计

  • 实现三个函数: effect:将回调函数保存起来备用,立即执行一次回调函数触发它里面一些响应数据的getter
  • track:getter中调用track,把前面存储的回调函数和当前target,key之间建立映射关系
  • trigger:setter中调用trigger,把target,key对应的响应函数都执行一遍

target,key和响应函数映射关系

实现

设置响应函数,创建effect函数

// 保存当前活动响应函数作为getter和effect之间桥梁 const effectStack = []// effect任务:执行fn并将其入栈 function effect(fn) { const rxEffect = function() { // 1.捕获可能的异常 try { // 2.入栈,用于后续依赖收集  effectStack.push(rxEffect) // 3.运行fn,触发依赖收集  return fn() } finally { // 4.执行结束,出栈  effectStack.pop() } } // 默认执行一次响应函数  rxEffect() // 返回响应函数 return rxEffect}

依赖收集和触发

function reactive(obj) { // ... const observed = new Proxy(obj, { get(target, key, receiver) { // 依赖收集 track(target, key) return isObject(res) ? reactive(res) : res }, set(target, key, value, receiver) { // 触发响应函数 trigger(target, key) return res } })}// 映射关系表,结构大致如下:// {target: {key: [fn1,fn2]}}let targetMap = new WeakMap()function track(target, key) { // 从栈中取出响应函数 const effect = effectStack[effectStack.length - 1] if (effect) { // 获取target对应依赖表 let depsMap = targetMap.get(target) if (!depsMap) { depsMap = new Map() targetMap.set(target, depsMap) } // 获取key对应的响应函数集 let deps = depsMap.get(key) if (!deps) { deps = new Set() depsMap.set(key, deps) } // 将响应函数加入到对应集合 if (!deps.has(effect)) { deps.add(effect) } }}// 触发target.key对应响应函数function trigger(target, key) { // 获取依赖表 const depsMap = targetMap.get(target) if (depsMap) { // 获取响应函数集合 const deps = depsMap.get(key) if (deps) { // 执行所有响应函数 deps.forEach(effect => { effect() }) } }}

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

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

相关文章

使用kafka tools工具连接带有用户名密码的kafka

使用kafka tools工具连接带有用户名密码的kafka 创建kafka连接&#xff0c;配置zookeeper 在Security选择Type类型为SASL Plaintext 在Advanced页面添加如下图红框框住的内容 在JAAS_Config加上如下配置 需要加的配置&#xff1a; org.apache.kafka.common.security.plain.Pla…

企业数字化转型的主要方面有哪些?

本人研究企业数字化转型10余年&#xff0c;为企业软件选型、数字化提供咨询服务&#xff01;目前重点研究低代码数字化转型玩法&#xff0c;力争为各行各业探索出一条最具性价比的数字化方式。 关于“企业数字化转型包括哪些方面”这个问题&#xff0c;咱先来看个例子哈~ 比如…

用负载绿原酸的纳米复合水凝胶调节巨噬细胞表型以加速伤口愈合

引用信息 文 章&#xff1a;Modulating macrophage phenotype for accelerated wound healing with chlorogenic acid-loaded nanocomposite hydrogel. 期 刊&#xff1a;Journal of Controlled Release&#xff08;影响因子&#xff1a;10.8&#xff09; 发表时间&a…

基于pytoch卷积神经网络水质图像分类实战

具体怎么学习pytorch&#xff0c;看b站刘二大人的视频。 完整代码&#xff1a; import numpy as np import os from PIL import Image import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data…

resultType的类型错误

resultType的类型错误&#xff0c;不能是List而应该是对应的返回Bean对象的类型&#xff0c;VO 这里是引用 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: Error querying database. Cause: java.lang…

opencv进阶 ——(十二)基于三角剖分实现人脸对齐

三角剖分概念 三角剖分&#xff08;Triangulation&#xff09;是一种将多边形或曲面分解为一系列互不相交的三角形的技术&#xff0c;它是计算几何、计算机图形学、地理信息系统、工程和科学计算中的一个基本概念。通过三角剖分&#xff0c;复杂的形状可以被简化为基本的三角…

病理级Polymer酶标二抗IHC试剂盒上线!

免疫组织化学 Immunohistochemistry,lHC 是利用抗体与抗原特异性识别原理&#xff0c;对组织样本中的抗原进行定位/定性分析的实验技术。组织切片保留了样品的解剖学结构特征&#xff0c;从而可以高分辨率地显现蛋白在细胞&#xff0c;甚至细胞器中的定位。基于以上特性&…

Apple - Image I/O Programming Guide

翻译自&#xff1a;Image I/O Programming Guide&#xff08;更新时间&#xff1a;2016-09-13 https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/ImageIOGuide/imageio_intro/ikpg_intro.html#//apple_ref/doc/uid/TP40005462 文章目录 …

orbslam2代码解读(1):数据预处理过程

写orbslam2代码解读文章的初衷 首先最近陆陆续续花了一两周时间学习视觉slam&#xff0c;因为之前主要是做激光slam&#xff0c;有一定基础所以学的也比较快&#xff0c;也是看完了视觉14讲的后端后直接看orbslam2的课&#xff0c;看的cvlife的课&#xff08;课里大部分是代码…

jenkins的简单使用

2.1.简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 2.4.Jenkins安装 1.下载安装包jenkins.war&#xff1b; 2.在安装…

笔记 | 软件工程04:软件项目管理

1 软件项目及其特点 1.1 什么是项目 1.2 项目特点 1.3 影响项目成功的因素 1.4 什么是软件项目 针对软件这一特定产品和服务的项目努力开展“软件开发活动",&#xff08;理解&#xff1a;软件项目是一种活动&#xff09; 1.5 软件项目的特点 1.6 军用软件项目的特点 2 …

怎么用电脑把图片转换二维码?图片在线生成二维码的步骤内容

现在很多人会通过二维码来存储物品的信息图片&#xff0c;其他人可以通过扫描二维码的方式来查看对应的图片内容&#xff0c;那么当我们需要将一批图片每个单独生成二维码&#xff0c;该如何操作能够快速将图片转换二维码呢&#xff1f; 今天&#xff0c;小编来分享给大家一个…

CNN卷积神经网络

一、概述 卷积神经网络&#xff08;CNN&#xff09;是深度学习领域的重要算法&#xff0c;特别适用于处理具有网格结构的数据&#xff0c;比如说图像和音频。它起源于二十世纪80至90年代&#xff0c;但真正得到快速发展和应用是在二十一世纪&#xff0c;随着深度学习理论的兴起…

【ai】phc:安装issac环境且fix libstdc++.so 版本报错

Pycharm远程连接服务器(2023-11-9) 大神分享了pycharm远程连接ubuntu工作站的方法。 https://github.com/ZhengyiLuo/PHC 给出的操作同样适用: 参考 Pycharm远程连接服务器(2023-11-9) :前提是一样的 PHC的要求:isaac 创建 conda activate isaac

【Vue】scoped解决样式冲突

默认情况下写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。 全局样式: 默认组件中的样式会作用到全局&#xff0c;任何一个组件中都会受到此样式的影响 局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件 一、代码示例 BaseOne…

RocketMQ可视化界面安装

RocketMQ可视化界面安装 **起因&#xff1a;**访问rocketmq-externals项目的git地址&#xff0c;下载了源码&#xff0c;在目录中并没有找到rocketmq-console文件夹。 git下面文档提示rocketMQ的仪表板转移到了新的项目中&#xff0c;点击仪表板到新项目地址&#xff1b; 下载…

搜索与图论:宽度优先搜索

搜索与图论&#xff1a;宽度优先搜索 题目描述参考代码 题目描述 输入样例 5 5 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0输出样例 8参考代码 #include <iostream> #include <algorithm> #include <cstring> using namespace std;const int N …

VsQt单元测试目录的管理方式

正常项目的文件管理方式 正常项目的目录&#xff0c;是由文件系统中实际的文件夹进行分类管理的。 但是如果单元测试用实际文件夹管理的话&#xff0c;会出现问题&#xff0c;就是被测类太多了&#xff0c;用文件系统管理的话&#xff0c;不太方面查看&#xff0c;如下图所示。…

contentType 与 dataType

contentType 与 dataType contentType contentType&#xff1a;发送的数据格式&#xff08;请求方发送给服务器的数据格式&#xff09;&#xff0c;这个内容会放在请求方的 请求头中 application/x-www-form-urlencoded 这个是默认的请求格式。 提交给后台的数据会按照 KV&am…

创新实训2024.06.06日志:部署web服务

1. 运行web项目前后端服务 首先我们要先在服务器上运行客户端以及服务端的应用程序。随后再考虑如何通过公网/局域网访问的问题。 如何启动服务在仓库对应分支下的Readme文件中已经有详细描述了。 1.1. 启动服务端 对于服务端&#xff0c;即&#xff08;要求你在服务端子项…