API 26 使用 SemVer 后,版本判断代码还安全吗?一次兼容性代码检查【鸿蒙心迹】

👋 你好,欢迎来到我的博客!我是【菜鸟学鸿蒙】
我是一名在路上的移动端开发者,正从传统“小码农”转向鸿蒙原生开发的进阶之旅。为了把学习过的知识沉淀下来,也为了和更多同路人互相启发,我决定把探索 HarmonyOS 的过程都记录在这里。
🛠️ 主要方向:ArkTS 语言基础、HarmonyOS 原生应用(Stage 模型、UIAbility/ServiceAbility)、分布式能力与软总线、元服务/卡片、应用签名与上架、性能与内存优化、项目实战,以及 Android → 鸿蒙的迁移踩坑与复盘。
🧭 内容节奏:从基础到实战——小示例拆解框架认知、专项优化手记、实战项目拆包、面试题思考与复盘,让每篇都有可落地的代码与方法论。
💡 我相信:写作是把知识内化的过程,分享是让生态更繁荣的方式。
如果你也想拥抱鸿蒙、热爱成长,欢迎关注我,一起交流进步!🚀
一、一个容易被忽略的兼容性问题
应用升级到 HarmonyOS 7 时,开发者通常会关注新接口、废弃接口和 UI 行为变化,但有一类代码同样需要重新检查:系统 API 版本判断。
从 API 26.0.0 开始,HarmonyOS API 版本号正式采用语义化版本(Semantic Versioning,简称 SemVer)格式,由主版本号、次版本号和修订版本号组成。华为官方明确提醒,版本号格式变化后,应用中涉及 API 兼容性判断的代码也需要进行相应优化。
这个变化看起来只是版本号多了几个字段,但如果项目中存在字符串比较、固定长度截取、手动解析版本号等逻辑,原有判断就可能不再适用。
本文聚焦一个具体的开发场景:应用需要根据设备支持的 API 版本,决定是否启用某项新功能。围绕这个场景,检查旧版本判断可能存在的问题,设计一段可复用的版本比较代码,再将检查范围扩展到工程配置、三方库和兼容性测试。
本文的代码属于基于官方版本规则设计的最小检查示例,不涉及真实项目故障或未经验证的真机测试结果。
二、API 26.0.0 前后,版本格式究竟发生了什么?
1. API 26.0.0 之前的版本格式
按照华为开发者联盟《版本号格式调整说明》,API 26.0.0 之前采用的版本格式为:
X.Y.Z(N)
其中,X 表示主版本号,Y 表示次版本号,Z 表示修订版本号,N 表示 OpenHarmony 底座 API Level。
原有格式将 HarmonyOS API 版本信息与底座 API Level 信息放在同一个版本表达式中。因此,处理历史版本时,不能直接假设版本号只有三个由小数点分隔的字段。
2. 从 API 26.0.0 开始采用 SemVer
新版本格式调整为:
X.Y.Z
官方定义的三个字段分别承担不同的版本语义:
| 字段 | 官方定义 |
|---|---|
| X:主版本号 | 包含大量新功能和重要变更,可能需要应用进行适配 |
| Y:次版本号 | 包含新功能,原则上向后兼容,但特定情况下可能引入不兼容变更 |
| Z:修订版本号 | 主要包含问题修复和小改进,保持向后兼容 |
例如:
26.0.0
26.0.1
26.1.0
27.0.0
这里的 26.0.1、26.1.0 和 27.0.0 是官方用于说明版本格式的示例,不能据此认为这些版本都已经发布,也不能将其当作实际的系统版本规划。
3. HarmonyOS 7 与 API 26.0.0 的关系
华为官方《应用升级适配指导——向 26.0.0 升级》明确将 HarmonyOS 7.0 与 API 26.0.0 对应,并建议开发者升级开发套件、评估 API 变更及执行新旧版本兼容性测试。
不过,系统 API 版本、应用的最低兼容 SDK 版本,以及应用自身的版本号,是不同的概念。
例如,应用自身的 versionName 即使采用 1.2.3 这样的格式,也不代表它可以用于判断设备是否支持 API 26.0.0 的系统能力。
真正需要检查的是:现有代码使用的版本信息究竟来自哪里,它的字段含义是什么,以及当前判断是否正确处理了版本边界。
三、哪些版本判断代码需要重点检查?
对于已经维护了一段时间的 HarmonyOS 工程,可以先从版本判断的用途入手,而不是直接全局替换版本号。
重点检查以下几类代码:
| 检查对象 | 需要确认的问题 |
|---|---|
| 系统 API 版本判断 | 是否将完整版本号当作普通字符串比较 |
| 版本号解析工具 | 是否假设版本号只有固定长度或固定字段数量 |
| 新功能开关 | 是否正确处理首次支持版本及后续版本 |
| 兼容性降级逻辑 | 是否在不支持新接口的设备上进入旧实现 |
| 工程 SDK 配置 | 最低兼容版本、目标版本与实际调用的 API 是否匹配 |
| 三方库内部判断 | 是否由三方库自行解析系统版本或调用较新的 API |
还需要区分两种情况。
一种是判断某个系统接口是否可用。例如,某接口从 API 26.0.0 开始支持,应用又需要兼容更早版本,此时就要根据官方接口说明进行兼容性处理。
另一种是判断某个接口的行为是否发生变化。即使一个接口在多个版本中都存在,它的行为也可能发生调整,不能仅凭接口存在就认定新旧版本行为一致。
华为官方升级指导同样将新 API 的可用性和已有 API 的行为变化作为两类不同的兼容性问题,要求开发者分别评估。
1. 不要把主版本号当成完整版本号
假设业务定义了这样一个功能开关:
设备 API 版本达到 26.1.0 时,
启用功能 A。
这里的 26.1.0 仅用于演示版本比较逻辑,并不代表功能 A 是真实存在的 HarmonyOS 系统能力。
如果代码只判断主版本号:
majorVersion >= 26
那么以下版本都会满足条件:
26.0.0
26.0.1
26.1.0
27.0.0
但是按照前面的业务条件,26.0.0 和 26.0.1 不应该启用功能 A。
这说明:只比较主版本号是否正确,取决于当前功能的最低版本要求。
如果官方接口的起始版本就是 26.0.0,主版本判断可能足以覆盖这一特定条件;如果接口需要更高的次版本或修订版本,就必须继续比较相应字段。
2. 不要用普通字符串比较完整版本号
下面是一段值得检查的代码:
let currentVersion: string = '26.10.0';
let requiredVersion: string = '26.2.0';
let supported: boolean =
currentVersion >= requiredVersion;
这段代码比较的是字符串,而不是三个数值字段。
对于示例中的两个版本,按数值比较:
26.10.0 > 26.2.0
但普通字符串比较会逐字符进行,比较到次版本号的首位时,字符 '1' 排在 '2' 之前,最终可能得到与版本数值关系相反的判断结果。
再看另一种写法:
let currentVersion: string = '26.0.10';
let requiredVersion: string = '26.0.2';
let supported: boolean =
currentVersion >= requiredVersion;
从数值关系看,修订版本 10 大于修订版本 2,但普通字符串比较无法正确表达这一关系。
这里的问题不是 SemVer 本身存在歧义,而是将版本号当作普通文本处理,丢失了三个字段的数值语义。
因此,涉及完整版本号的大小比较时,建议明确拆分主版本号、次版本号和修订版本号,再按顺序进行数值比较。
3. 小心固定位置截取
下面的写法同样值得检查:
let version: string = '26.10.0';
let major: string = version.substring(0, 2);
let minor: string = version.substring(3, 4);
这种实现隐含了两个前提:主版本号始终占两位,次版本号始终占一位。
只要字段位数发生变化,截取结果就可能不再对应完整的版本字段。
类似的问题还包括:
version.startsWith('26.0')
它可以作为某些固定格式字符串的前缀判断,但不适合作为通用的 API 版本大小比较方法。
如果代码需要表达的是“不低于某个 API 版本”,应该使用版本字段比较,而不是字符串前缀匹配。
四、设计一个最小可复现的版本判断场景
为了将问题集中在版本比较本身,这里设计一个独立的检查场景。
假设应用包含一个需要按版本控制的功能模块:
-
最低要求版本为
26.1.0; -
当前版本低于要求时,不启用该模块;
-
当前版本达到或高于要求时,允许继续执行后续检查;
-
遇到无法识别的版本号时,不直接放行。
上述规则属于本文自行设计的演示业务条件,不对应任何特定 HarmonyOS 系统接口。
这个示例不依赖 ArkUI 页面,也不需要申请系统权限。它只验证一个问题:给定两个符合新格式的版本号,能否正确判断它们的先后关系。
需要特别说明的是,下面的工具函数仅处理 API 26.0.0 开始采用的三段式版本格式,不负责将历史版本 X.Y.Z(N) 自动转换成新格式。
对于历史版本,应依据实际使用的系统接口及其官方返回值定义确定比较方式,而不是将不同格式直接混合处理。
五、核心代码:将版本号转换为数值字段后比较
1. 定义版本数据结构
先定义一个用于保存三个版本字段的结构:
interface ApiVersion {
major: number;
minor: number;
patch: number;
}
三个字段分别对应 SemVer 格式中的 X、Y、Z。
这里使用的是普通的数据结构,没有调用 HarmonyOS 系统 API,也没有引入额外 Kit。
2. 解析版本号
接下来编写一个独立的版本解析函数。
这段代码的目标不是获取设备版本,而是将已经得到的三段式版本字符串转换成可比较的数值结构。
function parseApiVersion(
version: string
): ApiVersion | null {
const parts: string[] = version.split('.');
if (parts.length !== 3) {
return null;
}
const major: number = Number(parts[0]);
const minor: number = Number(parts[1]);
const patch: number = Number(parts[2]);
const values: number[] = [
major,
minor,
patch
];
for (let i = 0; i < values.length; i++) {
const value: number = values[i];
if (!Number.isSafeInteger(value) || value < 0) {
return null;
}
const text: string = parts[i];
if (text.length === 0 ||
text.trim() !== text ||
text.startsWith('+') ||
text.startsWith('-') ||
text.startsWith('0x')) {
return null;
}
for (let j = 0; j < text.length; j++) {
const character: string = text[j];
if (character < '0' || character > '9') {
return null;
}
}
}
return {
major: major,
minor: minor,
patch: patch
};
}
这段代码重点处理了几个问题:
一是通过 split('.') 分离字段,而不是按照固定位置截取字符串。
二是要求输入恰好包含三个字段,避免将缺失字段或多余字段的字符串直接当成合法版本。
三是检查各字段是否属于非负安全整数,并限制字段内容为十进制数字,防止空字符串、负数、十六进制表示等输入被意外接受。
这里采用的是针对官方 X.Y.Z 格式设计的基础解析规则,并非对完整 SemVer 规范中所有扩展语法的通用实现。
如果输入包含历史版本格式、预发布标识或者其他额外字段,函数会返回 null,由调用方决定下一步如何处理。
3. 按照主、次、修订版本依次比较
完成解析以后,真正的比较逻辑就比较清晰了。
function compareApiVersion(
current: ApiVersion,
required: ApiVersion
): number {
if (current.major !== required.major) {
return current.major > required.major ? 1 : -1;
}
if (current.minor !== required.minor) {
return current.minor > required.minor ? 1 : -1;
}
if (current.patch !== required.patch) {
return current.patch > required.patch ? 1 : -1;
}
return 0;
}
函数返回值的含义如下:
| 返回值 | 含义 |
|---|---|
| 1 | 当前版本高于要求版本 |
| 0 | 两个版本相同 |
| -1 | 当前版本低于要求版本 |
比较顺序不能随意调整。
只有主版本号相同时,才需要继续比较次版本号;只有主版本号和次版本号都相同时,修订版本号才会影响最终结果。
这种写法不需要将版本号拼接成浮点数,也不需要将不同字段组合成一个人为定义的整数。
例如,26.10.0 与 26.2.0 的比较,会在次版本号处得到正确的数值关系,而不受字段位数影响。
4. 封装最低版本检查
有了前面的两个函数,就可以进一步封装功能开关需要的版本判断。
function isApiVersionSupported(
currentVersion: string,
requiredVersion: string
): boolean {
const current: ApiVersion | null =
parseApiVersion(currentVersion);
const required: ApiVersion | null =
parseApiVersion(requiredVersion);
if (current === null || required === null) {
return false;
}
return compareApiVersion(current, required) >= 0;
}
这里采用了一个保守处理原则:只要版本号无法解析,就返回 false。
它适合本文的演示场景,因为无法确认版本是否满足要求时,不应该贸然启用依赖较新版本的功能。
但这并不意味着所有无法识别的版本都低于要求版本。解析失败与版本过低是两种不同情况,正式项目如果需要日志、错误提示或兼容性诊断,应该进一步区分。
5. 使用示例
将前面的函数放在同一个 ArkTS 工具文件中,可以按照下面的方式组织版本判断:
const currentVersion: string = '26.1.0';
const requiredVersion: string = '26.1.0';
const supported: boolean =
isApiVersionSupported(
currentVersion,
requiredVersion
);
if (supported) {
// 满足演示业务定义的最低版本条件
// 继续执行其他功能可用性检查
} else {
// 未满足最低版本条件,或版本格式无法识别
// 保持该功能关闭
}
这段代码只是版本比较工具的调用示例。
currentVersion 是用于演示的输入值,并非实际读取到的设备版本。
在真实 HarmonyOS 工程中,不能将这里的硬编码字符串当成设备 API 版本。需要使用官方文档确认的系统版本获取方式取得相应信息,并核对返回值的字段含义,再接入版本判断逻辑。
同样,版本号达到要求只是启用具体系统能力的必要检查之一。接口是否可用,还要结合其官方起始版本、系统能力、权限、设备范围和其他使用约束判断。
六、实际项目中,应该怎样按照官方兼容性指导修改?
前面的示例解决了完整版本号的比较问题,但真正迁移到 HarmonyOS 工程时,还需要结合官方兼容性机制处理。
1. 区分运行设备版本和工程 SDK 配置
华为官方升级指导涉及三个需要重点关注的 SDK 配置字段。
| 配置字段 | 作用 |
|---|---|
| compileSdkVersion | 指定应用编译时使用的 SDK 版本 |
| compatibleSdkVersion | 指定应用能够兼容的最低 SDK 版本 |
| targetSdkVersion | 指定应用面向的目标 SDK 版本,部分 API 行为变化可通过该字段实施兼容性隔离 |
三者不能混为一谈。
例如,应用使用 API 26.0.0 SDK 编译,并不代表所有安装该应用的设备都支持 API 26.0.0 中引入的接口。
如果工程需要兼容更早的设备,就要检查新接口是否受最低兼容版本限制,并针对不同版本采取适当的兼容性处理。
官方还提醒,升级 DevEco Studio 后,如果项目没有显式配置 targetSdkVersion,该字段可能随开发工具版本自动更新;默认使用的编译 SDK 版本也可能发生变化。
因此,升级工具链后不能只检查业务代码,还应该复核工程配置。
2. 不要将系统版本号强制转换为浮点数
假设某段历史代码将版本号处理为:
TypeScript
let version: number = Number('26.1');
这种表示方式无法完整表达三段式版本信息。
例如:
26.1.0
26.1.1
26.1.10
如果仅保留 26.1,修订版本的信息就丢失了。
同样,不应该将 26.10.0 简单转换成 26.10,再依赖浮点数大小进行版本比较。
如果业务需要比较完整的 SemVer 版本,应采用前文的三个整数字段进行判断。
不过,这并不意味着所有 HarmonyOS 版本接口都应该改成字符串解析。
如果官方接口直接返回数值型 API Level,应按照该接口的正式定义使用数值,不需要额外拼接出一个字符串再解析。
真正需要修改的是错误的版本判断方式,而不是将所有版本信息统一转换成 SemVer 字符串。
3. 版本判断不能替代接口兼容性保护
假设某个新接口从 API 26.0.0 开始支持,而应用的最低兼容版本早于该版本。
即使编译工具能够识别新接口,也不能据此认为旧设备一定能够调用该接口。
应用应根据官方 API 参考中的起始版本、设备支持范围、模型限制及其他约束进行判断,并采用官方兼容性指导允许的方式组织调用代码。
另一方面,部分已有接口在新版本中可能发生行为变化。这种情况不能简单地通过增加一个版本比较函数解决,还需要阅读对应的行为变更说明。
华为官方提供了 DevEco Studio 的 API Change Assistant,可用于检查项目中使用的 ArkTS API 或 C API 在指定 SDK 版本范围内是否存在行为变化,并定位相关代码。
对于涉及系统 API 的升级工作,可以将版本判断检查与 API 变更检查结合起来进行。
4. 历史版本不能直接套用三段式解析函数
前面的 parseApiVersion() 明确要求输入恰好包含三个字段。
对于官方旧版本格式:
X.Y.Z(N)
不能直接将其传入函数,并认为它与新格式具有相同的比较语义。
更不应该为了让解析通过,直接删除括号及其中的内容,再把剩余字符串当作等价的新版本号。
如果项目仍然需要兼容采用旧版本格式的设备,建议将历史版本处理与新版本处理分开。
具体迁移时,应确认项目实际使用的设备版本接口返回什么类型、如何表达历史版本,以及相应接口在目标 API 版本中的定义。
在未确认这些条件之前,不应该设计一个通用的字符串转换公式来替代官方的版本兼容性规则。
七、建立不同版本的测试用例
版本比较工具的价值不在于代码有多少行,而在于能否正确处理边界条件。
针对本文设计的三段式版本比较函数,可以先建立下面这组测试用例。
| 当前版本 | 最低要求版本 | 预期结果 | 检查目标 |
|---|---|---|---|
| 26.0.0 | 26.0.0 | true | 版本相同 |
| 26.0.1 | 26.0.0 | true | 修订版本更高 |
| 26.1.0 | 26.0.0 | true | 次版本更高 |
| 26.0.0 | 26.1.0 | false | 次版本不足 |
| 26.0.1 | 26.0.2 | false | 修订版本不足 |
| 26.0.10 | 26.0.2 | true | 修订版本多位数比较 |
| 26.10.0 | 26.2.0 | true | 次版本多位数比较 |
| 27.0.0 | 26.1.0 | true | 主版本更高 |
| 26.0 | 26.0.0 | false | 字段缺失 |
| 26.0.0.1 | 26.0.0 | false | 字段数量不符合要求 |
| abc | 26.0.0 | false | 非法版本字符串 |
表中的 true 和 false 是按照本文演示函数定义推导出的预期结果,不代表已经在 HarmonyOS 设备上实际运行并获得了这些结果。
其中,26.10.0 与 26.2.0 这组用例主要用于检查多位数字段的比较逻辑,不代表对应版本已经正式发布。
1. 先验证纯版本比较逻辑
可以将上表中的输入依次传入 isApiVersionSupported(),检查实际返回值是否与预期一致。
尤其要关注:
-
主版本相同、次版本不同的情况;
-
次版本相同、修订版本不同的情况;
-
多位数版本字段;
-
非法输入与字段缺失;
-
当前版本恰好等于最低要求版本。
这一步不依赖真实设备版本,只验证自定义工具函数的逻辑。
2. 再验证设备上的兼容性行为
纯版本比较正确,并不能保证整个应用的系统 API 兼容性已经满足要求。
华为官方升级指导要求,在应用基于新 SDK 编译后,对运行旧版本和新版本系统的设备开展兼容性验证,覆盖应用的相关功能。官方同时提供了 AppGallery Connect 云调试作为多设备、多 API 版本验证的一种方式。
实际项目可以建立以下验证矩阵:
| 验证环境 | 验证重点 |
|---|---|
| 应用最低兼容版本对应的设备 | 基础功能是否可用,较新接口是否被正确保护 |
| API 26.0.0 设备 | API 26.0.0 相关适配及原有功能 |
| 后续可用的正式 API 版本设备 | 次版本或修订版本相关的功能条件 |
| 不同版本的应用构建包 | 目标 SDK 配置变化是否影响兼容性行为 |
这里不能把版本比较工具的预期结果直接当作应用在真实设备上的测试结果。
正式验证时,应记录设备实际 API 版本、应用构建配置、测试功能和观察到的行为。
对于受特定设备类型限制的系统能力,还需要按照该接口的官方支持范围选择相应设备,不能仅凭 API 版本号判断所有设备都支持该能力。
八、三方库是否也依赖版本判断?
检查版本判断代码时,不能只关注应用自身的 ArkTS 文件。
如果项目集成了三方 HAR、HSP 或其他形式的依赖,还需要评估这些依赖是否涉及 HarmonyOS 系统 API 的版本兼容性。
华为官方升级指导明确将三方 HAR 和集成态 HSP 纳入应用升级的兼容性评估范围。
对于具体的三方库,可以从以下几个方面检查。
第一,检查是否存在系统版本判断。
如果能够取得依赖源码,可以搜索其版本获取及比较逻辑,重点关注是否将完整版本号当作普通字符串比较,或者是否使用固定位置截取版本字段。
如果依赖只提供编译产物,无法查看内部实现,就需要参考库维护者提供的官方兼容性说明或发布记录。
第二,检查依赖是否调用较新的系统接口。
即使应用自身没有直接调用某个新 API,也不能据此认定三方库不存在相关兼容性要求。
应结合三方库实际使用的系统能力,检查对应 HarmonyOS 官方 API 参考及起始版本。
第三,检查依赖更新是否改变了最低兼容要求。
升级三方库时,不应只确认它是否适用于当前编译 SDK,还要检查它是否仍然支持应用所声明的最低兼容版本。
第四,区分库自身版本与系统 API 版本。
三方库的 1.2.3 版本号属于该库自身的版本管理体系,不能直接用于判断设备是否支持 HarmonyOS API 26.0.0。
本文设计的三段式比较工具可以用于理解版本数值关系,但不能代替三方库自身的兼容性规则。
九、一次升级检查应该按照什么顺序进行?
结合官方升级指导与前面的最小示例,可以将整个检查流程整理为几个连续步骤。
01确定版本范围
记录当前编译 SDK、目标 SDK、最低兼容 SDK,以及需要覆盖的设备 API 版本。
02检查版本信息来源
区分系统 API 版本、应用自身版本和三方库版本,核对对应接口的返回类型与字段含义。
03定位并修改版本判断
检查字符串比较、固定长度截取、仅比较主版本号和历史格式解析等逻辑。
04评估 API 兼容性
依据官方 API 参考检查接口起始版本、行为变化和使用约束,并结合 API Change Assistant 排查。
05检查三方依赖
确认依赖自身的版本判断、系统 API 调用和最低兼容要求。
06执行兼容性验证
先检查版本比较函数的边界用例,再在新旧版本设备上验证实际功能。
其中,针对 API 变更的检查可以使用 DevEco Studio 提供的 API Change Assistant。
按照官方文档,在 DevEco Studio 中通过 Tools > API Change Assistant 打开工具后,可以选择需要比较的 SDK 版本范围并启动扫描。
扫描结果可用于查看 API 行为变化、定位相关代码,以及查阅对应的适配指导。
需要注意,API 行为变化扫描与本文的自定义字符串比较检查并不是同一件事。
即使完成了 API Change Assistant 扫描,也仍然需要检查应用中自行编写的版本解析与比较逻辑,不能认为前者已经覆盖了所有业务层面的版本判断问题。
十、开发经验总结:不要只改版本号,要改判断依据
这次围绕 API 26.0.0 版本格式变化设计的兼容性代码检查,可以归纳出四个适合实际项目复用的处理原则。
一是区分版本号的来源。 API 版本号、应用版本号和三方库版本号即使采用相似的文本格式,也不能直接混用。使用版本信息前,应先核对官方接口或对应库文档对该字段的定义。
二是根据业务要求选择比较粒度。 如果功能只要求特定主版本,可以采用符合该要求的主版本判断;如果要求具体的次版本或修订版本,就必须继续比较相应字段。完整 SemVer 字符串不应该直接依赖普通字符串大小比较。
三是不要让自定义解析函数替代官方兼容性机制。 本文的三段式工具函数只能解决完整版本号的比较问题,不能决定某个系统 API 是否在特定设备上可用。真正调用系统能力时,仍需依据官方 API 参考和兼容性指导进行处理。
四是将版本比较检查纳入升级流程。 升级 SDK 时,除了新增和废弃接口,还应检查旧版本判断逻辑、工程配置和三方库依赖,并在不同系统版本设备上完成实际兼容性验证。
API 26.0.0 使用 SemVer 后,原有版本判断代码并不是全部失效。需要重点处理的是那些依赖旧格式假设、错误比较版本字段或者混淆不同版本信息的实现。
对于正在升级 HarmonyOS 7 的项目,不妨检查一下现有代码:是否存在一段已经使用多年的版本判断逻辑,看起来只是简单比较两个数字或字符串,却隐含了对系统版本格式的固定假设?
这类代码很适合作为升级前兼容性检查的一个独立项目。
📝 写在最后
如果你觉得这篇文章对你有帮助,或者有任何想法、建议,欢迎在评论区留言交流!你的每一个点赞 👍、收藏 ⭐、关注 ❤️,都是我持续更新的最大动力!
我是一个在代码世界里不断摸索的小码农,愿我们都能在成长的路上越走越远,越学越强!
感谢你的阅读,我们下篇文章再见~👋
✍️ 作者:菜鸟不学编程
🧵 本文原创,转载请注明出处。
更多推荐



所有评论(0)