前言
现在很多项目都需要支持跨端H5、APP、各种小程序,uni-app框架就是很好的一种选择。虽然uni-app基于Vue实现,但却没有路由文件route.js,只有一个pages.json,并且只配置了路由路径,没有指明组件加载路径,那它是怎么实现路由注册的呢?我知道你也有这个疑问,那就跟我一起拨开云雾,探寻uni-app怎么如何一步步将pages.json转换为Vue所需要的路由。
本篇文章主要讲解uni-app在H5环境的路由实现。
探索怎么处理pages.json文件
uni-app虽然没有像vue一样直观的路由文件route.js用来配置页面路由和路由对应的组件,但我们在pages.json的pages配置了页面的路由path,从这个点可以联想到最终的路由结构是通过pages.json这个文件处理得到的。对于文件处理我们自然而然的想起了webpack的loader,沿着这个线索我们再去找到对应的loader源码。
@dcloudio是uni-app官方npm包名的作用域,所以node_modules/@dcloudio文件夹底下包含了uni-app编译需要的webpack的plugins和loader。在这里我们可以看到有个npm包名为webpack-uni-pages-loader,这个命名也比较直观,就是uni-app用来处理pages.json文件的loader。点开这个文件夹,可以看到包含了很多个平台的代码,我这边分析的是H5平台,所以需要查看的文件是h5.js。
在uni-app开发中,支持小程序是个很普遍的现象,而小程序又会遇到主包太大无法发布的烦恼,这时会采用分子包的方案,所以路由配置可能就不单单只有pages,还会有subPackages。以下是一份简单的page.json配置,看看怎么转换为符合Vue格式的路由配置,以及路由配置是什么样子的。
处理pages.json文件
在webpack-uni-pages-loader中的有一个核心方法getPageComponents,顾名思义就是获取页面组件信息。对于生成路由来讲就主要做了两件事,当然还有其他是否Tab页判断,导航栏样式等等,这些细枝末节就不做阐述了,感兴趣的可以自己看看源码。
- 将subPackages遍历拼接根路径合并到pages
- 遍历pages得到页面路由和组件文件路径
将subPackages遍历拼接根路径合并到pages
在webpack的loader获取到pages.json文件源码是字符串,将字符串转为json就得到了pagesJson,然后在作为getPageComponents方法的参数。这边先来看getPageComponents方法的一个片段。首先获取到pages,然后判断是否存在subPackages并且有内容,如果有的话,对其进行遍历,给path都拼接上跟路径root,得到完整路径,然后插入到pages。
这个片段代码执行完成后,最终会得到这么一个pages数组。
遍历pages得到页面路由和组件文件路径
再来看这个代码片段,对于pages这个路径数组进行遍历。
- 将path的
/替换为-,即pages/index/index转换为pages-index-index,得到name,这个主要是用来作为当前组件最终的chunk名称。 - 这边的inputDir是当前项目的src目录,例如
C:/workspace/uni-split-demo/src/,然后拼接上页面路由,就得到了组件的完整路径C:/workspace/uni-split-demo/src/pages/index/index,可以这样子的原因是我们在pages.json的pages配置的path就是和文件目录相对应的。 - fs.existsSync是nodejs用来判断文件是否存在,这边通过判断是否存在nvue文件来设置标记isNVue
- 返回一个新的数组
执行完getPageComponents方法,就得到包含chunk名称name,页面路由route,组件路径path,是否为nvue标识isNVue四个字段(其他字段省略了)的数组,有了这些,就可以构建出vue需要的路由配置了。
路由配置
uni-app生成路由,跟我们平常使用的将组件的引入放在路由配置中有些许不同,它是分为两步,一步全局注册组件, 一步在路由中通过createElement创建组件。
生成组件注册代码
这边涉及到另外一个方法genRegisterPageVueComponentsCode,参数为上一步得到的组件数据pageComponents。以name为组件名称,通过isNVue判断组件扩展名,path拼接扩展名为组件路径,同时name也作为组件的chunk名称,生成如下的全局注册组件代码,require.ensure这个是以前webpack注册异步组件的方式,现在都是采用import来实现异步组件注册了。
Vue.component('pages-index-index', resolve => {
const component = {
component: require.ensure([], () => resolve(require("C:/workspace/uni-split-demo/src/pages/index/index.vue")))
};
});
Vue.component('pages-me-index', resolve => {
const component = {
component: require.ensure([], () => resolve(require("C:/workspace/uni-split-demo/src/pages/me/index.vue")), 'pages-me-index')
};
});
Vue.component('pages-child-test-index', resolve => {
const component = {
component: require.ensure([], () => resolve(require("C:/workspace/uni-split-demo/src/pages/child/test/index.vue")), 'pages-child-test-index')
};
});
Vue.component('pages-child-passion-index', resolve => {
const component = {
component: require.ensure([], () => resolve(require("C:/workspace/uni-split-demo/src/pages/child/passion/index.vue")), 'pages-child-passion-index')
};
});
生成路由配置
这边涉及到另外一个方法genPageRoutes,参数为上一步得到的组件数据pageComponents。可以看到这边对于路由的path虽然添加了isEntry是否为首页判断,但用到的字段就是之前生成route字段,然后组件方面没有直接使用组件路径,而是通过createElement和全局注册的组件做关联。
最终会得到这样子的一份符合Vue期待的路由配置,这里就完成了从pages.json到我们理解中Vue路由的转换了。
[
{
path: '/',
alias: '/pages/index/index',
component: {
render(createElement) {
return createElement(
'Page',
{
props: Object.assign(__uniConfig.globalStyle, {}),
},
[
createElement('pages-index-index', {
slot: 'page',
}),
],
);
},
},
},
{
path: '/pages/me/index',
component: {
render(createElement) {
return createElement(
'Page',
{
props: Object.assign(__uniConfig.globalStyle, {}),
},
[
createElement('pages-me-index', {
slot: 'page',
}),
],
);
},
},
},
{
path: '/pages/child/test/index',
component: {
render(createElement) {
return createElement(
'Page',
{
props: Object.assign(__uniConfig.globalStyle, {}),
},
[
createElement('pages-child-test-index', {
slot: 'page',
}),
],
);
},
},
},
{
path: '/pages/child/passion/index',
component: {
render(createElement) {
return createElement(
'Page',
{
props: Object.assign(__uniConfig.globalStyle, {}),
},
[
createElement('pages-child-passion-index', {
slot: 'page',
}),
],
);
},
},
},
];
总结
有些东西总感觉习以为常,也许它本来就是这个样子的。就像文章所讲的关于uni-app的路由配置,别人问你为什么uni-app的路由可以这么配置,你说文档就是这么写的,而不会探寻其实现的原理,我也一样,希望我们有更多的好奇心。
如果觉得这篇文章对您有帮助,欢迎点赞收藏,有问题也欢迎指出。