艺灵设计

全部文章
×

@vue/cli3项目实战之后端返回动态路由后前端使用router.addRoutes无效?

作者:艺灵设计 - 来源:http://www.yilingsj.com - 发布时间:2020-07-15 21:17:19 - 阅: - 评:0 - 积分:0

摘要:之前在开发@vue/cli3项目时遇到一个动态导航的需求,部分导航菜单由后端返回。正常情况下,我们的导航对应的路由都是写死的,存放在router文件夹中。这回要动态的,不太好办呀!经过一番搜索和踩坑,axios请求后端获取数据,然后循环遍历数组生成符合routes规则的路由,然后再通过router.addRoutes动态添加路由,最后通过vuex来管理......

一、通过一个小场景来谈需求

接触过vue项目的看官应该都知道,通过@vue/cli脚手架搭建的前端项目中会有一个名为router的文件夹,里面有一个名为index.js的文件,其中存放我们事先定义好的路由。当前端路由过多时,我们会再拆分出一个名为list.js的文件用来专门存放路由。截止目前为止,一切都正常,没毛病~

直到有一天,我在开发数式科技官网的时候,同事提了一个需求,这个需求让我很头大!是什么呢?就是导航中的数式百科下面的栏目分类由后端返回。如图:百科下面的分类栏目由后端返回.png百科下面的分类栏目由后端返回

说到这里,看官应该能猜到这篇文章接下来该讲什么了吧。若看官对这个需求感兴趣,不妨往下继续看文章。或许在不久的将来,你也会遇到此需求哦~

二、解决方案

关于这个需求,不知各位看官有啥想法。对艺灵我而言,我肯定是想简单点了,估计这也是很多看官常用的方法。

2.1、法一:前后端约定好路由,工作量放在前端上

法一的实现很简单,前端跟后端约定好有哪些栏目分类,然后前端直接将路由写到list.js文件中。当后端那边需要增加分类或删除分类时,再跟前端沟通下,前端这边继续修改list.js文件,然后打包上线。

这个方法是非常可行的方案,但也暴露出了弊端!一是提高了沟通成本,二是每次修改前端都要重新打包上线,费时费力还不灵活。尽管如此,这种做法在众多项目中非常常见,一般都是通过通过某个特殊字段来达到“动态路由”的效果!比如:
游客与登录后的访问权限不同;
会员等级的权限;
xx应用的体验版;
.......

那有没有更好的方案呢?答案是肯定有的,对于未接触过的看官而言,在使用这个方法的时候难免会踩些坑。其中最大的坑就是:明明后端返回了数据,我也添加进路由了,前端页面咋不更新呢?

2.2、法二:前端通过接口请求后端,拿到数据后动态渲染前端路由

在未上网搜索前,艺灵我是有几个疑问的,不知道各位看官曾经有没有同样的疑问。
疑问一:既然前端要请求接口,那请求的操作发生在什么时候?
疑问二:拿到数据后,通过什么方式将动态数据加进路由中?
疑问三:动态路由中的component模板该如何引入?

带着三个疑问,干货即将出现。

三、疑问解答

3.1、疑问一:既然前端要请求接口,那请求的操作发生在什么时候?

对此疑问,可能有相当大一部分看官认为此操作发生在挂载路由前。实际上,即使页面完全加载,我们也可以通过点击按钮的形式动态添加。不信?视频为证!
视频:先用axios请求接口获取数据,再通过点击操作更新路由.png先用axios请求接口获取数据,再通过点击操作更新路由(友情提示:点击上面的图片即可播放视频)

通过上面的视频演示,我们可以清楚的看到一共经历了两个步骤:
一、手动请求接口拿到要展示的动态导航数据;
二、通过点击“添加动态路由”这个按钮成功将刚获取到的导航添加到了路由中。
当路由添加完毕后,点击路由会发现页面切换正常,此时表示我们操作成功。如果当添加完路由后,页面导航没有更新或出现点击了导航没有切换对应的页面,则说明我们的步骤是有问题的。

