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
}

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

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

相关文章

TIP-2021《SRGAT: Single Image Super-Resolution With Graph Attention Network》

推荐深蓝学院的《深度神经网络加速&#xff1a;cuDNN 与 TensorRT》&#xff0c;课程面向就业&#xff0c;细致讲解CUDA运算的理论支撑与实践&#xff0c;学完可以系统化掌握CUDA基础编程知识以及TensorRT实战&#xff0c;并且能够利用GPU开发高性能、高并发的软件系统&#xf…

大语言模型与多模态模型比较

一、核心差异&#xff1a;输入数据类型与模态融合 输入数据类型 LLM&#xff1a;仅处理文本数据&#xff0c;例如文本分类、机器翻译、问答等任务&#xff0c;通过大规模语料库学习语言规律。 LMM&#xff1a;支持文本、图像、音频、视频等多种模态输入&#xff0c;例如根据图…

Apache HttpClient 5 用法-Java调用http服务

Apache HttpClient 5 核心用法详解 Apache HttpClient 5 是 Apache 基金会推出的新一代 HTTP 客户端库&#xff0c;相比 4.x 版本在性能、模块化和易用性上有显著提升。以下是其核心用法及最佳实践&#xff1a; 一、添加依赖 Maven 项目&#xff1a; <dependency><…

基于 Spark 的流量统计

一、引言 在互联网行业&#xff0c;流量统计是分析网站或应用用户行为、评估业务表现、优化资源分配以及制定营销策略的关键环节。借助 Apache Spark 强大的分布式数据处理能力&#xff0c;我们可以高效地对大规模的流量数据进行统计分析&#xff0c;获取有价值的洞察。本文将…

Python模块化编程进阶指南:从基础到工程化实践

一、模块化编程核心原理与最佳实践 1.1 模块化设计原则 根据企业级项目实践&#xff0c;模块化开发应遵循以下核心原则&#xff1a; ​​单一职责原则​​&#xff1a;每个模块只承担一个功能域的任务&#xff08;如用户认证模块独立于日志模块&#xff09;​​接口隔离原则…

锐捷交换机STP环路日志信息解读

因公司网络组建使用锐捷全系列交换机&#xff0c;近期设备巡检时发现部分日志提示信息&#xff0c; 接入交换机NBS3100-24GT4SFP-V2&#xff0c;设备频繁打出STP Blocking的日志信息。 误以为是环路导致&#xff0c;故进行实验测试&#xff0c;来验证环路情况下会如何报日志。…

使用Python调用DeepSeek的示例

使用Python调用DeepSeek API的示例代码,包括API密钥的获取、基本请求的发送以及响应处理。请确保你已经注册了DeepSeek账号并获取了API密钥。 文章目录 前言一、获取API密钥二、python示例代码三、代码说明四、注意事项五、扩展功能总结前言 提示:这里可以添加本文要记录的大…

mysql的not exists走索引吗

在MySQL中&#xff0c;​NOT EXISTS子句是否使用索引取决于子查询中关联字段是否建立了合适的索引。以下是关键点总结&#xff1a; ​索引的作用​&#xff1a; 当子查询的关联字段&#xff08;例如B.a_id&#xff09;存在索引&#xff08;如普通B-tree索引&#xff09;时&…

Python线性回归:从理论到实践的完整指南

Python线性回归&#xff1a;从理论到实践的完整指南 线性回归是数据科学和机器学习中最基础且最重要的算法之一。本文将深入探讨如何使用Python实现线性回归&#xff0c;从理论基础到实际应用&#xff0c;帮助读者全面理解这一重要的统计学和机器学习方法。 什么是线性回归&a…

鸿蒙OSUniApp 实现的二维码扫描与生成组件#三方框架 #Uniapp

UniApp 实现的二维码扫描与生成组件 前言 最近在做一个电商小程序时&#xff0c;遇到了需要扫描和生成二维码的需求。在移动应用开发中&#xff0c;二维码功能已经成为标配&#xff0c;特别是在电商、社交和支付等场景下。UniApp作为一个跨平台开发框架&#xff0c;为我们提供…

