uni-app 怎么生成路由

689 阅读5分钟

前言

现在很多项目都需要支持跨端H5APP各种小程序uni-app框架就是很好的一种选择。虽然uni-app基于Vue实现,但却没有路由文件route.js,只有一个pages.json,并且只配置了路由路径,没有指明组件加载路径,那它是怎么实现路由注册的呢?我知道你也有这个疑问,那就跟我一起拨开云雾,探寻uni-app怎么如何一步步将pages.json转换为Vue所需要的路由。

本篇文章主要讲解uni-appH5环境的路由实现。

探索怎么处理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的pluginsloader。在这里我们可以看到有个npm包名为webpack-uni-pages-loader,这个命名也比较直观,就是uni-app用来处理pages.json文件的loader。点开这个文件夹,可以看到包含了很多个平台的代码,我这边分析的是H5平台,所以需要查看的文件是h5.js

image.png

在uni-app开发中,支持小程序是个很普遍的现象,而小程序又会遇到主包太大无法发布的烦恼,这时会采用分子包的方案,所以路由配置可能就不单单只有pages,还会有subPackages。以下是一份简单的page.json配置,看看怎么转换为符合Vue格式的路由配置,以及路由配置是什么样子的。

image.png

处理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。

image.png

这个片段代码执行完成后,最终会得到这么一个pages数组。

image.png

遍历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
  • 返回一个新的数组

image.png

执行完getPageComponents方法,就得到包含chunk名称name,页面路由route,组件路径path,是否为nvue标识isNVue四个字段(其他字段省略了)的数组,有了这些,就可以构建出vue需要的路由配置了。

image.png

路由配置

uni-app生成路由,跟我们平常使用的将组件的引入放在路由配置中有些许不同,它是分为两步,一步全局注册组件, 一步在路由中通过createElement创建组件。

生成组件注册代码

这边涉及到另外一个方法genRegisterPageVueComponentsCode,参数为上一步得到的组件数据pageComponents。以name为组件名称,通过isNVue判断组件扩展名,path拼接扩展名为组件路径,同时name也作为组件的chunk名称,生成如下的全局注册组件代码,require.ensure这个是以前webpack注册异步组件的方式,现在都是采用import来实现异步组件注册了。

image.png

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和全局注册的组件做关联。

image.png

最终会得到这样子的一份符合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的路由可以这么配置,你说文档就是这么写的,而不会探寻其实现的原理,我也一样,希望我们有更多的好奇心。

如果觉得这篇文章对您有帮助,欢迎点赞收藏,有问题也欢迎指出。