HarmonyOS 鸿蒙Next基于自定义注解和代码生成实现路由框架

发布于 1周前 作者 gougou168 来自 鸿蒙OS

HarmonyOS 鸿蒙Next基于自定义注解和代码生成实现路由框架

场景描述

在应用开发中无论是出于工程组织效率还是开发体验的考虑,开发者都需要对项目进行模块间解耦,此时需要构建一套用于模块间组件跳转、数据通信的路由框架。

业界常见的实现方式是在编译期生成路由表。

1. 实现原理及流程

  • 在编译期通过扫描并解析ets文件中的自定义注解来生成路由表和组件注册类
  • Har中的rawfile文件在Hap编译时会打包在Hap中,通过这一机制来实现路由表的合并
  • 自定义组件通过wrapBuilder封装来实现动态获取
  • 通过NavDestination的Builder机制来获取wrapBuilder封装后的自定义组件

2. 使用ArkTS自定义装饰器来代替注解的定义

由于TS语言特性,当前只能使用自定义装饰器

使用@AppRouter装饰器来定义路由信息

// 定义空的装饰器
export function AppRouter(param:AppRouterParam) {
return Object;
}

export interface AppRouterParam{ uri:string; }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

自定义组件增加路由定义

@AppRouter({ uri: “app://login” })
@Component
export struct LoginView {
build(){
//…
}
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

3. 实现动态路由模块

定义路由表(该文件为自动生成的路由表)

{
“routerMap”: [
{
“name”: “app://login”,   /* uri定义  /
“pageModule”: “loginModule”,  / 模块名  /
“pageSourceFile”: “src/main/ets/generated/RouterBuilder.ets”,  / Builder文件  /
“registerFunction”: “LoginViewRegister”  / 组件注册函数  */
}
]
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

应用启动时,在EntryAbility.onCreate中加载路由表

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
DynamicRouter.init({
libPrefix: @app, mapPath: “routerMap”
}, this.context);
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

export class DynamicRouter {
// 路由初始化配置
static config: RouterConfig;
// 路由表
static routerMap: Map<string, RouterInfo> = new Map();
// 管理需要动态导入的模块,key是模块名,value是WrappedBuilder对象,动态调用创建页面的接口
static builderMap: Map<string, WrappedBuilder<Object[]>> = new Map();
// 路由栈
static navPathStack: NavPathStack = new NavPathStack();
// 通过数组实现自定义栈的管理
static routerStack: Array<RouterInfo> = new Array();
static referrer: string[] = [];

public static init(config: RouterConfig, context: Context) { DynamicRouter.config = config; DynamicRouter.routerStack.push(HOME_PAGE) RouterLoader.load(config.mapPath, DynamicRouter.routerMap, context) } //… }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

路由表存放在src/main/resources/rawfile目录中,通过ResourceManager进行读取

export namespace RouterLoader {

export function load(dir: string, routerMap: Map<string, RouterInfo>, context: Context) { const rm: resourceManager.ResourceManager = context.resourceManager; try { rm.getRawFileList(dir) .then((value: Array<string>) => { let decoder: util.TextDecoder = util.TextDecoder.create(‘utf-8’, { fatal: false, ignoreBOM: true }) value.forEach(fileName => { let fileBytes: Uint8Array = rm.getRawFileContentSync(${dir}/${fileName}) let retStr = decoder.decodeWithStream(fileBytes) let routerMapModel: RouterMapModel = JSON.parse(retStr) as RouterMapModel loadRouterMap(routerMapModel, routerMap) }) }) .catch((error: BusinessError) => { //… }); } catch (error) { //… } } }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

根据URI跳转页面时,通过动态import并执行路由表中定义的registerFunction方法来实现动态注册组件

Button(“跳转”)
.onClick(()=>{
DynamicRouter.pushUri(“app://settings”)
})<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

export class DynamicRouter {
//…
public static pushUri(uri: string, param?: Object, onPop?: (data: PopInfo) => void): void {
if (!DynamicRouter.routerMap.has(uri)) {
return;
}
let routerInfo: RouterInfo = DynamicRouter.routerMap.get(uri)!;
if (!DynamicRouter.builderMap.has(uri)) {
// 动态加载模块
import(${DynamicRouter.config.libPrefix}/${routerInfo.pageModule})
.then((module: ESObject) => {
modulerouterInfo.registerFunction!   // 进行组件注册,实际执行了下文中的LoginViewRegister方法
DynamicRouter.navPathStack.pushDestination({ name: uri, onPop: onPop, param: param });
DynamicRouter.pushRouterStack(routerInfo);
})
.catch((error: BusinessError) => {
console.error(promise import module failed, error code: ${error.code}, message: ${error.message}.);
});
} else {
DynamicRouter.navPathStack.pushDestination({ name: uri, onPop: onPop, param: param });
DynamicRouter.pushRouterStack(routerInfo);
}
}
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

组件注册实际执行的方法为LoginViewRegister(该文件为自动生成的模版代码)

// auto-generated RouterBuilder.ets
import { DynamicRouter, RouterInfo } from @app/dynamicRouter/Index’
import { LoginView } from ‘…/components/LoginView’

@Builder function LoginViewBuilder() { LoginView() }

export function LoginViewRegister(routerInfo: RouterInfo) { DynamicRouter.registerRouterPage(routerInfo, wrapBuilder(LoginViewBuilder)) }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

通过wrapBuilder将自定义组件保存在组件表

export class DynamicRouter {
//…
// 通过URI注册builder
public static registerRouterPage(routerInfo: RouterInfo, wrapBuilder: WrappedBuilder<Object[]>): void {
const builderName: string = routerInfo.name;
if (!DynamicRouter.builderMap.has(builderName)) {
DynamicRouter.registerBuilder(builderName, wrapBuilder);
}
}

private static registerBuilder(builderName: string, builder: WrappedBuilder<Object[]>): void { DynamicRouter.builderMap.set(builderName, builder); }

// 通过URI获取builder public static getBuilder(builderName: string): WrappedBuilder<Object[]> { const builder = DynamicRouter.builderMap.get(builderName); return builder as WrappedBuilder<Object[]>; } }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

首页Navigation通过组件表获取自定义组件Builder

@Entry
@Component
struct Index {
build() {
Navigation(DynamicRouter.getNavPathStack()) {
//…
}
.navDestination(this.PageMap)
.hideTitleBar(true)
}

@Builder PageMap(name: string, param?: ESObject) { NavDestination() { DynamicRouter.getBuilder(name).builder(param); } }

}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

4. 实现路由表生成插件

新建插件目录etsPlugin,建议创建在HarmonyOS工程目录之外

mkdir etsPlugin
cd etsPlugin<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

创建npm项目

npm init<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

安装依赖

npm i --save-dev @types/node @ohos/hvigor @ohos/hvigor-ohos-plugin
npm i typescript handlebars<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

初始化typescript配置

./node_modules/.bin/tsc --init<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

修改tsconfig.json

{
“compilerOptions”: {
“target”: “es2021”,                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. /
“module”: “commonjs”,                                / Specify what module code is generated. /
“strict”: true,                                      / Enable all strict type-checking options. /
“esModuleInterop”: true,                             / Emit additional JavaScript to ease support for importing CommonJS modules. This enables ‘allowSyntheticDefaultImports’ for type compatibility. /
“forceConsistentCasingInFileNames”: true,            / Ensure that casing is correct in imports. /
“skipLibCheck”: true,                                 / Skip type checking all .d.ts files. /
“sourceMap”: true,
“outDir”: “./lib”,
},
“include”: [".eslintrc.js", "src/**/"],
“exclude”: [“node_modules”, “lib/**/*”],
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

创建插件文件src/index.ts

export function etsGeneratorPlugin(pluginConfig: PluginConfig): HvigorPlugin {
return {
pluginId: PLUGIN_ID,
apply(node: HvigorNode) {
pluginConfig.moduleName = node.getNodeName();
pluginConfig.modulePath = node.getNodePath();
pluginExec(pluginConfig);
},
};
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

修改package.json

{
//…
“main”: “lib/index.js”,
“scripts”: {
“test”: “echo “Error: no test specified” && exit 1”,
“dev”: “tsc && node lib/index.js”,
“build”: “tsc”
},
//…
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

插件实现流程

image.png

  • 通过扫描自定义组件的ets文件,解析语法树,拿到注解里定义的路由信息
  • 生成路由表、组件注册类,同时更新Index.ets

定义插件配置

const config: PluginConfig = {
builderFileName: “RouterBuilder.ets”,  // 生成的组件注册类文件名
builderDir: “src/main/ets/generated”,  // 代码生成路径
routerMapDir: “src/main/resources/rawfile/routerMap”,  // 路由表生成路径
scanDir: “src/main/ets/components”,  // 自定义组件扫描路径
annotation: “AppRouter”,  // 路由注解
viewKeyword: “struct”,  // 自定义组件关键字
builderTpl: “viewBuilder.tpl”,  // 组件注册类模版文件
};<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

插件核心代码:

function pluginExec(config: PluginConfig) {
// 读取指定自定义组件目录下的文件
const scanPath = ${config.modulePath}/${config.scanDir};
const files: string[] = readdirSync(scanPath);
files.forEach((fileName) => {
// 对每个文件进行解析
const sourcePath = ${scanPath}/${fileName};
const importPath = path
.relative(${config.modulePath}/${config.builderDir}, sourcePath)
.replaceAll("\", “/”)
.replaceAll(".ets", “”);

<span class="hljs-comment"><span class="hljs-comment">// 执行语法树解析器</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">const</span></span> analyzer = <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> EtsAnalyzer(config, sourcePath);
analyzer.start();

<span class="hljs-comment"><span class="hljs-comment">// 保存解析结果</span></span>
console.log(<span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(analyzer.analyzeResult));
console.log(importPath);
templateModel.viewList.push({
  viewName: analyzer.analyzeResult.viewName,
  importPath: importPath,
});
routerMap.routerMap.push({
  name: analyzer.analyzeResult.uri,
  pageModule: config.moduleName,
  pageSourceFile: `${config.builderDir}/${config.builderFileName}`,
  registerFunction: `${analyzer.analyzeResult.viewName}Register`,
});

});

// 生成组件注册类 generateBuilder(templateModel, config); // 生成路由表 generateRouterMap(routerMap, config); // 更新Index文件 generateIndex(config); }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

语法树解析流程

  • 遍历语法树节点,找到自定义注解@AppRouter
  • 读取URI的值
  • 通过识别struct关键字来读取自定义组件类名
  • 其他节点可以忽略

核心代码:

export class EtsAnalyzer {
sourcePath: string;
pluginConfig: PluginConfig;
analyzeResult: AnalyzeResult = new AnalyzeResult();
keywordPos: number = 0;

constructor(pluginConfig: PluginConfig, sourcePath: string) { this.pluginConfig = pluginConfig; this.sourcePath = sourcePath; }

start() { const sourceCode = readFileSync(this.sourcePath, “utf-8”); // 创建ts语法解析器 const sourceFile = ts.createSourceFile( this.sourcePath, sourceCode, ts.ScriptTarget.ES2021, false ); // 遍历语法节点 ts.forEachChild(sourceFile, (node: ts.Node) => { this.resolveNode(node); }); }

// 根据节点类型进行解析 resolveNode(node: ts.Node): NodeInfo | undefined { switch (node.kind) { case ts.SyntaxKind.ImportDeclaration: { this.resolveImportDeclaration(node); break; } case ts.SyntaxKind.MissingDeclaration: { this.resolveMissDeclaration(node); break; } case ts.SyntaxKind.Decorator: { this.resolveDecorator(node); break; } case ts.SyntaxKind.CallExpression: { this.resolveCallExpression(node); break; } case ts.SyntaxKind.ExpressionStatement: { this.resolveExpression(node); break; } case ts.SyntaxKind.Identifier: { return this.resolveIdentifier(node); } case ts.SyntaxKind.StringLiteral: { return this.resolveStringLiteral(node); } case ts.SyntaxKind.PropertyAssignment: { return this.resolvePropertyAssignment(node); } } }

resolveImportDeclaration(node: ts.Node) { let ImportDeclaration = node as ts.ImportDeclaration; }

resolveMissDeclaration(node: ts.Node) { node.forEachChild((cnode) => { this.resolveNode(cnode); }); }

resolveDecorator(node: ts.Node) { let decorator = node as ts.Decorator; this.resolveNode(decorator.expression); }

resolveIdentifier(node: ts.Node): NodeInfo { let identifier = node as ts.Identifier; let info = new NodeInfo(); info.value = identifier.escapedText.toString(); return info; }

resolveCallExpression(node: ts.Node) { let args = node as ts.CallExpression; let identifier = this.resolveNode(args.expression); this.parseRouterConfig(args.arguments, identifier); }

resolveExpression(node: ts.Node) { let args = node as ts.ExpressionStatement; let identifier = this.resolveNode(args.expression); if (identifier?.value === this.pluginConfig.viewKeyword) { this.keywordPos = args.end; } if (this.keywordPos === args.pos) { this.analyzeResult.viewName = identifier?.value; } }

resolveStringLiteral(node: ts.Node): NodeInfo { let stringLiteral = node as ts.StringLiteral; let info = new NodeInfo(); info.value = stringLiteral.text; return info; }

resolvePropertyAssignment(node: ts.Node): NodeInfo { let propertyAssignment = node as ts.PropertyAssignment; let propertyName = this.resolveNode(propertyAssignment.name)?.value; let propertyValue = this.resolveNode(propertyAssignment.initializer)?.value; let info = new NodeInfo(); info.value = { key: propertyName, value: propertyValue }; return info; }

}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

使用模版引擎生成组件注册类

使用Handlebars生成组件注册类

const template = Handlebars.compile(tpl);
const output = template({ viewList: templateModel.viewList });<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

模版文件viewBuilder.tpl示例:

// auto-generated RouterBuilder.ets
import { DynamicRouter, RouterInfo } from @app/dynamicRouter/Index’
{{#each viewList}}
import { {{viewName}} } from ‘{{importPath}}’
{{/each}}

{{#each viewList}} @Builder function {{viewName}}Builder() { {{viewName}}() }

export function {{viewName}}Register(routerInfo: RouterInfo) { DynamicRouter.registerRouterPage(routerInfo, wrapBuilder({{viewName}}Builder)) }

{{/each}}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

生成的RouterBuilder.ets代码示例:

// auto-generated RouterBuilder.ets
import { DynamicRouter, RouterInfo } from @app/dynamicRouter/Index’
import { LoginView } from ‘…/components/LoginView’

@Builder function LoginViewBuilder() { LoginView() }

export function LoginViewRegister(routerInfo: RouterInfo) { DynamicRouter.registerRouterPage(routerInfo, wrapBuilder(LoginViewBuilder)) }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

将路由表和组件注册类写入文件

  • 路由表保存在rawfile目录
  • 组件注册类保存在ets代码目录
  • 更新模块导出文件Index.ets

核心代码:

function generateBuilder(templateModel: TemplateModel, config: PluginConfig) {
console.log(JSON.stringify(templateModel));
const builderPath = path.resolve(__dirname, ../${config.builderTpl});
const tpl = readFileSync(builderPath, { encoding: “utf8” });
const template = Handlebars.compile(tpl);
const output = template({ viewList: templateModel.viewList });
console.log(output);
const routerBuilderDir = ${config.modulePath}/${config.builderDir};
if (!existsSync(routerBuilderDir)) {
mkdirSync(routerBuilderDir, { recursive: true });
}
writeFileSync(${routerBuilderDir}/${config.builderFileName}, output, {
encoding: “utf8”,
});
}

function generateRouterMap(routerMap: RouterMap, config: PluginConfig) { const jsonOutput = JSON.stringify(routerMap, null, 2); console.log(jsonOutput); const routerMapDir = ${config.modulePath}/${config.routerMapDir}; if (!existsSync(routerMapDir)) { mkdirSync(routerMapDir, { recursive: true }); } writeFileSync(${routerMapDir}/${config.moduleName}.json, jsonOutput, { encoding: “utf8”, }); }

function generateIndex(config: PluginConfig) { const indexPath = ${config.modulePath}/Index.ets; const indexContent = readFileSync(indexPath, { encoding: “utf8” }); const indexArr = indexContent .split("\n") .filter((value) => !value.includes(config.builderDir!)); indexArr.push( export * from <span class="hljs-string"><span class="hljs-string">'./${config.builderDir}/${config.builderFileName?.replace( ".ets", "" )}'</span></span> ); writeFileSync(indexPath, indexArr.join("\n"), { encoding: “utf8”, }); }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

5. 在应用中使用

修改项目的hvigor/hvigor-config.json文件,导入路由表插件

{
“hvigorVersion”: “4.2.0”,
“dependencies”: {
@ohos/hvigor-ohos-plugin”: “4.2.0”,
@app/ets-generator” : “file:…/…/etsPlugin”   // 插件目录的本地相对路径,或者使用npm仓版本号
},
//…
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

修改loginModule模块的hvigorfile.ts文件(loginModule/hvigorfile.ts),加载插件

import { harTasks } from @ohos/hvigor-ohos-plugin’;
import {PluginConfig,etsGeneratorPlugin} from @app/ets-generator’

const config: PluginConfig = { builderFileName: “RouterBuilder.ets”, builderDir: “src/main/ets/generated”, routerMapDir: “src/main/resources/rawfile/routerMap”, scanDir: “src/main/ets/components”, annotation: “AppRouter”, viewKeyword: “struct”, builderTpl: “viewBuilder.tpl”, }

export default { system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. / plugins:[etsGeneratorPlugin(config)] / Custom plugin to extend the functionality of Hvigor. */ }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

在loginModule模块的oh-package.json5中引入动态路由模块依赖

{
“name”: “loginmodule”,
“version”: “1.0.0”,
“description”: “Please describe the basic information.”,
“main”: “Index.ets”,
“author”: “”,
“license”: “Apache-2.0”,
“dependencies”: {
@app/dynamicRouter”: “file:…/routerModule”
}
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

在loginModule模块的自定义组件中使用@AppRouter定义路由信息

@AppRouter({ uri: “app://login” })
@Component
export struct LoginView {
build(){
//…
}
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

在entry中的oh-package.json5中引入依赖

{
“name”: “entry”,
“version”: “1.0.0”,
“description”: “Please describe the basic information.”,
“main”: “”,
“author”: “”,
“license”: “”,
“dependencies”: {
@app/loginModule”: “file:…/loginModule”,
@app/commonModule”: “file:…/commonModule”,
@app/dynamicRouter”: “file:…/routerModule”
}
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

在entry中的build-profile.json5中配置动态import

{
“apiType”: “stageMode”,
“buildOption”: {
“arkOptions”: {
“runtimeOnly”: {
“packages”: [
@app/loginModule”,  // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
@app/commonModule”  // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。
]
}
}
},
//…
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

在entry中的EntryAbility.onCreate中初始化路由组件

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
DynamicRouter.init({
libPrefix: @app, mapPath: “routerMap”
}, this.context);
}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

组件内使用pushUri进行跳转

Button(“立即登录”, { buttonStyle: ButtonStyleMode.TEXTUAL })
.onClick(() => {
DynamicRouter.pushUri(“app://login”)
})
.id(“button”)<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

在entry模块执行Run/Debug,即可在编译时自动生成路由表配置并打包运行。



关于HarmonyOS 鸿蒙Next基于自定义注解和代码生成实现路由框架的问题,您也可以访问:https://www.itying.com/category-93-b0.html 联系官网客服。

10 回复
很赞的分享,作者能分享一下npm的项目吗?

您好,当前技术文章配套的demo工程正在外发中,后续会发布至gitee上,敬请关注!

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

太赞了,作者能把这个封装成一个router库给同学们直接使用吗,每个应用的router模块需求功能都差不多

已刷火箭 已1000暴击

https://github.com/Star1128/AutoGenRouterMap

自己改了一版自动生成 Navigation 系统路由表的插件,在引入 Navigation 的基础上,给 NavDestination 所在的组件加上 [@AppRouter](/user/AppRouter) 注解,编译期会自动生成路由表和 Build 函数,减少一些繁琐的配置过程。

这个路由框架挺复杂的,看了也不知道怎么使用。需要时间⌚消化!

思路挺好,但是太复杂了,需要对鸿蒙开发非常了解了之后才比较好把握。
回到顶部