Westlake-Omni 情感端音频生成式输出模型

简述 github地址在 GitHub - xinchen-ai/Westlake-OmniContribute to xinchen-ai/Westlake-Omni development by creating an account on GitHub.https://github.com/xinchen-ai/Westlake-Omni Westlake-Omni 是由西湖心辰&#xff08;xinchen-ai&#xff09;开发的一个开源…

uv python 卸载

又是查了半天 官网wiki没有 网上一堆傻子胡说 uv提示也不对 AI还在这尼玛胡编乱造 开始 我原来装了这几个环境 uv python list 现在python3.7.7不需要了&#xff0c;卸载&#xff0c;直接 uv python uninstall 3.7.7 去找你自己要卸载的版本号&#xff0c;不需要整个包名复制…

使用哈希表封装myunordered_set和myunordered_map

文章目录 使用哈希表封装myunordered_set和myunordered_map实现出复用哈希表框架&#xff0c;并支持insert支持迭代器的实现constKey不能被修改unordered_map支持[ ]结语 我们今天又见面啦&#xff0c;给生活加点impetus&#xff01;&#xff01;开启今天的编程之路&#xff01…

后端框架(2):Java的反射机制

什么是java反射机制&#xff1f; 回顾之前java程序如何使用类 1.分析&#xff0c;确定类名&#xff0c;属性名&#xff0c;方法......创建类 2.创建类的对象 3.使用 一切都是已知的。 在程序开发中&#xff0c;在哪儿需要使用哪个类的对象&#xff0c;就在那儿创建这个类对象…

ch10 课堂参考代码

ch10 最小生成树 生成树&#xff1a;对于 n 个结点 m 条边的无向图 G&#xff0c;由全部 n 个结点和其中 n - 1 条边构成的无向连通子图称为 G 的一棵生成树。 如果图 G 原本就不连通&#xff0c;则不存在生成树&#xff0c;只存在生成森林。 最小生成树&#xff08;Minimum…

费曼技巧及提高计划

费曼技巧及提高计划 一、什么是费曼技巧&#xff1f; 费曼技巧&#xff08;Feynman Technique&#xff09;由诺贝尔物理学奖得主理查德费曼提出&#xff0c;是一种通过“以教代学”来彻底理解复杂概念的学习方法。其核心逻辑是&#xff1a; “如果你不能简单解释一件事&#x…

LongRefiner:解决长文档检索增强生成的新思路

大语言模型与RAG的应用越来越广泛&#xff0c;但在处理长文档时仍面临不少挑战。今天我们来聊聊一个解决这类问题的新方法——LongRefiner。 背景问题&#xff1a;长文档处理的两大难题 使用检索增强型生成&#xff08;RAG&#xff09;系统处理长文档时&#xff0c;主要有两个…

5月16日复盘-目标检测开端

5月16日复盘 一、图像处理之目标检测 1. 目标检测认知 ​ Object Detection&#xff0c;是指在给定的图像或视频中检测出目标物体在图像中的位置和大小,并进行分类或识别等相关任务。 ​ 目标检测将目标的分割和识别合二为一。 ​ What、Where 2. 使用场景 目标检测用于…

MySQL基础面试通关秘籍(附高频考点解析)

文章目录 一、事务篇&#xff08;必考重点&#xff09;1.1 事务四大特性&#xff08;ACID&#xff09;1.2 事务实战技巧 二、索引优化大法2.1 索引类型全家福2.2 EXPLAIN命令实战 三、存储引擎选型指南3.1 InnoDB vs MyISAM 终极对决 四、SQL优化实战手册4.1 慢查询七宗罪4.2 分…

Word图片格式调整与转换工具

软件介绍 本文介绍的这款工具主要用于辅助Word文档处理。 图片排版功能 经常和Word打交道的人或许都有这样的困扰&#xff1a;插入的图片大小各异&#xff0c;排列也参差不齐。若不加以调整&#xff0c;遇到要求严格的领导&#xff0c;可能会让人颇为头疼。 而这款工具能够统…