3.2、疑问二:拿到数据后,通过什么方法将动态数据加进路由中?

由于是vue项目,此处请求数据就用axios吧。我们在发送axios后得到了数据,虽然取数据很容易,但塞数据就让人头大啊!!!

这里我们需要封装一个new Promise,用回调的方式也是可以的。除此之外,还需要使用(router as any).options.routes.push(动态导航数据)router.addRoutes(动态导航数据)。现在我们只是将动态导航数据添加到了routes中,但页面上的路由并不会动态更新。所以,我们还需要借助vuex来强行更新路由。即:在/store/index.ts文件中的mutations中添加一个方法setDynamicNav,然后使用store.commit('setDynamicNav',routes)的方式强行更新路由数据。

3.3、疑问三:动态路由中的component模板该如何引入?

虽然导航数据可以由后端返回,但路由对应的component模板呢???回到文章开头的需求,由于百科下面的栏目分类都是同样的模板,所以我们可以指定一个模板。但是,若每个栏目对应的模板不同的话,那应该怎么搞呢?

这回还是要跟后端约定下,可以通过后端返回的字段来进行控制。比如:nameid。这样一来,我们知道了模板名,然后再通过字典映射的形式来动态挂载不同的component。特别说明:模板文件不需要提前引入,通过懒加载的方式也是可以的!

四、代码演练

前面已经把实现的逻辑整理了一遍,有兴趣的看官可以自己动手练练。目前为止,看着仍发懵的看官可以继续跟着艺灵的步伐向下看代码。

4.1、@vue/cli创建项目

此处以@vue/cli 3为例,若看官使用的是vue/cli @2,则创建的项目目录略有差别,但不影响我们将要实现的需求。下面是准备工作:
1、在我们日常工作目录中右键;
2、然后点击Git Bash Here
3、接着输入:winpty vue.cmd create dynamic-router并回车;
4、按上下方向键选择一个自己常用的模板,例如:es+prettier模板,选择后按下回车键。
稍等片刻后,当命令窗口中出现Successfully created project dynamic-router时就表示项目就已经创建完毕了。如图:@vue/cli3创建项目.png@vue/cli3创建项目

若想了解更多关于@vue/cli脚手架创建项目的资料,请移步于vue/cli官网查看更多

  1. dynamic-router 项目目录
  2.   public # 静态资源目录
  3.     img # 图片
  4.     favicon.ico # 网站图标
  5.     index.html # html模板
  6.     robots.txt # 爬虫协议
  7.   src # 资源目录,基本上开发过程中都在此目录下进行
  8.     assets # 静态资源
  9.     components # 组件目录
  10.     router # 路由目录
  11.       index.ts # 路由配置文件
  12.     store # vuex状态管理目录
  13.       index.ts # store配置文件
  14.     views # 相关页面
  15.       About.vue # about页面
  16.       Home.vue # home页面
  17.     App.vue # 启动页面
  18.     main.ts # 入口文件
  19.     shims-tsx.d.ts # 允许.tsx 结尾的文件,在 Vue 项目中编写 jsx 代码
  20.     shims-vue.d.ts # 主要用于 TypeScript 识别.vue 文件,Ts 默认并不支持导入 vue 文件
  21.   .browserslistrc # 可以理解为配置的css兼容性(用于支持 Autoprefixer)
  22.   .eslintrc.js # eslint配置
  23.   .gitignore # 忽略要提交的git文件
  24.   babel.config.js # babel-loader配置
  25.   package.json # package 配置
  26.   package-lock.json # 锁定package 配置
  27.   README.md # 描述文件
  28.   tsconfig.json # typescript 配置
  29.   node_modules # 包管理
  30.   .git # git目录

上面的目录及文件全都是自动生成的,为了演示我们今天要讲的动态路由,还需要创建几个文件及文件夹。

