HarmonyOS开发:NodeJs脚本实现组件化动态切换
前言
上篇文章,我们使用NodeJs脚本完成了HarmonyOS项目的组件化运行,但是由于脚本是基于4.0.0.400版本的DevEco Studio开发的,可能在配置文件的修改上有些许差距,那么遇到这种情况怎么办,一种是再写一套针对性的脚本文件或者在原有的脚本中增加配置版本参数,第二种就是自己搞一个,俗话说,授人以鱼不如授人以渔,索性这篇文章,就把上篇的脚本,是如何实现的,给大家阐述一下,这样,大家就可以自己操作了。
分析需求
需求的总体概括就非常的简单,让动态共享包的模块,在运行包和动态共享包之间可以动态的切换,省去人工配置的步骤,由上篇文章,我们已经得知,动态共享包和运行包之间的区别,主要来源于三处,分别是hvigorfile.ts文件、module.json5文件和缺少入口ability。
首先,肯定需要一个可以控制的开关,利用这个开关,判断是否要进行模块的动态切换,如果需要切换,那么就执行动态共享包切换运行包,否则就还原,大致流程如下:
梳理模板
无论是由动态共享包切换为运行包,还是由运行包切换为动态共享包,我们改变的都是配置文件,也就是上述中存在差异的那三个文件,文件的内容,如何来回的更改呢,当然了可以设置统一的内容,只更改区别之处,但是为了直观,方便的查看和修改,无疑使用模版是比较简单的。
首先准备好两份文件,一份是动态共享包,一份是运行包,切换的时候,直接选择不同的模版即可。
动态共享包模版
动态共享包,需要提供两个模版即可,分别是hvigorfile.ts文件和module.json5文件。
1、hvigorfile.ts
import { hspTasks } from '@ohos/hvigor-ohos-plugin';export default {system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}
2、module.json5
{"module": {"name": "mine","type": "shared","description": "$string:shared_desc","deviceTypes": ["phone","tablet"],"deliveryWithInstall": true,"pages": "$profile:main_pages"}
}
运行包模版
运行包除了两个配置文件不同,还必须有Ability,作为主入口,这是必不可少的。
1、hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';export default {system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}
2、module.json5
{"module": {"name": "entry","type": "entry","description": "$string:module_desc","mainElement": "EntryAbility","deviceTypes": ["phone","tablet"],"deliveryWithInstall": true,"installationFree": false,"pages": "$profile:main_pages","abilities": [{"name": "EntryAbility","srcEntry": "./ets/entryability/EntryAbility.ts","description": "$string:EntryAbility_desc","icon": "$media:icon","label": "$string:EntryAbility_label","startWindowIcon": "$media:icon","startWindowBackground": "$color:start_window_background","exported": true,"skills": [{"entities": ["entity.system.home"],"actions": ["action.system.home"]}]}]}
}
3、Ability
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');}onDestroy() {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');}onWindowStageCreate(windowStage: window.WindowStage) {// Main window is created, set main page for this abilityhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');windowStage.loadContent('pages/Index', (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');});}onWindowStageDestroy() {// Main window is destroyed, release UI related resourceshilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');}onForeground() {// Ability has brought to foregroundhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');}onBackground() {// Ability has back to backgroundhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');}
}
技术实现
1、创建配置文件
第一步,书写开关,也就是配置文件,当然了配置文件具体如何定义,看自己安排,无论何种形式展现,该有的参数一定要有,比如是否要开启组件化以及开启组件化的模块名字,至于其他的参数,可以根据需要进行添加,目前我定义的配置文件如下,具体的解释,都有注释,上篇文章中也做了一系列的解读。
#组件化配置文件
#是否开启组件化
startModule=false
#开启的组件名字,开启后,当前的组件可以独立运行
startModuleName=
#上述组件开启后,其他非必要组件是否改为动态包模式,默认不改变
startOtherShared=false
#过滤组件名字,永远不会独立运行,以应为逗号作为分割
filterModuleName=
#当前脚本默认加载的页面,默认不填是Index.ets
loadPage=
配置文件,这里我自定义了后缀,具体是什么文件都所谓,主要的是文件里的内容。
有了配置文件之后,我们就可以根据配置文件来一层一层的实现相关逻辑。
2、初始化项目
nodeJs环境,在安装DevEco Studio的时候就已经配置完成,检验是否安装,可以在命令行中执行如下命令:
node -v
如果正常能显示版本号,则安装成功。
在需要创建脚本的目录,执行初始化操作:
npm init
具体的步骤解释:
package name 包名,也就是工程名,默认是括号中的内容
version:版本号,默认是括号中的内容
description:描述信息
entry point:入口文件名,默认是括号中的内容
test command:测试命令
git repository:git仓库地址
keywords: 密码
author: 作者名字
license: (ISC)许可证
一路按照相关提示,执行下一步即可,其实就是生产了一个json配置文件,大家也可以自己手动创建。
执行完毕之后,就会在当前目录,创建一个json文件。
json文件内容:
3、创建执行js文件
这个js文件就是我们所有逻辑的书写地方,为了能够让js文件可以正常运行,我们需要在package.json文件里进行配置,如下:
以后执行脚本的时候,直接在命令行中,执行npm run module即可,我们先简单的输出一个“Hello world”,首先在module.js里进行打印,如下所示:
执行命令结果:
3、完成最后的逻辑
由于需要对文件进行操作,这里使用到了Node.js中的核心模块fs,一句话介绍,fs模块提供了丰富的函数和方法,可以进行文件的读取、写入、复制、删除等操作,同时也支持目录的创建、遍历和修改等操作,如果你想要更详细的了解,可以查看我的往期文章,或者在网上搜索也行,有大量的资料存在。
1)、读取配置文件
所有的功能实现都是基于配置文件,所以配置文件里的参数至关重要,也是程序的第一步,读取配置文件,逐项拿到设置的相关参数,并记录下来。
//读取文件信息let path = require('path');let dirName = path.join(__dirname); //获取跟目录try {//读取配置文件,查找对应的配置信息let data = fs.readFileSync(dirName + "/module.harmony", 'utf-8');var startModule;var startModuleName;var filterModuleName;var startOtherShared;var loadContentPage;data.split(/\r?\n/).forEach((line, position) => {if (position === 2) {let open = line.split("=")[1];startModule = open.toString();}if (position === 4) {let moduleName = line.split("=")[1];startModuleName = moduleName.toString();}if (position === 6) {let otherName = line.split("=")[1];startOtherShared = otherName.toString();}if (position === 8) {let filterName = line.split("=")[1];filterModuleName = filterName.toString();}if (position === 10) {//load的页面信息let loadPage = line.split("=")[1];loadContentPage = loadPage.toString();}});//开启组件化之后,单独的模块可以独立运行//不开启组件化,那么entry可以独立运行,其他均不可,需要一一更改配置文件traverseFolder(dirName, startModule.indexOf("true") !== -1,startModuleName, startOtherShared, filterModuleName, loadContentPage);} catch (e) {console.log("发生了错误,请检查配置文件是否存在,或反馈至AbnerMing");}
2)、动态修改配置文件信息
根据配置文件信息,进行组件化运行,也就是动态共享包切换为运行包,如何切换,拿到差异性文件,然后读取模版信息,进行写入即可。
需要注意的是,动态共享包,切换为运行包,需要动态创建ability,除此之外,关于组件的名字,ability名字,尽量和组件保持一致。
运行包切换为动态共享包,也是读取配置文件,然后进行写入即可。
function traverseFolder(folderPath, isModule, startModuleName,startOtherShared, filterModuleName, loadContentPage) {const items = fs.readdirSync(folderPath);items.forEach(item => {let dir = folderPath + "/" + item;const stats = fs.statSync(dir);if (stats.isDirectory()) {let hvigorFilePath = dir + "/hvigorfile.ts";fs.readFile(hvigorFilePath, "utf8", (err, dataStr) => {if (err) {return;}if (isModule) {//开启组件化//把当前的组件改为运行状态if (item == startModuleName) {let moduleName = item.substring(0, 1).toUpperCase()+ item.substring(1, item.length)//修改为可运行状态let entryHvigorFile = getEntryHvigorFile();//读取string.json文件,增加labellet jsonName = dir + "/src/main/resources/base/element/string.json";fs.readFile(jsonName, "utf8", (err, dataStr) => {if (err) {return;}let obj = JSON.parse(dataStr);let array = obj["string"];let label = { "name": "shared_label", "value": item };let isSharedLabel = false;for (var i = 0; i < array.length; i++) {let name = array[i]["name"];if (name == "shared_label") {isSharedLabel = true;break;}}if (!isSharedLabel) {array.push(label);}writeContent(jsonName, JSON.stringify(obj));//进一步更改json5文件let json5 = dir + "/src/main/module.json5";writeContent(json5, getEntryModuleJson5(item, moduleName));});if (loadContentPage == null || loadContentPage == "") {//为空的时候才去创建//创建Index.ets文件let indexPath = dir + "/src/main/ets/pages";const indexItem = fs.readdirSync(indexPath);let isHaveIndex = false;indexItem.forEach(item => {if (item == "Index.ets") {//证明存在isHaveIndex = true;}});if (!isHaveIndex) {//不存在,就要去创建writeContent(indexPath + "/Index.ets", getIndex());}}//创建Ability文件let etsPath = dir + "/src/main/ets/" + item + "ability/" + moduleName + "Ability.ts";fs.mkdir(dir + "/src/main/ets/" + item + "ability", function (err) {if (err) {writeContent(etsPath, getAbility(moduleName, loadContentPage));return;}//写入文件writeContent(etsPath, getAbility(moduleName, loadContentPage));});} else {//非当前的组件,需要改为动态包模式吗,根据配置文件来改变,有两种是永远不能改变的if (item != "entry" && filterModuleName.indexOf(item) == -1 && startOtherShared) {//把其他的模块都改成动态包,不能运行let moduleJson5 = getSharedModuleJson5(item);let hvigorFile = getSharedHvigorFile();writeContent(hvigorFilePath, hvigorFile);writeContent(dir + "/src/main/module.json5", moduleJson5);}}} else {//主模块和需要过滤的模块不进行动态包设置if (item != "entry" && filterModuleName.indexOf(item) == -1) {//把其他的模块都改成动态包,不能运行let moduleJson5 = getSharedModuleJson5(item);let hvigorFile = getSharedHvigorFile();writeContent(hvigorFilePath, hvigorFile);writeContent(dir + "/src/main/module.json5", moduleJson5);}}});}});
}
相关总结
由于逻辑比较简单,完整的逻辑,大家可以查看源码:
https://gitee.com/abnercode/harmony-os-module
由于开发环境的不同,配置文件信息也有所不同,无非就是更改的模版不一样,只需要在脚本中,换成你的环境下的配置文件内容即可。
相关文章:

HarmonyOS开发:NodeJs脚本实现组件化动态切换
前言 上篇文章,我们使用NodeJs脚本完成了HarmonyOS项目的组件化运行,但是由于脚本是基于4.0.0.400版本的DevEco Studio开发的,可能在配置文件的修改上有些许差距,那么遇到这种情况怎么办,一种是再写一套针对性的脚本文…...

基于springboot实现就业信息管理系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现就业信息管理系统演示 摘要 随着信息化时代的到来,管理系统都趋向于智能化、系统化,就业信息管理系统也不例外,但目前国内仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大,人…...
Vue组件的本质和手写通过render渲染函数渲染组件
1.组件的本质 组件就是一组 DOM 元素的封装,本质就是一个对象 (mounted函数中打印一下组件即可看到打印的是一个对象) 如何利用javascript对象来描述一个组件? const MyComponent {render() {return {tag: div,props: {onClick: () > alert(hell…...

【优选算法系列】第一节.双指针(283. 移动零和1089. 复写零)
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:优选算法系列 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!!…...

Vue(uniapp)父组件方法和子组件方法执行优先顺序
涉及到的知识点:watch监控:先看问题,父组件从后端通过$ajax获取数据,在将父组件将值传输给子组件,使用子组件使用created钩子函数获取数据,按自己的想法应该是父组件先获取后端数据,在传入给子组…...

怎么突破反爬虫机制
在当今的数字化时代,网络爬虫已经成为了收集信息和数据的重要工具。然而,许多网站和平台都配备了反爬虫机制,以防止恶意攻击和过度访问。对于普通用户来说,如何突破这些反爬虫机制呢?本文将为你提供一些实用的技巧和建…...
CSP-J2023入门组第二轮T4:旅游巴士
题目描述 小 Z 打算在国庆假期期间搭乘旅游巴士去一处他向往已久的景点旅游。 旅游景点的地图共有 n n n 处地点,在这些地点之间连有 m m m 条道路。其中 1 1...

OS的Alarm定时器调度机制
调度表触发的任务在编译时就被静态定义,任务的触发时间和执行顺序是固定的。这种方式适用于已知的、固定的任务触发模式,例如周期性任务或事件驱动任务。而使用 Alarm 机制触发的任务具有更大的灵活性。Alarm 允许在运行时动态地设置和修改任务的触发时间…...

I2C协议
1.简介 IIC(Inter-Integrated Circuit)其实是IICBus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,半双工通信,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用…...
全栈经验总结(不间断更新)
1.当后端传回来的值为列表套字典[{"id":1,"num":"1"},{"id":2"num":"3"}],如果要在vue3里面渲染图片,可以这样操作 <el-form-item label"图片:"><el-uploa…...

什么是恶意代码?
前言:本文旨在分享交流技术,在这里对恶意代码进行全面的介绍和讲解 目录 一.什么是恶意代码 二.恶意代码的发展史 三.恶意代码的相关定义 四.恶意代码攻击机制 PE病毒 PE文件的格式 脚本病毒 脚本文件隐藏方法 宏病毒 浏览器恶意代码 U盘病毒 …...

HCL模拟器选路实验案例
此选路题目选自职业院校技能竞赛中的一道题比较考验思路,适合于参加新华三杯大赛以及网络专业的同学,当做练习题目进行解题 题目 1.S1、S2、R1、R2运行ospf进程100,区域0,R1、R2、R3、R4、R5运行ospf进程200&#…...

toluaframework中C#怎么调用Lua的方法以及无GC方法
toluaframework中C#怎么调用Lua的方法 问题Util.CallMethodLuaManager.CallFunctionLuaFunction.LazyCall 解决方案LuaFunction脚本无GC消耗的调用 用法总结 问题 用过luaframework框架的人应该都知道框架提供了Util的工具类,工具类提供了一个方法就是Util.CallMet…...
安装pandas报错
报错信息: C:\Users\Jordan>pip install pandas Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/ Collecting pandasUsing cached https://pypi.tuna.tsinghua.edu.cn/packages/3a/6e/6c9c197ec2da861ea8c9c6848f0f887b7563f16e607bc6a35506af6…...
总有一天,你一定会很棒
晚上从一个大商场吃完饭出来,看到两个垂头丧气的男孩,他们坐在商场门口的户外台阶上,低着头,不停的搓着手,看不见他们的脸,只能看见他们不说话。在他们的面前,是一个年级大约25岁左右的女孩子&a…...

带你深入了解队列(c/cpp双版本模拟实现)
目录 一.队列的概念及结构 二.队列的实现 2.1队列的结构 2.2初始化队列 2.3队尾入队列 2.4队头出队列 2.5获取队列头部元素 2.6获取队列队尾元素 2.7获取队列中有效元素个数 2.8检测队列是否为空 2.9销毁队列 三.C 版本模拟实现队列 一.队列的概念及结构 队列…...

接口自动化测试实操
实现思路 使用excel管理用例用例信息,requests模块发送http请求,实现了记录日志,邮件发送测试报告的功能 目录结构如下: 下面直接上代码: 统筹脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24…...

Virtual DOM
目录 Virtual DOM 前言 用法 代码 理解 Virtual DOM的工作原理: 为什么使用Virtual DOM? 哪些库/框架使用Virtual DOM? 总结 Virtual DOM(虚拟DOM)是一种编程概念,它是对真实DOM的轻量级抽象表示。在前端开发中&#x…...

数据结构与算法-二叉树的遍历
🌞 “少年没有乌托邦,心向远方自明朗!” 二叉树 🎈1.二叉树的遍历🔭1.1先序遍历🔭1.2中序遍历🔭1.3后序遍历🔭1.4层次遍历🔭1.5二叉树遍历的递归算法📝1.5.1先…...

Qt之普通项目如何生成DLL(含源码+注释)
文章目录 一、示例图二、普通项目需要改造的内容三、源码(创建了一个TestDLL的项目,更改内容主要在pro文件和maindow.h文件)TestDLL.promainwindow.hmainwindow.cppmainwindow.ui 总结 一、示例图 使用不同的编译模式编译,会在对…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...