当前位置: 首页 > news >正文

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脚本实现组件化动态切换

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

基于springboot实现就业信息管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现就业信息管理系统演示 摘要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;就业信息管理系统也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人…...

Vue组件的本质和手写通过render渲染函数渲染组件

1.组件的本质 组件就是一组 DOM 元素的封装&#xff0c;本质就是一个对象 (mounted函数中打印一下组件即可看到打印的是一个对象) 如何利用javascript对象来描述一个组件&#xff1f; const MyComponent {render() {return {tag: div,props: {onClick: () > alert(hell…...

【优选算法系列】第一节.双指针(283. 移动零和1089. 复写零)

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;优选算法系列 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&#xff01…...

Vue(uniapp)父组件方法和子组件方法执行优先顺序

涉及到的知识点&#xff1a;watch监控&#xff1a;先看问题&#xff0c;父组件从后端通过$ajax获取数据&#xff0c;在将父组件将值传输给子组件&#xff0c;使用子组件使用created钩子函数获取数据&#xff0c;按自己的想法应该是父组件先获取后端数据&#xff0c;在传入给子组…...

怎么突破反爬虫机制

在当今的数字化时代&#xff0c;网络爬虫已经成为了收集信息和数据的重要工具。然而&#xff0c;许多网站和平台都配备了反爬虫机制&#xff0c;以防止恶意攻击和过度访问。对于普通用户来说&#xff0c;如何突破这些反爬虫机制呢&#xff1f;本文将为你提供一些实用的技巧和建…...

CSP-J2023入门组第二轮T4:旅游巴士

题目描述 小 Z 打算在国庆假期期间搭乘旅游巴士去一处他向往已久的景点旅游。 旅游景点的地图共有 n n n 处地点,在这些地点之间连有 m m m 条道路。其中 1 1...

OS的Alarm定时器调度机制

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

I2C协议

1.简介 IIC&#xff08;Inter-Integrated Circuit&#xff09;其实是IICBus简称&#xff0c;所以中文应该叫集成电路总线&#xff0c;它是一种串行通信总线&#xff0c;使用多主从架构&#xff0c;半双工通信&#xff0c;由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用…...

全栈经验总结(不间断更新)

1.当后端传回来的值为列表套字典[{"id":1,"num":"1"},{"id":2"num":"3"}]&#xff0c;如果要在vue3里面渲染图片&#xff0c;可以这样操作 <el-form-item label"图片&#xff1a;"><el-uploa…...

什么是恶意代码?

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

HCL模拟器选路实验案例

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

toluaframework中C#怎么调用Lua的方法以及无GC方法

toluaframework中C#怎么调用Lua的方法 问题Util.CallMethodLuaManager.CallFunctionLuaFunction.LazyCall 解决方案LuaFunction脚本无GC消耗的调用 用法总结 问题 用过luaframework框架的人应该都知道框架提供了Util的工具类&#xff0c;工具类提供了一个方法就是Util.CallMet…...

安装pandas报错

报错信息&#xff1a; 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…...

总有一天,你一定会很棒

晚上从一个大商场吃完饭出来&#xff0c;看到两个垂头丧气的男孩&#xff0c;他们坐在商场门口的户外台阶上&#xff0c;低着头&#xff0c;不停的搓着手&#xff0c;看不见他们的脸&#xff0c;只能看见他们不说话。在他们的面前&#xff0c;是一个年级大约25岁左右的女孩子&a…...

带你深入了解队列(c/cpp双版本模拟实现)

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

接口自动化测试实操

实现思路 使用excel管理用例用例信息&#xff0c;requests模块发送http请求&#xff0c;实现了记录日志&#xff0c;邮件发送测试报告的功能 目录结构如下&#xff1a; 下面直接上代码&#xff1a; 统筹脚本 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的工作原理&#xff1a; 为什么使用Virtual DOM? 哪些库/框架使用Virtual DOM? 总结 Virtual DOM&#xff08;虚拟DOM&#xff09;是一种编程概念&#xff0c;它是对真实DOM的轻量级抽象表示。在前端开发中&#x…...

数据结构与算法-二叉树的遍历

&#x1f31e; “少年没有乌托邦&#xff0c;心向远方自明朗&#xff01;” 二叉树 &#x1f388;1.二叉树的遍历&#x1f52d;1.1先序遍历&#x1f52d;1.2中序遍历&#x1f52d;1.3后序遍历&#x1f52d;1.4层次遍历&#x1f52d;1.5二叉树遍历的递归算法&#x1f4dd;1.5.1先…...