4.2、新增后的目录结构:

  1. dynamic-router 动态路由目录
  2.   package.json # 此文件有修改
  3.   vue.config.js # 新增配置文件
  4.   src
  5.     App.vue # 此文件有修改
  6.     mock # 本地mock数据目录,此目录为新增
  7.       dynamicNav.json # 本地模拟数据,真实场景应该直接请求线上接口
  8.     router # 此目录下有新增文件
  9.       dynamicRouter.ts # 获取动态导航的文件(非必须,此处单独创建是为了方便管理)
  10.       index.ts # 有修改
  11.     store # 此目录下文件有修改
  12.       index.ts # 有修改
  13.     views # 此目录下有新增文件
  14.       PageA.vue # pageA页面
  15.       PageB.vue # pageB页面
  16.       PageC.vue # pageC页面
  17.   ...... 其他目录及文件暂时不做修改

4.3、npm run serve 启动项目

此时我们切换到git窗口,然后按提示输入:cd dynamic-router即可进入目录,然后再输入:npm run serve即可成功把项目跑起来。当git窗口中会出现 App running at: - Local: http://localhost:8080/ 字样的代码时就表示我们的项目启动成功了,否则失败。直接在浏览器地址栏中输入:http://localhost:8080即可看到我们的默认项目。需要注意的是,此时的npm run serve并不会自动启动浏览器,我们需要手动修改package.json文件后才能做到自动打开浏览器。

4.4、修改package.json文件,解放双手!

在vs code或其他编辑器中打开package.json文件,通常情况下应该是第6行。或者直接搜索:"serve": "vue-cli-service serve",然后做如下修改。

  1.   "serve": "vue-cli-service serve", /* 修改前长这样 */
  2.   "serve": "vue-cli-service serve --open", /* 修改后的 */

注意黄色高亮部分,只添加了一个--open属性。现在我们切换到git窗口,Ctrl+C终止项目后再次输入npm run serve并回车。此时,神奇的一幕就出现了哈!

4.5、新增本地mock数据

接下来,我们需要在本地构造一下动态导航的数据。没条件的就按文章的方法老老实实创建文件,有条件的可以自己搞接口。

  1. [{
  2.     "id": "1",
  3.     "name": "pageA",
  4.     "title": "动态路由A"
  5.   },
  6.   {
  7.     "id": "3",
  8.     "name": "pageC",
  9.     "title": "动态路由C"
  10.   },
  11.   {
  12.     "id": "2",
  13.     "name": "pageB",
  14.     "title": "动态路由B"
  15.   }
  16. ]

现在“动态导航”的数据已经有了,接下来就是重点了!留个小疑问:“目前动态导航的顺序已经被打乱,应该如何恢复正常的排序呢?”

4.6、axios请求后端接口并拿到动态导航的数据

此时的项目中是没有axios包的,所以我们需要手动安装。切换到刚才的git窗口中,停止当前项目后输入:npm install axios --save-dev并回车。此时我们不需要等待命令跑完,可以先在编辑器中打开/src/router/dynamicRouter.ts并开始写点儿代码了。

  1. import pageA from "../views/PageA.vue";
  2. import pageB from "../views/PageB.vue";
  3. import axios from "axios";
  4. const dictionaryMapping: { [key: string]: any } = {
  5.   "pageA": pageA,
  6.   "pageB": pageB,
  7.   "pageC": () => import(/* webpackChunkName: "pageC" */ "../views/PageC.vue"), /* 懒加载的方式也是可以的 */,
  8. };
  9. const getDynamicRouter = function (callback: any) {
  10.   const url = "/api/dynamicNav.json"; /* 后端接口 */
  11.   axios
  12.     .get(url)
  13.     .then((res: any) => {
  14.       console.log("res=", res);
  15.       callback && callback(res.data);
  16.     })
  17.     .catch((err: any) => {
  18.       console.log("err=", err);
  19.     });
  20.   };
  21. export { dictionaryMapping, getDynamicRouter };

这里使用回调new Promise的方式都可以,毕竟new VueRouter的操作要早于我们请求接口。

由于是本地模拟请求后端数据,所以我们要确保上面的url能正常访问。直接访问上述链接是不成功的,毕竟项目中根本就没有api这个路径!

艺灵你是在逗我吗?本地没有这个路径你请求个啥???

