Vue 3.0双向数据绑定实现原理
Vue3 的数据双向绑定是通过响应式系统来实现的。相比于 Vue2,Vue3 在响应式系统上做了很多改进,主要使用了 Proxy 对象来替代原来的 Object.defineProperty。本文将介绍 Vue3 数据双向绑定的主要特点和实现方式。
1. 响应式系统
1.1. Proxy对象
Vue3 使用 JavaScript 的 Proxy 对象来实现响应式数据。Proxy 可以监听对象的所有操作,包括读取、写入、删除属性等,从而实现更加灵活和高效的响应式数据。
1.2. reactive函数
Vue3 提供了一个 reactive 函数来创建响应式对象,通过 reactive 函数包装的对象会变成响应式数据,Vue 会自动跟踪这些数据的变化。
import { reactive } from 'vue';const state = reactive({message: 'Hello Vue3'
});
1.3. ref函数
对于基本数据类型,如字符串、数字等,Vue3 提供了 ref 函数来创建响应式数据,使用 ref 包装的值可以在模板中进行双向绑定。
import { ref } from 'vue';const count = ref(0);
2. 双向绑定
Vue3 中的双向绑定主要通过 v-model 指令来实现,适用于表单元素,如输入框、复选框等。以下是一个简单的示例:
<template><input v-model="message" /><p>{{ message }}</p>
</template><script>
import { ref } from 'vue';export default {setup() {const message = ref('');return {message}}
}
</script>
在 Vue3 中,v-model 的使用更加灵活,可以支持自定义组件的双向绑定:
<template><CustomInput v-model:value="message" />
</template><script>
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';export default {components: {CustomInput},setup() {const message = ref('');return {message}}
}
</script>
在 CustomInput 组件中:
<template><input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"/>
</template><script>
export default {props: {modelValue: String}
};
</script>
下面,我们来深入了解 Vue3 如何通过源码实现数据的双向绑定。
3. 源码实现
3.1. Proxy实现响应式
Vue3 使用 Proxy 对象来实现响应式数据。Proxy 允许我们定义基本操作的自定义行为,如读、写、删除、枚举等。
以下是 Vue3 响应式系统的核心代码片段:
function reactive(target) {return createReactiveObject(target, mutableHandlers);
}const mutableHandlers = {get(target, key, receiver) {// 依赖收集track(target, key);const res = Reflect.get(target, key, receiver);// 深度响应式处理if (isObject(res)) {return reactive(res);}return res;},set(target, key, value, receiver) {const oldValue = target[key];const result = Reflect.set(target, key, value, receiver);// 触发更新if (oldValue != value) {trigger(target, key);}return result;},// 其他处理函数 (deleteProperty, has, ownKeys 等)
};function createReactiveObject(target, handlers) {if (!isObject(target)) {return target;}const proxy = new Proxy(target, handlers);return proxy;
}
3.2. 依赖心集与触发更新
在响应式系统中,依赖收集和触发更新是两个核心概念。Vue3 使用 track和 trigger 函数来实现这两个功能。
const targetMap = new WeakMap();function track(target, key) {const effect = activeEffect;if (effect) {let depsMap = targetMap.get(target);if (!depsMap) {targetMap.set(target, (depsMap = new Map()));}let dep = depsMap.get(key);if (!dep) {depsMap.set(key, (dep = new Set()));}if (!dep.has(effect)) {dep.add(effect);effect.deps.push(dep);}}
}function trigger(target, key) {const depsMap = targetMap.get(target);if (!depsMap) return;const effects = new Set();const add = effectsToAdd => {if (effectsToAdd) {effectsToAdd.forEach(effect => effects.add(effect));}};add(depsMap.get(key));effects.forEach(effect => effect());
}
3.3. ref实现
对于基本数据类型,Vue3 提供了 ref 函数来创建响应式数据。ref 使用一个对象来包装值,并通过 getter和 setter 来实现响应式。
function ref(value) {return createRef(value);
}function createRef(rawValue) {if (isRef(rawValue)) {return rawValue;}const r = {__v_isRef: true,get value() {track(r, 'value');return rawValue;},set value(newVal) {if (rawValue !== newVal) {rawValue = newVal;trigger(r, 'value');}}};return r;
}function isRef(r) {return r ? r.__v_isRef === true : false;
}
3.4. v-model实现
Vue3 中的 v-model 实现依赖于响应式系统。
3.4.1. 编译时实现
// packages/compiler-core/src/transforms/vModel.ts
export const transformModel: NodeTransform = (node, context) => {if (node.type === NodeTypes.ELEMENT) {// 对每个元素节点执行此方法return () => {// 只处理有 v-model 指令的节点const node = context.currentNodelif (node.tagType === ElementTypes.ELEMENT) {const dir = findDir(node, 'model')if (dir && dir.exp) {// 根据节点类型调用不同的处理函数const { tag } = nodeif (tag === 'input') {processInput(node, dir, context)} else if (tag === 'textarea') {processTextArea(node, dir, context)} else if (tag === 'select') {processSelect(node, dir, context)} else if (!context.inSSR) {// 组件上的 v-modelprocessComponent(node, dir, context)}}}}}
}// 处理组件上的v-model
function processComponent(node: ElementNode,dir: DirectiveNode,context: TransformContext
) {// 获取 v-model 的参数,支持 v-model:arg 形式const { arg, exp } = dir// 默认参数是 'modelValue'const prop = arg ? arg : createSimpleExpression('modelValue', true)// 默认事件是 'update:modelValue'const event = arg? createSimpleExpression(`update:${arg.content}`, true): createSimpleExpression('update:modelValue', true)// 添加 prop 和 event 到 props 中const props = [createObjectProperty(prop, dir.exp!),createObjectProperty(event, createCompoundExpression([`$event => ((`, exp, `) = $event)`]))]// 将 v-model 转换为组件的 props 和事件node.props.push(createObjectProperty(createSimpleExpression(`onUpdate:modelValue`, true),createCompoundExpression([`$event => (${dir.exp!.content} = $event)`])))
}
3.4.2. 运行时实现
// packages/runtime-core/src/helpers/vModel.ts
export function vModelText(el: any, binding: any, vnode: VNode) {// 处理文本输入框的 v-modelconst { value, modifiers } = bindingel.value = value == null ? '' : value// 添加事件监听el._assign = getModelAssigner(vnode)const lazy = modifiers ? modifiers.lazy : falseconst event = lazy ? 'change' : 'input'el.addEventListener(event, e => {// 触发更新el._assign(el.value)})
}export function vModelCheckbox(el: any, binding: any, vnode: VNode) {// 处理复选框的 v-modelconst { value, oldValue } = bindingel._assign = getModelAssigner(vnode)// 处理数组类型的值(多选)if (isArray(value)) {const isChecked = el._modelValue? looseIndexOf(value, el._modelValue) > -1: falseif (el.checked !== isChecked) {el.checked = isChecked}} else {// 处理布尔值if (value !== oldValue) {el.checked = looseEqual(value, el._trueValue)}}
}// 辅肋函数
function getModelAssigner(vnode: VNode): (value: any) => void {// 获取模型赋值函数const fn = vnode.props!['onUpdate:modelValue']return isArray(fn) ? (value: any) => invokeArrayFns(fn, value) : fn
}
相关文章:
Vue 3.0双向数据绑定实现原理
Vue3 的数据双向绑定是通过响应式系统来实现的。相比于 Vue2,Vue3 在响应式系统上做了很多改进,主要使用了 Proxy 对象来替代原来的 Object.defineProperty。本文将介绍 Vue3 数据双向绑定的主要特点和实现方式。 1. 响应式系统 1.1. Proxy对象 Vue3 …...

Please install it with pip install onnxruntime
无论怎么安装都是 Please install it with pip install onnxruntime 我python 版本是3.11 ,我换成3.10 解决了...
java -jar命令运行 jar包时如何运行外部依赖jar包
java -jar命令运行 jar包时如何运行外部依赖jar包 场景: 打包发不完,运行时。发现一个问题, java java.lang.NoClassDefFoundError: org/apache/commons/lang3/ArrayUtils 显示此,基本表明,没有这个依赖,如果在开发…...

低损耗高效能100G O Band DWDM 10km光模块 | 支持密集波分复用
目录 前言 一、产品概述 100G QSFP28 O Band DWDM 10km光模块核心特点包括: 二、为何选择O Band DWDM方案? 1.低色散损耗,传输更稳定 2.兼容性强 三、典型应用场景 1.数据中心互联(DCI) 2.企业园区/智慧城市组网 3.电信…...
【解决分辨数字】2021-12-16
缘由用C语言解决分辨数字-编程语言-CSDN问答 int a 0, w 0, aa[6]{};cin >> a;while (a)aa[w] a % 10, a / 10, w;cout << w << endl;while (a<w)cout << aa[a] << ends, aa[5] * 10, aa[5] aa[a];cout << endl << aa[5] <…...
el-tree结合checkbox实现数据回显
组件代码 <el-tree:data"vertiList"show-checkboxnode-key"id":props"defaultProps"ref"treeRefx"class"custom-tree"check-change"handleCheckChange"> </el-tree>获取选择的节点 handleCheckChan…...

第二十六天打卡
全局变量 global_var 全局变量是定义在函数、类或者代码块外部的变量,它在整个程序文件内都能被访问。在代码里, global_var 就是一个全局变量,下面是相关代码片段: print("\n--- 变量作用域示例 ---") global_var …...

阿里云ECS部署Dify
一:在ECS上面安装Docker 关防火墙 sudo systemctl stop firewalld 检查防火墙状态 systemctl status firewalld sudo yum install -y yum-utils device-mapper-persistent-data lvm2 设置阿里镜像源,安装并启动docker [base] nameCentOS-$releas…...
【线段树】P4588 [TJOI2018] 数学计算|普及+
本文涉及知识点 C线段树 [TJOI2018] 数学计算 题目描述 小豆现在有一个数 x x x,初始值为 1 1 1。小豆有 Q Q Q 次操作,操作有两种类型: 1 m:将 x x x 变为 x m x \times m xm,并输出 x m o d M x \bmod M…...

日志与策略模式
什么是设计模式 IT⾏业 ,为了让 菜鸡们不太拖⼤佬的后腿, 于是⼤佬们针对⼀些经典的常⻅的场景, 给定了⼀些对应的解决⽅案, 这个就是 设计模式 日志认识 计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件,主要作⽤是监控运⾏状态、记录异常信 息ÿ…...

Jenkins 最佳实践
1. 在Jenkins中避免调度过载 过载Jenkins以同时运行多个作业可能导致资源竞争、构建速度变慢和系统性能问题。分配作业启动时间可以防止瓶颈,并确保更顺畅的执行。如何实现? 在Cron表达式中使用H:引入抖动(jitter)&a…...

天能股份SAP系统整合实战:如何用8个月实现零业务中断的集团化管理升级
目录 天能股份SAP系统整合案例:技术驱动集团化管理的破局之路 一、企业背景:新能源巨头的数字化挑战 二、项目难点:制造业的特殊攻坚战 1. 生产连续性刚性需求 2. 数据整合三重障碍 3. 资源限制下的技术突围 三、解决方案:S…...
搜索引擎的高级语法
文章目录 精确搜索:双引号站内搜索:site通配符搜索:*减号缩小范围:-文档搜索:filetypeURL搜索: inurl标题搜索:intitle正文搜索:intext参考链接 精确搜索:双引号 “ ” …...

uniapp-商城-59-后台 新增商品(属性的选中,进行过滤展示,filter,some,every和map)
前面讲了属性的添加,添加完成后,数据库中已经存在数据了,这时再继续商品的添加时,就可以进行属性的选择了。 在商品添加过程中,属性选择是一个关键步骤。首先,界面需要展示嵌套的属性数据,用户通…...
linux用户切换
在 Linux 系统中,/etc/shadow 文件存储了用户的加密密码和其他安全相关信息,因此默认只有 root 用户 才有权限读取。当你尝试用普通用户身份查看时,会收到 Permission denied 错误。 如何查看 /etc/shadow 文件? 方法 1ÿ…...

B2C 商城转型指南:传统企业如何用 ZKmall模板商城实现电商化
在数字化浪潮席卷全球的当下,传统企业向电商转型已不再是选择题,而是关乎生存与发展的必答题。然而,缺乏技术积累、开发成本高、运营经验不足等问题,成为传统企业转型路上的 “拦路虎”。ZKmall模板商城以其低门槛、高灵活、强适配…...
鸿蒙OSUniApp 实现的二维码扫描与生成组件#三方框架 #Uniapp
UniApp 实现的二维码扫描与生成组件 前言 最近在做一个电商小程序时,遇到了需要扫描和生成二维码的需求。在移动应用开发中,二维码功能已经成为标配,特别是在电商、社交和支付等场景下。UniApp作为一个跨平台开发框架,为我们提供…...

生成树协议 - STP
目录 BPDU STP选举机制 STP端口状态 STP计时器 STP拓扑变更机制 生成树协议(Spanning Tree Protocol),简写为STP。 STP是二层网络中用于消除环路的协议,通过阻塞冗余链路,使可用链路在拓扑上呈现出无环的树结构&…...

计算机指令分类和具体的表示的方式
1.关于计算机的指令系统 下面的这个就是我们的一个简单的计算机里面涉及到的指令: m就是我们的存储器里面的地址,可以理解为memory这个意思,r可以理解为rom这样的单词的首字母,帮助我们去进行这个相关的指令的记忆,不…...

mvc-service引入
什么是业务层 1)Model1(JSP)和Model2(模糊的mvc): MVC:Model(模型),View(视图),Controller(控制器) 视图层:用于数据展示以及用户交互的界…...

基于微信小程序的城市特色旅游推荐应用的设计与实现
💗博主介绍💗:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示:文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…...

【暗光图像增强】【基于CNN的方法】2020-AAAI-EEMEFN
EEMEFN:Low-Light Image Enhancement via Edge-Enhanced Multi-Exposure Fusion Network EEMEFN:基于边缘增强多重曝光融合网络的低光照图像增强 AAAI 2020 论文链接 0.论文摘要 本研究专注于极低光照条件下的图像增强技术,旨在提升图像亮度…...

【Linux】ssh命令 – 安全的远程连接服务
原创:厦门微思网络 SSH命令的概念 ssh命令的功能是安全地远程连接服务器主机系统,作为OpenSSH套件中的客户端连接工具,ssh命令可以让我们轻松地基于SSH加密协议进行远程主机访问,从而实现对远程服务器的管理工作。 语法 ssh 参…...

AT9850B—单北斗导航定位芯片
AT9850B是一款高性能低功耗双频单北斗卫星导航接收机SOC单芯片。芯片集成射频前端和数字基带、多模式卫星信号处理引擎、电源管理功能,集成度高,外围应用电路简洁。 支持中国北斗B1I/B1C单频定位或B1I/B1C/B2a双频定位,支持北斗二号和三号&a…...
【开源Agent框架】CAMEL:角色扮演+任务分解
一、项目概览:重新定义智能体协作范式 CAMEL(Communicative Agents for “Mind” Exploration of Large Language Model Society)是由camel-ai社区开发的开源多智能体框架,致力于探索智能体的规模法则(Scaling Laws)。该项目通过构建包含百万级智能体的复杂社会系统,研…...

工业4G路由器IR5000公交站台物联网应用解决方案
随着城市化进程的加速,公共交通是智慧城市的重要枢纽。城市公共交通由无数的公交站台作作为节点组合而成,其智能化升级成为提升城市出行效率与服务质量的关键。传统公交站台信息发布滞后、缺乏实时性,难以满足乘客对公交信息快速获取的需求&a…...

idea中Lombok失效的解决方案
Lombok 是一个 Java 库,旨在通过注解简化 Java 代码的编写,减少样板代码,提高开发效率。它通过自动生成常见的代码(如 getter、setter、构造函数等)来减少开发者的手动编码工作。 一般Lombok失效有四步排查方案&#…...
如何借助iPaaS集成平台做好API 版本管理
在当今数字化快速发展的浪潮中,API 作为企业连接内外部系统、实现数据交互与业务协同的关键桥梁,在企业发展进程中扮演着至关重要的角色。它不仅支撑着企业的日常运营,更是企业拓展业务边界、提升竞争力的核心要素之一。然而,API …...

黑马k8s(九)
1.Pod-生命周期概述 2.Pod生命周期-创建和终止 3.Pod生命周期-初始化容器...

【超分辨率专题】一种考量视频编码比特率优化能力的超分辨率基准
这是一个Benchmark,超分辨率视频编码(2024) 专题介绍一、研究背景二、相关工作2.1 SR的发展2.2 SR benchmark的发展 三、Benchmark细节3.1 数据集制作3.2 模型选择3.3 编解码器和压缩标准选择3.4 Benchmark pipeline3.5 质量评估和主观评价研…...