@vue/composition-api原理解析
前言
上一篇文章介绍了@vue/composition-api是什么,以及为什么要用,现在来系统地解析一下 @vue/composition-api 的实现原理,希望可以加深对其工作机制的理解。
老规矩先分享下AI评价:对@vue/composition-api实现原理的介绍整体上非常详细和准确,展示了核心代码以及关键逻辑,这为理解其工作机制提供了很好的分析。
核心代码分析
install
引入@vue/composition-api时,需要手动通过Vue.use(),来调用该插件的install,所以我们看看安装时到底做了什么:
export function install(Vue: VueConstructor) {setVueConstructor(Vue)mixin(Vue)
}
这里最重要的其实是mixin函数,定义了在beforeCreate钩子中去执行初始化,
构建props和上下文ctx对象,执行setup函数:
export function mixin(Vue: VueConstructor) {Vue.mixin({beforeCreate: functionApiInit,mounted(this: ComponentInstance) {afterRender(this)},beforeUpdate() {updateVmAttrs(this as ComponentInstance)},updated(this: ComponentInstance) {afterRender(this)},})function functionApiInit(this: ComponentInstance) {const vm = thisconst $options = vm.$optionsconst { setup, render } = $optionsconst { data } = $options// wrapper the data option, so we can invoke setup before data get resolved$options.data = function wrappedData() {// 在data周期时,进行setup函数的执行initSetup(vm, vm.$props)return data || {}}}function initSetup(vm: ComponentInstance, props: Record<any, any> = {}) {const setup = vm.$options.setup!// 构造ctx对象,有以下key:// slots: 组件的插槽,默认为 {}// root: 组件的根实例// parent: 组件的父实例// refs: 组件的 ref 引用// listeners: 组件的事件监听器// isServer: 是否是服务端渲染// ssrContext: 服务端渲染的上下文// emit: 组件的自定义事件触发函数const ctx = createSetupContext(vm)const instance = toVue3ComponentInstance(vm)instance.setupContext = ctx// 通过Vue.observable对props进行响应式监听def(props, '__ob__', createObserver())}
}
ref
ref函数可以把一个普通的值转成响应式的数据。它返回一个可变的ref对象,对象上挂载了一个.value属性,我们可以通过这个.value属性读取或者修改ref的值。
其本质还是基于reactive来实现的响应式,只不过做了一个前置的get、set封装,源码如下:
export function ref(raw?: unknown) {// ref的本质其实还是调用的reactive// 然后通过 get/set 方法操作这个对象的值来实现对 ref 值的跟踪和响应const value = reactive({ [RefKey]: raw })return createRef({get: () => value[RefKey] as any,set: (v) => ((value[RefKey] as any) = v),})
}
createRef最后还是基于Object.defineProperty:
export function proxy(target: any,key: string,{ get, set }: { get?: Function; set?: Function }
) {Object.defineProperty(target, key, {enumerable: true,configurable: true,get: get || noopFn,set: set || noopFn,})
}
reactive
底层基于Vue.observable来建立响应式监听,Vue.observable 在 Vue2 和 Vue3 中的实现方式有所区别而已:
export function reactive<T extends object>(obj: T): UnwrapRef<T> {// 基于Vue.observableconst observed = observe(obj)setupAccessControl(observed)return observed as UnwrapRef<T>
}
computed
computed函数用来创建一个计算属性,它根据依赖进行缓存和懒执行。
构建get、set函数,通过vue的Watcher对象,进行监听绑定。
export function computed<T>(getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
): ComputedRef<T> | WritableComputedRef<T> {const vm = getCurrentScopeVM()let getter: ComputedGetter<T>let setter: ComputedSetter<T> | undefinedgetter = getterOrOptions.getsetter = getterOrOptions.setlet computedSetterlet computedGetterconst { Watcher, Dep } = getVueInternalClasses()let watcher: anycomputedGetter = () => {if (!watcher) {watcher = new Watcher(vm, getter, noopFn, { lazy: true })}if (watcher.dirty) {watcher.evaluate()}if (Dep.target) {watcher.depend()}return watcher.value}computedSetter = (v: T) => {if (setter) {setter(v)}}return createRef<T>({get: computedGetter,set: computedSetter,},!setter,true) as WritableComputedRef<T> | ComputedRef<T>
}
watch
watch函数用于侦听特定的数据源,并在回调函数中执行副作用。
底层是通过Vue.$watch来实现的:
// 底层是通过Vue.$watch来实现
function createVueWatcher(vm: ComponentInstance,getter: () => any,callback: (n: any, o: any) => any,options: {deep: booleansync: booleanimmediateInvokeCallback?: booleannoRun?: booleanbefore?: () => void}
): VueWatcher {const index = vm._watchers.length// @ts-ignore: use undocumented optionsvm.$watch(getter, callback, {immediate: options.immediateInvokeCallback,deep: options.deep,lazy: options.noRun,sync: options.sync,before: options.before,})return vm._watchers[index]
}
toRefs
toRefs 可以把一个 reactive 对象的属性都转成 ref 形式。
底层是一个个遍历key去调用toRef,然后基于createRef来保留响应式能力:
export function toRef<T extends object, K extends keyof T>(object: T,key: K
): Ref<T[K]> {if (!(key in object)) set(object, key, undefined)const v = object[key]if (isRef<T[K]>(v)) return vreturn createRef({get: () => object[key],set: (v) => (object[key] = v),})
}
生命周期
生命周期其实是一个临时覆盖:
// 利用 Vue 的选项合并策略(option merge strategies),
// 通过给组件实例的 $options 注入自定义的生命周期hook函数,来override原有的生命周期选项。// 生命周期hook函数需要通过 getCurrentInstance() 获取当前活跃的组件实例,
// 并在执行回调前通过 setCurrentInstance()绑定实例,在回调执行完毕后恢复之前的实例。export const onBeforeMount = createLifeCycle('beforeMount')
export const onMounted = createLifeCycle('mounted')
export const onBeforeUpdate = createLifeCycle('beforeUpdate')
export const onUpdated = createLifeCycle('updated')
export const onBeforeUnmount = createLifeCycle('beforeDestroy')
export const onUnmounted = createLifeCycle('destroyed')
export const onErrorCaptured = createLifeCycle('errorCaptured')
export const onActivated = createLifeCycle('activated')
export const onDeactivated = createLifeCycle('deactivated')
export const onServerPrefetch = createLifeCycle('serverPrefetch')
相关文章:
@vue/composition-api原理解析
前言 上一篇文章介绍了vue/composition-api是什么,以及为什么要用,现在来系统地解析一下 vue/composition-api 的实现原理,希望可以加深对其工作机制的理解。 老规矩先分享下AI评价:对vue/composition-api实现原理的介绍整体上非…...
Kubernetes(K8s)从入门到精通系列之三:K8s的基本概念和术语之资源对象概述
Kubernetes K8s从入门到精通系列之三:K8s的基本概念和术语之资源对象概述 K8s中的基本概念和术语大多是围绕资源对象(Resource Object)来说的,而资源对象在总体上可分为以下两类: 某种资源的对象,例如节点(Node)、Pod、服务(Serv…...
cc2652在使用过程中的一些注意事项
可能不只是cc2652有这些坑,估计cc26xx系列都存在。 CCS的预编译宏配置位置 时钟获取 时钟获取__STATIC_INLINE uint32_t SysCtrlClockGet( void )在sys_ctrl.h中,sys_ctrl.h没有在工程路径下面,在其sdk中 节拍时间获取 ICall_getTicks(); …...
YAPI接口自动鉴权功能部署详解
目录 安装准备 在线安装 离线安装 配置使用 安装准备 以下操作,默认要求自己部署过yapi,最好是部署过yapi二次开发环境。 无论是选择在线安装或者是本地安装,都需要安装client工具。 1、yapi-cli:npm install yapi-cli –g…...
【雕爷学编程】Arduino动手做(180)---Seeeduino Lotus开发板3
37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…...
搜索与图论(二)
最短路 单源最短路 所有边权都是正数 朴素Dijkstra算法 基本思路:从1号点到其他点的最短距离 步骤: 定义一个s集合包含当前已确定最短距离的点 1、初始化距离dis[1] 0,dis[其它] 正无穷 2、for i 0-n循环n次 2.1找到不在s中的距离最近的点 ->t 2.2把t加到s当中去…...
【SQL】-【计算两个varchar类型的timestamp的毫秒差】
背景 TRANSTAMP3、TRANSTAMP2在Oracle数据库中的类型为varchar,但实际保存的值是时间戳timestamp类型,现在要计算二者的毫秒差 Oracle或MySQL extract(second from (to_timestamp(TRANSTAMP3,yyyy-mm-dd hh24:mi:ss.ff) - to_timestamp(TRANSTAMP2,yyy…...
Java 微信商家打款到零钱(旧版本接口)
旧版微信支付接口要求请求时携带证书请求 构建请求参数 /*** 付款到零钱** param withdrawalDto dto撤军* return {link Map }<{link String }, {link Object }>* throws Exception 异常* Author chen 2023-07-27 15:04*/private Map<String, Object> payToUser(Wi…...
Vue+Element ui Study
目录 一、VueElement ui 1、show-overflow-tooltip属性设置宽度 2、this.$refs使用方法 Error in v-on handler: “TypeError: Cannot read properties of undefined (reading ‘xxx‘)“ 一、VueElement ui 1、show-overflow-tooltip属性设置宽度 :show-overflow-toolti…...
JAVA基础-多线程入门(详解)
目录 引言 一,线程概念 二,创建线程 2.1,继承Thread类,重写run方法 2.2,实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函 数的target 2.3,通…...
Cirno‘s Perfect Equation Class 2023牛客暑期多校训练营5 D
登录—专业IT笔试面试备考平台_牛客网 题目大意:有q次询问,每次给出三个整数k,c,n,求有多少满足条件的数对(a,b)满足kabc且c是b的倍数,且gcd(a,b)>n 1<q<100;…...
pytorch学习——如何构建一个神经网络——以手写数字识别为例
目录 一.概念介绍 1.1神经网络核心组件 1.2神经网络结构示意图 1.3使用pytorch构建神经网络的主要工具 二、实现手写数字识别 2.1环境 2.2主要步骤 2.3神经网络结构 2.4准备数据 2.4.1导入模块 2.4.2定义一些超参数 2.4.3下载数据并对数据进行预处理 2.4.4可视化数…...
PySpark 数据操作
数据输入 RDD对象 如图可见,PySpark支持多种数据的输入,在输入完成后,都会得到一个:RDD类的对象 RDD全称为:弹性分布式数据集(Resilient Distributed Datasets) PySpark针对数据的处理&…...
FPGA2-采集OV5640乒乓缓存后经USB3.0发送到上位机显示
1.场景 基于特权A7系列开发板,采用OV5640摄像头实时采集图像数据,并将其经过USB3.0传输到上位机显示。这是验证数据流能力的很好的项目。其中,用到的软件版本,如下表所示,基本的硬件情况如下。该项目对应FPGA工程源码…...
亚信科技AntDB数据库专家参加向量数据库首次技术标准研讨会
2023年7月19日下午,中国通信标准化协会大数据技术标准推进委员会数据库与存储工作组(CCSA TC601 WG4)联合中国信通院数据库应用创新实验室(CAICT DBL)在线上召开《向量数据库技术要求》标准首次研讨会。本次会议由中国…...
Windows中实现右键把电子书通过邮件发到kindle
不使用第三方软件,通过Windows自带的函数,可以实现右键将电子书通过电子邮件发送到kindle邮箱,从而实现kindle电子书传送功能。实现过程如下: 1. 使用bat添加右键功能 打开资源管理器,在地址中输入%APPDATA%\Microso…...
Three.js之创建3D场景
参考资料 【G】Three.js官方文档:https://threejs.org/docs/ Three.js是一个流行的WebGL库,官方文档提供了详细的API参考和示例,适合学习和参考。【G】Three.js GitHub链接:https://github.com/mrdoob/three.js 这是一个流行的基…...
一个3年Android的找工作记录
作者:Petterp 这是我最近 1个月 的找工作记录,希望这些经历对你会有所帮助。 有时机会就像一阵风,如果没有握住,那下一阵风什么时候吹来,往往是个运气问题。 写在开始 先说背景: 自考本,3年经验࿰…...
CAS原理解析
CAS是一种乐观锁机制,一种比较并交换的过程和理念,用来解决线程安全问题,具体来讲就是对共享变量值的安全更新机制。能够保证原子、可见、一致性。这种交换过程是在Unsafe类中实现。 从一段简单的代码开始来对源码做分析 public static void…...
SQL项目实战:银行客户分析
大家好,本文将与大家分享一个SQL项目,即根据从数据集收集到的信息分析银行客户流失的可能性。这些洞察来自个人信息,如年龄、性别、收入和人口统计信息、银行卡类型、产品、客户信用评分以及客户在银行的服务时间长短等。对于银行而言&#x…...
Wireshark实战:从流量包里‘捞出’图片和压缩包的两种方法(附CTF解题步骤)
Wireshark实战:从流量包里‘捞出’图片和压缩包的两种方法(附CTF解题步骤) 在网络安全和数字取证领域,网络流量分析是一项基础但至关重要的技能。想象一下这样的场景:你正在调查一起数据泄露事件,或者参加…...
飞机在飞行中将电力传输至地面接收器
此次演示为太空太阳能新方案奠定了基础。在2025年11月一个狂风大作的日子,一架塞斯纳涡轮螺旋桨飞机在5000米的高度飞越宾夕法尼亚州上空时,遭遇了时速高达70节(约130公里/小时)的侧风,风速几乎与这架小型飞机的飞行速…...
AI赋能能耗管理:解锁智能照明低碳运维新范式
摘要在双碳战略全面落地、智慧楼宇数字化转型的浪潮下,智能照明已广泛应用于商业园区、市政道路、写字楼等各类场景。传统照明能耗管理模式粗放,存在能耗数据模糊、浪费隐蔽、管控滞后、节能无依据等痛点,大量无效耗电持续增加运营成本。新一…...
从‘Hello World’到工业通信:我的第一个C++ ADS客户端连接倍福PLC踩坑实录
从零搭建C ADS客户端:一位工程师的倍福PLC连接实战手记 第一次在Visual Studio里看到那个红色的编译错误时,我盯着屏幕足足愣了五分钟。"LNK2019: 无法解析的外部符号 __imp_AdsPortOpen",这行冰冷的报错彻底击碎了我以为照着官方…...
AI赋能泳装设计——让科技与时尚共舞
AI赋能泳装设计——让科技与时尚共舞当AI遇见泳装:北京先智先行用智能技术重新定义夏日时尚夏日的脚步渐近,泳装市场即将迎来年度销售旺季。在这个看脸的时代,消费者对泳装的要求早已不止于"能穿",更追求个性化、时尚感…...
LVGL按钮(lv_btn)与开关(lv_switch)事件处理全解析:从点击检测到实现‘智能家居面板’
LVGL按钮与开关事件处理实战:构建智能家居控制面板 在嵌入式设备的人机交互界面开发中,LVGL作为轻量级图形库已经成为许多开发者的首选。特别是当我们需要为智能家居系统设计控制面板时,如何高效地处理按钮(lv_btn)和开关(lv_switch)的交互事…...
2026年做了一个大胆的决定:我要收徒弟了!
如果你已经在测试/开发领域摸爬滚打几年,一定有过这样的困惑:• 报了很多课,学了很多工具/框架/技术,但遇到真实项目问题依然无从下手• 想转型测开/管理/AI测试,却不知道自己的短板到底在哪• 职场遇到瓶颈࿰…...
别再死记硬背UML关系了!用4+1视图帮你理清类图、时序图到底画给谁看
别再死记硬背UML关系了!用41视图帮你理清类图、时序图到底画给谁看 在软件工程领域,UML(统一建模语言)是每个开发者都绕不开的话题。但有多少人真正理解这些图形的实际应用场景?我们常常看到这样的现象:团队…...
Google I/O 2026发布Gemini 3.5 Flash:性能超越3.1 Pro,输出速度快4倍!
Google在I/O 2026上正式发布Gemini 3.5 Flash,这是其最新一代结合前沿智能与行动能力的模型系列,在多项基准测试中表现出色,输出token速度更是其他前沿模型的4倍。 性能卓越 3.5 Flash定位为迄今最强的Agentic和编程模型,在Termin…...
深入浅出:拆解Xilinx ERNIC IP的硬件架构,看RoCE v2如何卸载CPU
深入浅出:拆解Xilinx ERNIC IP的硬件架构,看RoCE v2如何卸载CPU 在数据中心和高性能计算领域,RDMA(远程直接内存访问)技术正成为突破网络性能瓶颈的关键。Xilinx的ERNIC IP核作为RoCE v2协议的硬件实现,通过…...
