背景
了解过鸿蒙开发的应该都清楚,在单模块应用和多模块应用,应该如何进行页面的跳转
js
// url:页面在模块内的相对路径,在模块内的main_pages.json中声明
router.pushUrl({ url: 'pages/LaunchPage' })
- 单模块应用,可以直接传入模块内的
main_pages.json
中页面的路径即可跳转 - 多模块应用,方法不变,页面的路径修改为:
js
// 固定的前缀:@bundle:
// 固定的应用包名:bundleName
// 页面所在的模块名:moduleName
// 默认的模块内的源文件目录:ets
// 固定的页面在main_pages.json中的相对路径:例如:'pages/LaunchPage'
router.pushUrl({ url: '@bundle:${bundleName}/${moduleName}/ets/pages/LaunchPage' })
因为大部分的应用都会是多模块应用,所以这次主要想解决的,也是多模块应用下的页面跳转。
遇到的问题
- 页面跳转时,需要拼接的字符串比较长,不易于开发
- 页面的文件名一般不会修改,但是文件路径可能会有修改,如果文件路径有修改,所有跳转该页面的代码,都需要做调整,工作量较大
解决思路
a. 目的是通过封装统一的路由方法,调用方只需要传入页面的文件名即可完成跳转,不需要额外的拼接
b.后续也可以结合自己的业务,可以手动给每个Page绑定自己的路由别名,在跳转时,通过别名跳转,这个别名会确保不会修改,并且可以关联新的页面文件。类似安卓的ARouter,鸿蒙Next目前还没有发现好用的三方库
- 新建一个
WLRouterKit
的模块,并且新建一个RouterSourceData.ts
文件,目的是想通过编译期间的脚本代码,自动读取每个模块的main_pages.json
文件,获取所有页面的模块内的相对路径 - 在读取每个模块的文件路径时,直接生成好包含模块名的页面路径部分:
/${moduleName}/ets/pages/LaunchPage'
- 创建一个
Map
属性,key
是页面的文件名,value
是上面第二步生成的路径。如此就可以通过文件名匹配到对应的路径,最后在跳转时,拼接上固定的前缀部分即可。
实现代码
脚本代码
编译期间会执行hvigorfile.ts
,他里面默认是可以注册插件的,这个我还没有了解怎么使用,目前就是直接在这个文件中定义方法,并且执行即可
hvigorfile.ts
let fs = require('fs')
let path = require('path')
function generateRouter() {
// 读取文件信息
// WLRouterKit的路径:/Users/xxx/WLRouterKit
let routerKitDirname = path.join(__dirname)
// 获取项目的根目录:/Users/xxxx/xxx,因为我的WLRouterKit是在根目录的common目录下
const rootPath = path.dirname(path.dirname(routerKitDirname))
let totalRouters = {}
// 读取features和common目录下的所有模块
const dirs = ['features', 'common']
// main_pages.json在模块内的相对路径,固定的路径
const mainPagesModulePath = 'src/main/resources/base/profile/main_pages.json'
dirs.forEach(dir => {
// 读取所有features和common目录下的模块
const dirPath = path.join(rootPath, dir)
const moduleDirs = fs.readdirSync(dirPath)
moduleDirs.forEach(file => {
// 拼接文件路径
const mainPagesFilePath = path.join(dirPath, file, mainPagesModulePath)
// 判断文件是否存在
if (fs.existsSync(mainPagesFilePath)) {
console.log(`模块内的main_pages.json读取成功,${mainPagesFilePath}`)
// 读取文件内容
const jsonData = fs.readFileSync(mainPagesFilePath, 'utf-8')
const data = JSON.parse(jsonData)
const src = data.src
for (const path of src) {
const values = path.split('/')
const fileName = values[values.length - 1]
const pageUrl = `${file}/ets/${path}`
totalRouters[fileName] = pageUrl
}
}
})
})
// 1. 生成类名等信息
var content = '/*自动生成的路由数据*/'
content = content + '\nabstract class RouterSourceData { \n'
// 2. 根据本地配置的路由routers,生成targetDatas属性
content = content + "\tstatic targetDatas: { [key: string]: string } = { "
const keys = Object.keys(totalRouters)
keys.forEach(key => {
const value = totalRouters[key]
const line = `\r\t '${key}': '${value}',`
content = content + line
})
content = content + '\r\t}'
/// 类结束定义
content = content + '\n}'
content = content + '\nexport default RouterSourceData'
// 写入文件
const writePath = path.join(__dirname, 'src/main/ets/RouterSourceData.ts')
fs.writeFile(writePath, content, function (err) {
if (err) {
console.log('文件写入失败')
return
}
console.log('文件写入成功')
})
}
生成的目标类结构:
RouterSourceData.ts
/*自动生成的路由数据*/
abstract class RouterSourceData {
static targetDatas: { [key: string]: string } = {
'Index': 'xxKit/ets/pages/Index',
'LaunchPage': 'XXModule/ets/pages/LaunchPage',
'LoginRegisterPage': 'XXModule/ets/pages/LoginRegisterPage',
'TestPage': 'xxKit/ets/CommonWeb/Test/TestPage',
}
}
export default RouterSourceData
生成出这样的数据结构,就可以很方便的进行页面跳转的封装,通过页面的文件名,找到对应的文件路径。并且每次新增文件或者修改文件路径,在编译之后,就自动有正确的文件路径覆盖这个文件。
最后
提供这样一个解决的思路,提供了最基本的获取多模块应用下的文件路径的思路,后续可以自行进行其他的扩展。