看官莫急,接下来我们来搞个proxy代理即可,这也是个知识点哈!

  1. module.exports = {
  2.   devServer: {
  3.     proxy: {
  4.       "/api": {
  5.         target: "http://localhost:8081", /* 代理服务器的地址 */
  6.         pathRewrite: {
  7.           "^/api": "/mock" /* 访问/api/xxx时定向到/mock */
  8.         }
  9.       }
  10.     }
  11.   }
  12. };

proxy代理已经设置好了,可是我们本地并没有服务器呀,这可要咋整呢???

若看官本地没有安装http-server的话需要安装下,或者若本地有wampphpStudy等环境的话也是可以的。为了省事儿,此处以http-server为例。

  1. npm install http-server -g /* -g 全局安装 */

在终端中输入上面一行代码并回车,稍等片刻即可安装成功。安装成功后我们进入项目中的src目录,并在此目录中使用终端命令。在终端中输入:http-server -p 8081后回车即可启动一个本地服务器。当屏幕上出现http://127.0.0.1:8081字样时则表示启动成功,否则有可能是端口冲突,具体看提示。如图:http-server成功启动本地服务器.pnghttp-server成功启动本地服务器

现在,我们经历了很多步骤,基本上已经万事俱备了,就差最后一步了,激动不激动?!

看官先别激动,凡事要悠着点儿。

接下来就是修改/src/router/index.ts文件了,我们要把刚从后端获取到的“动态导航”数据添加进来,这样我们就成功啦!

4.7、把动态导航数据转换成符合路由规则后添加到原路由中

  1. import { dictionaryMapping, getDynamicRouter } from "./dynamicRouter"; /* 导入文件 */
  2. ...... /* 原来的代码不做修改 */
  3. const router = new VueRouter({
  4.   ...... /* 原来的代码不做修改 */
  5. });
  6. /* 新增代码 20200712113825 start */
  7. getDynamicRouter((res: any) => {
  8.   const result = res.map((item: any) => {
  9.     return {
  10.       path: "/" + item.name,
  11.       name: item.name,
  12.       meta: {
  13.         title: item.name
  14.       },
  15.       id: item.id,
  16.       component: dictionaryMapping[item.name]
  17.     }
  18.   }); /* 输出结构跟routes一样的一个数组 */
  19.   (router as any).options.routes.push(...result); /* 把动态路由追加到原路由中 */
  20.   router.addRoutes(result); /* 动态添加更多的路由规则(来来来,这一句话谁能看的懂?我敬你是条汉子!)出处请访问→→ 官方文档 */
  21. })
  22.   第三处修改(答案在下面)
  23. /* 新增代码 20200712113825 end */
  24. export default router;

这里有3处需要注意的地方,分别是:
1、result
2、(router as any).options.routes.push(...result);router.addRoutes(result)
3、第三处修改(答案在下面);

当我们启动项目后打开控制台,通过Vue Devtools插件可以看到有5个路由。如图:Vue Devtools中已经显示我们的动态路由添加成功了.pngvueDevtools中已经显示我们的动态路由添加成功了

看样子我们已经成功了,现在只需要修改App.vue文件,把页面上的静态router-link搞成动态输出即可。当然,你直接在页面上写死5个路由也是可以的,但这并没有太大意义。

4.8、修改App.vue,循环输出路由

继续在编辑器中打开App.vue文件,按下方提示进行修改。

  1. /* 代码修改前 start */
  2. <div id="nav">
  3.   <router-link to="/">Home</router-link> |
  4.   <router-link to="/about">About</router-link>
  5. </div>
  6. /* 代码修改前 end */
  7. /* 代码修改后 start */
  8. <div id="nav">
  9.   <router-link v-for="(item, index) in $router.options.routes" :key="index" :to="item.path">\{\{item.name\}\} | </router-link> |
  10. </div>
  11. /* 代码修改后 end */

友情提示:上面代码中的\反斜线是为了防止在文章中转码,看官复制后替换为空即可。

