框架的源码理解——V3中的ref和reactive
最近在研究各个框架的源码,从源码角度去理解 vue3 的 reactive 和 ref API,记录下研究的成果
reactive
首先,reactive() 的参数必须是一个对象,返回值是一个 Proxy 对象,具有响应性。如果参数不是对象类型,会提示:value cannot be made reactive。
多次对同一个对象使用 reactive 进行代理,返回的是相同的代理对象,也就是说使用的是缓存的值。而且,取值时直接读取属性就行,不需要加 .value 。
例子:
import { reactive } from 'vue'
const state = reactive({ count: 0 })
console.log(state.count) // 0const name = reactive('hh')
console.log('name', name) // warn: value cannot be made reactive: hhconst raw = {}
const proxy = reactive(raw)
console.log(proxy === raw) // false
// calling reactive() on the same object returns the same proxy
console.log(reactive(raw) === proxy) // true
// calling reactive() on a proxy returns itself
console.log(reactive(proxy) === proxy) // true
接下来说下 reactive 的局限性。
首先,参数只支持 object 类型 (比如 objects, arrays, Map, Set),不支持基础数据类型,比如string, number 或boolean;
其次,对变量重新赋值会丢失响应性,比如:
let state = reactive({ count: 0 })
// the above reference ({ count: 0 }) is no longer being tracked
// (reactivity connection is lost!)
state = reactive({ count: 1 })
而且,解构赋值容易丢失响应性:
const state = reactive({ count: 0 })// count is disconnected from state.count when destructured.
let { count } = state
// does not affect original state
count++
这种情况下,我们可以使用 toRefs 函数来将响应式对象转换为 ref 对象
import { toRefs } from 'vue';const state = reactive({ count: 0 });
let { count } = toRefs(state);
count++; // count 现在是 1
ref
再来看下 ref() 。reactive 和 ref 都是声明响应式变量的写法,但是,ref 的参数既可以是基本数据类型的值,也可以是对象,很自由!这就是为什么我们在开发时更推荐使用 vue3 的 ref 的原因了。
而且,ref 声明的变量在取值时必须加上 .value,而在 template 调用时中不加。
例子:
再来看下 ref() 。reactive 和 ref 都是声明响应式变量的写法,但是,ref 的参数既可以是基本数据类型的值,也可以是对象,很自由!这就是为什么我们在开发时更推荐使用 vue3 的 ref 的原因了。
而且,ref 声明的变量在取值时必须加上 .value,而在 template 调用时中不加。
例子:
const {ref, effect} = Vueconst name = ref('张三')
console.log('name', name.value) // name 张三const state = ref({ count: 0 })
console.log('state', state.value.count) // state 0
ref 源码
深入源码看下为什么。
ref() 中调用的是 createRef(value, false),在这个函数中,首先判断属性 __v_isRef 是否为 true,为 true 说明是 Ref 类型的值,直接返回;否则,返回的是 RefImpl 类的实例。
类的 get 和 set
再来看 RefImpl 类,重点是类中定义了 get 函数和 set 函数。当我们对类实例的 value 属性取值和赋值时,就会触发这两个函数。
// ref.tsexport function ref(value?: unknown) {return createRef(value, false)
}function createRef(rawValue: unknown, shallow: boolean) {// 判断属性 __v_isRef 是否为 true,为 true 说明是 Ref 类型的值,直接返回if (isRef(rawValue)) {return rawValue}return new RefImpl(rawValue, shallow)
}export function isRef(r: any): r is Ref {return !!(r && r.__v_isRef === true)
}class RefImpl<T> {private _value: Tprivate _rawValue: T// 依赖项public dep?: Dep = undefined// 属性 __v_isRef 设置为 truepublic readonly __v_isRef = trueconstructor(value: T, public readonly __v_isShallow: boolean) {this._rawValue = __v_isShallow ? value : toRaw(value)this._value = __v_isShallow ? value : toReactive(value)}get value() {// 依赖收集trackRefValue(this)// 返回值return this._value}set value(newVal) {const useDirectValue =this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)newVal = useDirectValue ? newVal : toRaw(newVal)if (hasChanged(newVal, this._rawValue)) {this._rawValue = newValthis._value = useDirectValue ? newVal : toReactive(newVal)triggerRefValue(this, newVal)}}
}
举个例子理解下类中的 get 和 set 函数:
class RefImpl {// ref实例的getter行为get value () {console.log('get');return '111'}// ref实例的setter行为set value (val) {console.log('set');}
}const ref = new RefImpl()ref.value = '123'
ref.value
这里定义了 RefImpl 类,当我们对 ref.value 赋值时,会打印 set;当我们调用 ref.value 时,会打印 get。因此,我们不难理解为什么 Vue3 的 ref() 要加上 .value 了,因为也是使用了类中的 getter 和 setter 的写法。
此外,ref() 最终的返回值是 this._value,我们再来看下这部分的代码。这里是判断属性 __v_isShallow 是否为 true,为true 则直接返回,否则经过 toReactive() 处理下再返回。
this._value = __v_isShallow ? value : toReactive(value)
toReactive()
看下这个函数发生了什么。可以看到,如果参数是对象类型,则使用 reactive() 处理一下并返回;否则直接返回这个参数。
而 reactive() 中,我们是返回一个对象的 Proxy 对象,这个 Proxy 对象具有响应性,可以监听到我们对对象属性的读取和修改。值得一提的是,这里的 reactive() 正是 上面说到的声明响应性变量的 reactive() !也就是说,ref 的底层也用到了 reactive() ,二者是相通的,只不过 ref 多包装了一层,支持了基本数据类型的值。
// reactive.ts/*** Returns a reactive proxy of the given value (if possible).** If the given value is not an object, the original value itself is returned.** @param value - The value for which a reactive proxy shall be created.*/
export const toReactive = <T extends unknown>(value: T): T =>isObject(value) ? reactive(value) : value/*** Returns a reactive proxy of the object.** The reactive conversion is "deep": it affects all nested properties. A* reactive object also deeply unwraps any properties that are refs while* maintaining reactivity.** @example* ```js* const obj = reactive({ count: 0 })* ```** @param target - The source object.* @see {@link https://vuejs.org/api/reactivity-core.html#reactive}*/
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {// if trying to observe a readonly proxy, return the readonly version.if (isReadonly(target)) {return target}return createReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers,reactiveMap)
}
createReactiveObject()
看下响应性是如何实现的。
首先,在 createReactiveObject() 函数中,如果传参 target 是非对象类型的,会提示并直接返回,我们之前的例子中也观察到这种现象了;
其次,判断 target 是否是 Proxy 或者已经存在哈希表 proxyMap 中,如果是直接返回;
最后,如果传参只是一个普通的对象,我们需要使用 new Proxy() 将其转化为一个 Proxy 对象,我们知道在 Vue3 中响应性的实现正是通过 Proxy 去实现的。生成 Proxy 对象后,存入 proxyMap 中,并返回该 Proxy 对象即可。
function createReactiveObject(target: Target,isReadonly: boolean,baseHandlers: ProxyHandler<any>,collectionHandlers: ProxyHandler<any>,proxyMap: WeakMap<Target, any>
) {if (!isObject(target)) {if (__DEV__) {console.warn(`value cannot be made reactive: ${String(target)}`)}return target}// target is already a Proxy, return it.// exception: calling readonly() on a reactive objectif (target[ReactiveFlags.RAW] &&!(isReadonly && target[ReactiveFlags.IS_REACTIVE])) {return target}// target already has corresponding Proxyconst existingProxy = proxyMap.get(target)if (existingProxy) {return existingProxy}// only specific value types can be observed.const targetType = getTargetType(target)if (targetType === TargetType.INVALID) {return target}const proxy = new Proxy(target,targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers)proxyMap.set(target, proxy)return proxy
}
小结:
createReactiveObject 函数,即 reactive 函数,最终是将传参的对象转化为一个 Proxy 对象并返回,而 Vue3 中响应性的实现正是通过 Proxy 去实现的。
相关文章:
框架的源码理解——V3中的ref和reactive
最近在研究各个框架的源码,从源码角度去理解 vue3 的 reactive 和 ref API,记录下研究的成果 reactive 首先,reactive() 的参数必须是一个对象,返回值是一个 Proxy 对象,具有响应性。如果参数不是对象类型࿰…...
一台入网的电脑有6要素, 机器名,mac,ip,俺码,网关,dns,分别有什么作用
一台入网的电脑需要配置的 六大网络要素(机器名、MAC地址、IP地址、子网掩码、网关、DNS)各自承担不同的关键作用,共同确保设备能正确通信和访问网络资源。以下是它们的详细功能解析: 1. 机器名(主机名) 作…...
LED点阵屏模块
目录 1.LED点阵屏介绍 2.显示原理 3.74HC595 4.C51的sfr、sbit 5.LED点阵屏显示图形代码 第一步: 第二步: 第三步: 第四步: 第五步: 第六步: 最终代码: 模块: main.c …...
服务器性能参数分析基础:磁盘-CPU-内存
在Linux系统中,"挂载"(Mount)是指将物理存储设备(如磁盘分区)或逻辑存储卷(如LVM、网络存储)关联到文件系统目录树的特定路径节点(即挂载点),使得该…...
MoonBit 新特性:Virtual Package 虚拟包机制
Moonbit 最近新增了一项特性:virtual package。通过将一个 package 声明为虚拟包,定义好一套接口(通过 .mbti 文件声明),用户可选择具体使用哪一份实现,如不指定则使用该虚拟包的默认实现。通过这项特性&am…...
[特殊字符][特殊字符]知识库PHP版 | ChatMoneyAI宝塔面板Docker多部署
官方文档💰📚知识库PHP版 | ChatMoneyAI docker-compose2.yml 修改文件名 ports:- "181:80" #【180】为Nginx挂载主机的端口 fastcgi_pass php1:9000; #名称修改php1 container_name: nginx1 #Nginx容器名修改 container_name: php1 #P…...
来一个复古的技术FTP
背景 10年前的老代码,需要升级springboot框架,在升级过程中,测试业务流程里,有FTP的下载业务,不管测试环境如何测试,都没有成功,最后只能自己搭建一个FTP服务器,写一个ftp-demo来测试…...
ShardingSphere:查询报错:Actual table `数据源名称.表名` is not in table rule configuration
目录 简介异常信息排查原因解决 简介 1、使用ShardingSphere框架,版本为5.2.1 <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core</artifactId><version>5.2.1</version>…...
内部检测实验室数字化转型新路径 质检LIMS系统如何实现合规、效率、资质三重突破?
在高质量发展成为主旋律的今天,内部检测实验室作为企业质量管控的 “心脏”,正面临着合规标准升级、检测任务激增、设备管理复杂等多重挑战。传统管理模式下,数据追溯难、人员效率低、资质评审周期长等问题,成为制约实验室发展的核…...
PyTorch 的 F.scaled_dot_product_attention 返回Nan
“为什么 PyTorch 的 scaled_dot_product_attention 会输出 NaN?如何正确构造 Attention Mask” 引言:看似正常的 mask,为什么会引发 NaN? 在使用 F.scaled_dot_product_attention 构建跨模态或多源注意力时,我们常通…...
MySQL的触发器
本章了解一下即可,并不是很难,大家加油!!! 触发器实际上是多表关联的一个操作,无需调用,是一个自动的过程,当对数据库表中的数据执行DML操作时自动触发这个SQL片段的执行࿰…...
高可用消息队列实战:AWS SQS 在分布式系统中的核心解决方案
引言:消息队列的“不可替代性” 在微服务架构和分布式系统盛行的今天,消息队列(Message Queue) 已成为解决系统解耦、流量削峰、异步处理等难题的核心组件。然而,传统的自建消息队列(如RabbitMQ、Kafka&am…...
内存泄漏系列专题分析之十六:高通相机CamX内存泄漏内存占用分析--chi-cdk部分ION内存拆解方法
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:内存泄漏系列专题分析之十五:高通相机CamX架构chi-cdk部分ION内存管理机制CHI ImageBuffer原理 这一篇我们开始讲: 内存泄漏系列专题分析之十六:高通相机CamX内存泄漏&内存占用分析--chi-cdk部分…...
HANA数据库死锁
死锁是两个或多个事务相互交叉锁定的情况,因此任何事务都无法继续进行。 通常死锁是由应用程序设计缺陷引起的,但在主键约束的上下文中也可能存在更多的技术死锁(这种情况请参考 SAP note 2429521)。 当 HANA 数据库出现死锁时&am…...
Unity雷火UX工具插件中的本地化功能(Unity项目中文字图片多语言功能)
一、插件下载地址如下: 雷火UX工具插件下载 二、本地化功能,也就是多语言功能官方文档地址如下: 雷火UX工具本地化功能官方文档 三、UX Image组件需要注意的事项,也就是官方文档没有提到的地方。 1、UX Image组件所引用的Source Image资...
公路水运安全员B证主要考核内容有哪些
公路水运安全员B证(交安B证)是交通运输行业施工企业主要负责人和项目负责人必备的安全生产考核证书,主要考核以下内容: 一、安全生产法律法规(30%) 国家法律法规:重点考核《安全生产法》《建设…...
电缆故障常见故障及应对方法
一、常见故障类型及原因 1. 机械损伤 原因:安装时的过度牵引或弯曲、外力破坏(如施工挖损、车辆振动)、自然因素(如土地沉降导致电缆拉伸)等。 表现:绝缘层破损、金属护套裂损,可能发展为短…...
Rust 数据结构:String
Rust 数据结构:String Rust 数据结构:String什么是字符串?创建新字符串更新字符串将 push_str 和 push 附加到 String 对象后使用 运算符和 format! 宏 索引到字符串字符串在内存中的表示字节、标量值和字形簇 分割字符串遍历字符串的方法 R…...
算法基础 -- 小根堆构建的两种方式:上浮法与下沉法
小根堆构建的两种方式:上浮法与下沉法 在构建小根堆(Min-Heap)时,通常有两种常见的构建方式: 上浮建堆(逐个插入,上浮调整)下沉建堆(Heapify 自底向上,下沉…...
Sprnig MVC 如何统一异常处理 (Exception Handling)?
主要有以下几种方式来实现统一异常处理,其中 ControllerAdvice (或 RestControllerAdvice) 结合 ExceptionHandler 是最常用的方式。 1. ExceptionHandler 注解 作用: 用于标记一个方法,该方法将处理在同一个 Controller 类中抛出的特定类型…...
03、基础入门-SpringBoot的大时代背景
03、基础入门-SpringBoot的大时代背景 # Spring Boot的大时代背景 Spring Boot的出现和发展,与以下时代背景密切相关: ## 1. 微服务架构的兴起 ### 背景 随着互联网应用的复杂度增加,传统的单体架构在扩展性、维护性和团队协作方面遇到瓶…...
【51单片机中断】
目录 配置流程 1.在IE寄存器中开启总中断通道和需要的某中断通道 2.在TCON寄存器开启所用中断的触发方式 3.使用中断函数完成中断 4.若需要中断嵌套则在IP寄存器中配置 5.若需要使用串口的中断,则配置SCON寄存器 6.代码示例 配置流程 1.在IE寄存器中开启总中…...
英飞凌tle9954 GPIO
9 通用输入输出(GPIO) 9.1 功能概述 通用输入 / 输出(GPIO)由输入 / 输出驱动级和端口控制逻辑组成。GPIO 具备以下功能: 输入 / 输出端口功能(PBx) 输出状态可编程,输入状态可读。输出驱动器可编程为推挽、开漏和三态模式。输出驱动器的驱动强度和转换速率(压摆率)可…...
Linux操作系统--进程间通信(system V共享内存)
目录 1.system V共享内存 2.共享内存数据结构 3.共享内存函数 4.实例代码: 1.system V共享内存 共享内存区是最快的IPC(进程间通信)形式。一旦这样的内存映射到共享它的进程地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再…...
力扣HOT100之二叉树:102. 二叉树的层序遍历
这道题太简单了,相当于基础的模板题,但凡涉及到层序遍历一定会用到队列来实现,其他的倒没啥好说的,用两层while循环来层序遍历,外层while循环用于控制访问二叉树的每一层,而内层while循环则负责收割每一层的…...
分布式锁: Redisson 实现分布式锁的原理与技术细节
在分布式系统中,分布式锁是协调多个节点对共享资源访问的核心机制之一。Redis 作为高性能内存数据库,常被用于实现分布式锁,而 Redisson 是 Java 生态中最成熟、功能最丰富的 Redis 客户端之一,其内置的分布式锁实现被广泛应用于生…...
day22-数据结构之 栈队列
一、栈 1.1 栈的基本概念 栈是限定仅在表尾进行插入和删除操作的线性表 栈的特性:先进后出、后进先出 栈顶:允许操作的一端栈底:不允许操作的一端 栈的使用分为入栈,出栈栈分为顺序栈和链式栈 1.2 栈的基本操作 链栈示意图:最好采取头插和头…...
Oracle 批量操作脚本解析:动态执行与分批次删除
一、脚本功能概述 本文分享两段 Oracle PL/SQL 脚本,分别实现动态 SQL 执行和大表分批次删除功能,适用于数据清洗、批量操作优化等场景。通过实际案例演示语法逻辑与使用场景。 二、脚本一:动态 SQL 执行与数据清理 1. 核心逻辑 从临时表t…...
我用 CodeBuddy 开发了一个颜色命名搜索器 —— ColorNameHub 的诞生记
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 在一次整理设计稿配色时,我突然萌生了一个想法:“如果能输入一个颜色代码,就…...
强化学习算法实战:一个例子实现sarsa、dqn、ddqn、qac、a2c、trpo、ppo
简介 在学习强化学习算法:sarsa、dqn、ddqn、qac、a2c、trpo、ppo时,由于有大量数据公式的推导,觉得十分晦涩,且听过就忘记了。 但是当把算法应用于实战时,代码的实现要比数据推导要直观很多。 接下来通过不同的算法实…...
零基础玩转Apache Superset可视化部署
根据官方Quick Start Guide,你可以按照以下步骤进行部署: 1. 确认环境2. 获取代码3. 获取官方最新代码4. 启动服务5. 访问Superset Web界面6. 接入数据源 前提条件: dockerdocker compose 1. 确认环境 安装Docker和Docker Compose 确保你…...
单片机-STM32部分:18、WiFi模组
飞书文档https://x509p6c8to.feishu.cn/wiki/WFmqwImDViDUezkF7ercZuNDnve 一、WiFi模组应用 当设备需要连接网络,实现远程控制,状态监控时,就需要添加通信模组,常见的通信模组WiFi模组、2G模组、4G模组等: 我们的板卡…...
TLS 1.3黑魔法:从协议破解到极致性能调优
一、TLS协议逆向工程实验 1.1 密码学套件破解剧场 实验准备: 靶机:启用TLS 1.2的Nginx服务器 工具集:Wireshark OpenSSL s_client 定制Python脚本 实战攻击复现: # 强制使用弱加密套件连接 openssl s_client -connect exa…...
职业院校物联网安装调试员(工业数智技术)实训解决方案
一、物联网安装调试员 (1)职业定义: 利用检测仪器和专用工具,安装、配置、调试物联网产品与设备的人员。其工作任务就是要搭建数据互联的信息网络,并通过电子标签将真实的物体上网连接,并通过对各类设备的…...
mongodb用systemctl启动code=killed, signal=ABRT
参照在 Ubuntu 上安装 MongoDB Community Edition - 数据库手册 v8.0 - MongoDB Docs 安装后,sudo systemctl start mongod启动失败。 sudo systemctl status mongod 结果: mongod.service - MongoDB Database ServerLoaded: loaded (/lib/systemd/sys…...
基于51单片机和8X8点阵屏、矩阵按键的匹对消除类小游戏
目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、矩阵按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 用的是普中A2开发板,用到板上的8X8LED点阵屏和矩阵按键。 【单片机】STC89C52RC 【频率】12T11.0592MHz 效果查看/操…...
智能呼叫系统中的NLP意图理解:核心技术解析与实战
引言:当AI拿起电话时 在智能客服、电话营销等场景中,智能呼叫系统正以每年23%的增长率重塑人机交互方式。而支撑这一变革的核心技术,正是自然语言处理(NLP)中的意图理解模块。本文将深入解析意图理解的技术原理&#…...
信号灯和旋钮在接地电阻柜内的作用主要包括以下几个方面
信号灯的作用: 指示状态:信号灯用于指示接地电阻柜的工作状态,如正常运行、故障报警等。通过不同颜色的灯光(如红色表示故障,绿色表示正常)来提醒操作人员柜子的当前状态,确保及时处理潜…...
MongoDB 应用实战
1. java 原生客户端 引入maven 1 <dependencies> 2 <dependency> 3 <groupId>org.mongodb</groupId> 4 <artifactId>mongodb‐driver‐sync</artifactId> 5 <version>4.1.1</version> 6 </dependency> 7 </depende…...
Java EE初阶——wait 和 notify
1. 线程饥饿 线程饥饿是指一个或多个线程因长期无法获取所需资源(如锁,CPU时间等)而持续处于等待状态,导致其任务无法推进的现象。 典型场景 优先级抢占: 在支持线程优先级的系统中,高优先级线程可能持续…...
SpringBoot--Bean管理详解
Bean管理 Bean扫描 回顾spring: 在XML配置文件中,可以借助 <context:component-scan base-package "com.lyc"> 或者注解 ComponentScan(basePackages"com.lyc") 再springboot项目中,既没有标签,也…...
python爬虫实战训练
前言:哇,今天终于能访问豆瓣了,前几天爬太多次了,网页都不让我访问了(要登录)。 先来个小练习试试手吧! 爬取豆瓣第一页(多页同上篇文章)所有电影的排名、电影名称、星…...
探索大型语言模型(LLM)的开源学习路径:mlabonne/llm-course 深度解析
引言:为什么LLM学习需要系统化课程? 近年来,大型语言模型(Large Language Models, LLMs)彻底改变了自然语言处理领域。从GPT系列到Llama、Mistral等开源模型,掌握LLM的开发和应用能力已成为技术人员的核心竞争力。然而,LLM技术栈涵盖从理论基础到工程实践的复杂内容,如…...
IDEA怎么汉化idea中文改回英文版
第一步:点击左上角的File,然后选择Setting 第二步:Setting页面选择 Appearance & Behavior,然后展开System Settings,然后选择 Language and Region,进行修改 我操作的是2024年的版本 File->Settings -> Ap…...
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
目录 1. 创建Flutter项目 1.1使用Android Studio创建Flutter项目 1.2 使用命令行创建Flutter项目 2. Flutter项目介绍 2.1所有代码都在lib目录下编写 2.1 pubspec.yaml 依赖库/图片的引用 编辑 3. 运行项目 4. 编写mian.dart文件 4.1 使用MaterialApp 和 Scaffold两个组件…...
C++23 中的 ranges::fold_left:范围折叠算法
文章目录 1. **ranges::fold_left 的基本概念**2. **使用示例**示例 1:计算整数范围的和示例 2:计算字符串范围的连接示例 3:使用自定义函数 3. **与其他折叠算法的比较**4. **为什么需要 ranges::fold_left**5. **总结** 随着 C23 的到来&am…...
Vue2项目created不执行
Vue2项目created不执行 设置唯一值 name在 created 调用方法在 watch 中监听路由完整代码示例 设置唯一值 name 在 Vue 组件中,name 属性用于标识组件。确保每个组件的 name 属性是唯一的,这有助于在调试和开发过程中更好地识别组件。 export default …...
mysql的not exists走索引吗
在MySQL中,NOT EXISTS子句是否使用索引取决于子查询中关联字段是否建立了合适的索引。以下是关键点总结: 索引的作用: 当子查询的关联字段(例如B.a_id)存在索引(如普通B-tree索引)时&…...
红黑树实现
1.红黑树的概念 红黑树是一棵二叉搜索树,他的每个节点增加一个存储位来表示节点的颜色,可以是红丝或者黑色。通过对任何一条从根到叶子的路径上各个节点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平…...
将已打包好的aar文件,上传到 Coding 的 Maven 仓库
将已打包好的aar文件,上传到 Coding 的 Maven 仓库。 在android stuio项目的build.gradle 进行上传。 编写代码 plugins {id maven-publish }// 配置要上传的本地 AAR 文件 def aarFile file(D:\\mylibrary-1.0.0.aar)publishing {publications {mavenAar(MavenP…...