Qt之普通项目如何生成DLL(含源码+注释)

文章目录 一、示例图二、普通项目需要改造的内容三、源码&#xff08;创建了一个TestDLL的项目&#xff0c;更改内容主要在pro文件和maindow.h文件&#xff09;TestDLL.promainwindow.hmainwindow.cppmainwindow.ui 总结 一、示例图 使用不同的编译模式编译&#xff0c;会在对…...

探索下一代命令行界面:OpenCLI 架构设计与插件化实践

1. 项目概述&#xff1a;一个面向未来的命令行界面原型最近在开源社区里&#xff0c;我注意到一个名为sys-fairy-eve/nightly-mvp-2026-03-19-opencli的项目。这个标题信息量不小&#xff0c;它不像一个成熟的产品&#xff0c;更像是一个开发过程中的里程碑快照。sys-fairy-eve…...

Legacy-iOS-Kit完整指南:如何让老旧iPhone和iPad重获新生

Legacy-iOS-Kit完整指南&#xff1a;如何让老旧iPhone和iPad重获新生 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to restore/downgrade, save SHSH blobs, jailbreak legacy iOS devices, and more 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit …...

终极网络资源下载神器:面向内容创作者的5步实战指南

终极网络资源下载神器&#xff1a;面向内容创作者的5步实战指南 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader 你是否曾为保…...

开源可视化利器:用声明式数据驱动构建交互式技术解释图

1. 项目概述&#xff1a;一个将复杂概念可视化的开源利器最近在整理技术分享材料时&#xff0c;我一直在寻找一种能直观展示复杂系统架构或算法流程的工具。传统的流程图工具要么太笨重&#xff0c;要么定制化程度不够&#xff0c;直到我遇到了nicobailon/visual-explainer这个…...

AI智能体开发实战:从Devin现象到代码辅助智能体构建

1. 项目概述&#xff1a;当开发者遇上AI智能体最近在GitHub上闲逛&#xff0c;发现一个叫“awesome-devins”的仓库热度飙升。点进去一看&#xff0c;好家伙&#xff0c;这简直是一个关于“AI智能体”的宝藏目录。这个由e2b-dev团队维护的项目&#xff0c;本质上是一个精心整理…...

Agent 的记忆也会被投毒:长期记忆安全的六阶段框架

过去&#xff0c;我们更习惯把大模型的风险理解为“这一轮输入有没有问题”“这一轮输出会不会越界”。但有了长期记忆之后&#xff0c;风险结构发生了变化。恶意内容不一定在当场触发&#xff0c;也不一定在同一轮任务里显现出来。它可以先悄悄进入记忆&#xff0c;在几天后、…...

CodeWeaver:多仓库聚合分析工具的设计、部署与实战指南

1. 项目概述与核心价值最近在折腾一个老项目&#xff0c;需要把一堆陈年的、用不同语言和框架写的代码仓库整合到一个统一的视图里进行管理和分析。手动去每个仓库里翻看提交记录、统计代码行数、检查依赖关系&#xff0c;这活儿想想就头大。就在我准备硬着头皮写脚本的时候&am…...

C++中的封装、继承、多态理解

封装(encapsulation)&#xff1a;就是将抽象得到的数据和行为(或功能)相结合&#xff0c;形成一个有机的整体&#xff0c;也就是将数据与操作数据的源代码进行有机的结合&#xff0c;形成”类”&#xff0c;其中数据和函数都是类的成员。封装的目的是增强安全性和简化编程&…...

Gitee领跑本土化开发体验:深度解析国内代码托管平台的选择之道

在数字化转型浪潮中&#xff0c;代码托管平台已成为开发者团队不可或缺的基础设施。国内市场经过多年发展&#xff0c;已经从单一的海外平台依赖&#xff0c;逐步形成了多元化的平台选择生态。其中&#xff0c;Gitee凭借其本土化优势脱颖而出&#xff0c;成为众多国内开发团队的…...

Win11任务栏小喇叭失踪?别慌!3个亲测有效的修复方法(含重启资源管理器与音频服务)

Win11任务栏音量图标消失&#xff1f;3种专业修复方案与深度解析 刚升级Win11的用户常会遇到一个令人抓狂的小问题——任务栏右下角的音量图标突然"离家出走"。这个看似微不足道的小喇叭&#xff0c;却是我们日常调节系统音量的主要入口。当它消失时&#xff0c;不仅…...