接下来就是见证奇迹的时刻了!刷新浏览器你会发现页面上还是只有默认的两个导航,如图:刷新页面后动态路由没有显示出来.png刷新页面后动态路由没有显示出来

懵逼不?但是,如果我们直接在地址栏中输入/pageA并回车,神奇的一幕将会出现,我们添加的动态路由又显示出来了!幻觉???[:捂脸哭]
如图:直接在地址栏中输入动态路由是可以访问成功的.png直接在地址栏中输入动态路由是可以访问成功的

这可咋办呢?此时再祭出最后的杀手锏-- vuex

4.9、Vuex状态管理

  1. ...... /* 原代码不做修改 */
  2. export default new Vuex.Store({
  3.   state: {
  4.     dynamicNav: [], /* 存储路由导航*/
  5.   },
  6.   mutations: {
  7.     setDynamicNav (state: any, value: []) {
  8.       state.dynamicNav = value;
  9.     } /* 修改dynamicNav的值 */
  10.   },
  11.   actions: {},
  12.   modules: {}
  13. });

store中,我们添加了一个方法setDynamicNav用来给state.dynamicNav进行赋值。在App.vue页面中直接调用state.dynamicNav即可。

  1. /* 代码修改前 start */
  2. <div id="nav">
  3.   <router-link v-for="(item, index) in $router.options.routes" :key="index" :to="item.path">\{\{item.name\}\} | </router-link>
  4. </div>
  5. /* 代码修改前 end */
  6. /* 代码修改后 start */
  7. <div id="nav">
  8.   <router-link v-for="(item, index) in $store.state.dynamicNav" :key="index" :to="item.path">\{\{item.name\}\} | </router-link>
  9. </div>
  10. /* 代码修改后 end */

那现在是不是已经成功了呢?

显然没有,因为我们并没有调用setDynamicNav方法。

那在何处调用呢?

继续回到4.7.1,修改/src/router/index.ts文件。

  1.   store.commit("setDynamicNav", (router as any).options.routes); /* 这就是前面提到的第三处修改 */

现在,我们再来看下页面,可以看到已经成功显示5个导航了。如图:动态导航已经成功添加到页面上.gif动态导航已经成功添加到页面上

五、下载完整源码

  1. 尊重劳动成果,下载此源码需要20积分。积分或通过注册或购买的形式获取,不喜勿点下面的链接!
    下载地址→→→:[vue3源码]vue-cli3实战之axios请求后端动态更新前端路由  积分:20 大小:198KB

源码中会有必要注释和本篇文章中的所有示例,若看官在下载后有不明白的地方,可通过网页右侧菜单与艺灵联系。

六、最后

至此,动态路由的需求终于被我们搞定了!

在实现过程中,艺灵我也踩过不少坑走过不少弯路。虽然方法矬了点儿,但也算是实现需求了。感谢以下资料:
使用 Vue Router 的 addRoutes 方法实现动态添加用户的权限路由
请教一个有关addroutes的问题

好像前面还有一个小疑问没有解答:如何给导航排序呢?欲之后事如何,请听下回分解《@vue/cli项目实战之如何给无限级嵌套的导航添加激活样式

转载声明:
  若亲想转载本文到其它平台,请务必保留本文出处!
本文链接:/xwzj/2020-07-15/vue-cli3-router-addRoutes.html

若亲不想直保留地址,含蓄保留也行。艺灵不想再看到有人拿我的技术文章到他的地盘或者是其它平台做教(装)程(B)而不留下我的痕迹。文章你可以随便转载,随便修改,但请尊重艺灵的劳动成果!谢谢理解。

亲,扫个码支持一下艺灵呗~
如果您觉得本文的内容对您有所帮助,您可以用支付宝打赏下艺灵哦!

Tag: @vue/cli3 vue项目实战 动态路由 router.addRoutes axios vuex http-server Promise proxy代理 

上一篇: 关于鄙站在2020.7.8~7.12号之间无法访问的说明   下一篇: @vue/cli3+typescript项目实战之给无限级嵌套的导航添加激活样式(上)

评论区