详解Vue路由自动注入实践


Posted in Javascript onApril 17, 2019

什么是路由自动注入

路由自动注入概念学习自nuxt,我们不需要在 router.js 中每次手动输入代码引入模块而是自动根据 文件目录格式 生成 router.js

我们把这个功能独立成一个 webpack 插件,并对相关功能进行了完善,而且实现了 vue-router 的所有核心功能

更详细使用指南和文档可以查看我们的 github仓库

举一个简单的列子,比如你的目录长这样

src
├── views
│ ├── Login
│ │ └── Index.vue
│ └── User
│  ├── Account
│  │ └── Index.vue
│  ├── Home
│  │ └── Index.vue
│  └── Index.vue

规则很简单,如果目录的一层是 Index.vue ,则目录名便是当前的路由名字,如果是子文件夹则是第二层路由,之后自动生成的 router.js 会长成这样

{
 component: () =>
 import('@/views/Login/Index.vue'),
 name: 'login',
 path: '/login'
},
{
 component: () =>
 import('@/views/User/Index.vue'),
 name: 'user',
 path: '/user'
},
{
 component: () =>
 import('@/views/User/Account/Index.vue'),
 name: 'user-account',
 path: '/user/account'
},
{
 component: () =>
 import('@/views/User/Home/Index.vue'),
 name: 'user-home',
 path: '/user/home'
}

这里值得一提的是其实生成的 router.js 是没有必要加入到版本控制当中的,因为不论在开发( development )还是生产( production )第一次构建项目都会自动生成,比如你项目用到了 giteslint ,那么应该把它放在 .gitignore.eslintignore

为什么使用路由自动注入

方便

不用每次去引用模块,只用创建文件夹, router.js 会自动生成

统一路由命名

详解Vue路由自动注入实践

如果有完整的 code review 这个问题是不会存在的,但我们稍微做了一点简便,只要 code review 文件夹的命名就好了,最终生成的路由path会以驼峰命名,生成的name会以驼峰命名并且以连字符 - 连接不同层级的路由

统一路由层级

详解Vue路由自动注入实践

如图片中的列子,我们无法从文件的命名去判断路由到底在几级,而且经常写的时候,明明是2级或3级路由却和1级路由在一层路由下,这是很不规范而且与逻辑不符的

对比一下使用自动注入划分层级后的路由

src/views
├── Index.vue
├── NotFound.vue
├── Withdraw
 <!-- 第一级 -->
│ ├── Index.vue
│ └── Result
│  ├── Description
   <!-- 第三级 -->
│  │ └── Index.vue
  <!-- 第二级 -->
│  └── Index.vue
└── WithdrawHistory
 <!-- 第一级 -->
 └── Index.vue

可以从目录结构看出路由的层级

我们再来看看生成的路由,不同层级的路由名字通过连字符 - 连接,层级很清晰

{
 component: () => import('@/views/Withdraw/Index.vue'),
 name: 'withdraw',
 path: '/withdraw'
},
{
 component: () => import('@/views/Withdraw/Result/Index.vue'),
 name: 'withdraw-result',
 path: '/withdraw/result'
},
{
 component: () => import('@/views/Withdraw/Result/Description/Index.vue'),
 name: 'withdraw-result-description',
 path: '/withdraw/result/description'
},
{
 component: () => import('@/views/WithdrawHistory/Index.vue'),
 name: 'withdrawHistory',
 path: '/withdrawHistory'
},

为什么选择 vue-router-invoke-webpack-plugin

完善的单元测试

详解Vue路由自动注入实践

types支持

详解Vue路由自动注入实践

vue-router-invoke-webpack-plugin 中独特的路由划分思维

当我们的页面过多的时候,比如项目有60多个甚至70多个单页面,文件不可能会放在一个目录下,一般这种时候,我们会按 功能 将相似功能的路由放在一个目录下,我们之前也是这么做的,其实这么做也是没啥问题的,但在路由自动注入下,我们提出了另外一种思路按路由 层级 划分

什么是层级划分呢,简单的一句话就是根据页面所在的相对url地址进行划分,举个列子,我们的首页如下

详解Vue路由自动注入实践

首页的路由为 / ,我们把首页当作根路由,那么可以进入的一级路由分别为 提现 提现记录 分成数据 等,点击提现后,我们进入了提现路由 /withdraw

详解Vue路由自动注入实践

进入提现页面后,会有两处可点击,这两处便是二级页面,放在一级页面的子文件夹中,按刚才的说法,路由目录(截取部分)便是这样

src/views
├── Bank
 <!-- 银行卡管理 -->
│ └── Index.vue
├── DivideData
 <!-- 分成数据 -->
│ └── Index.vue
<!- 首页 --->
├── Index.vue
<!-- 404路由 -->
├── NotFound.vue
├── Withdraw
│ ├── BankDetails
  <!-- 提现中查看银行卡信息 -->
│ │ └── Index.vue
│ ├── Description
  <!-- 提现说明 -->
│ │ └── Index.vue
 <!-- 提现页面 -->
│ └── Index.vue
└── WithdrawHistory
  <!--提现记录 -->
 └── Index.vue

其实一般这么分下来,相似功能的是会在一个文件夹下面的,也实现了按功能分路由的思路,而且这种层级划分是一目了然的,很容易可以看出路由的从属关系

但有时候也会遇到一个麻烦,就是有些页面可能出现在当前层级下面,也可能出现在另外一个层级下面,按功能分的时候也有这种,就是功能可能存在于两个功能点之间,这种情况其实可以考虑下在哪个层级的权重重一点或者从用户的点击习惯考虑,哪个位置进去会多一点就放在哪个层级下面

vue-router-invoke-webpack-plugin 中独特的文件结构

也许大家会有疑问,为啥非要写成 Index.vue 并多加一层文件夹封装,直接命名 vue 文件不好吗,用过 nuxt 的同学可能也会感觉到这一点的区别,这也是我们在 nuxt 的基础上增加的一个 feature ,为了更友好的封装一个单页面

举个列子,如果你的项目没有引用ui库,很多业务组件需要自己写,除了常用的组件会放在目录最外面的 components 文件,其余的对应一个单页面的业务组件你会放在哪里呢,这就是我们预留的位置,比如一个目录结构如下

src/views
├── Audit
│ ├── Index.vue
│ ├── components
│ │ └── AuditItem.vue
│ └── images
│  └── AuditIntro.png

Audit 是我们的审批页面,其中用到了一个只有当前页面所用的 AuditItem.vue 组件,也引用了一个只有当前页面所用到的图片 AuditIntro.png ,独特的文件结构就是为了这种需求而生的,当前页面的组件图片放在一个文件夹中会更清晰,但值得一提的是,你也需要在插件中设置 ignore 去忽略掉不被我们解析的目录,比如这样

plugins: [
 new VueRouterInvokeWebpackPlugin({
 dir: 'src/views',
 alias: '@/views',
 language: 'javascript',
 ignore: ['images', 'components', 'template.vue']
 })
];

那么 images components template.vue 会被忽略不解析

聊一聊路由权限控制

关于前端控制路由权限,前段时间看到过一个文章,感觉实现思路稍微复杂了点,其实有一个比较简单的思路,就是后端给定当前用户没有权限的路由,然后前端在 beforeEach 钩子中去匹配,如果匹配到没有权限则直接跳404或者没有权限的页面就行了,如果用 vue-router-invoke-webpack-plugin 写会这么写

apis.getForbiddenRoute

export default {
 // 请求当前没有权限的路由列表
 async getForbiddenRoute() {
 return ['/single/user'];
 }
};
plugins: [
 new VueRouterInvokePlugin({
  // 观察的目录
  dir: 'demos/src',
  // 观察目录的别名
  alias: '@/src',
  // 当前语言
  language: 'javascript',
  // 生成router.js的位置
  routerDir: 'demos',
  // 忽略文件夹
  ignore: ['images', 'template.vue', 'components', 'notfound.vue'],
  // 404路由地址
  notFound: '@/src/NotFound.vue',
  // 引用的模块
  modules: [
  {
   name: 'apis',
   package: '@/apis'
  }
  ],
  // 同scrollBehavior
  scrollBehavior: (to, from, savedPosition) => {
  if (savedPosition) {
   return savedPosition;
  } else {
   return { x: 0, y: 0 };
  }
  },
  <!-- 主要是这段代码 -->
  /* eslint-disable */
  beforeEach: async (to, from, next) => {
  // 通过绑定在静态属性上的_cachedForbiddenRoute判断是否请求过接口
  if (!Vue._cachedForbiddenRoute) {
   Vue._cachedForbiddenRoute = [];
   await apis.getForbiddenRoute().then(res => {
   Vue._cachedForbiddenRoute = res;
   });
  }
  // 当当前页面的地址存在于禁止访问的列表中,则直接跳转到404页面
  if (Vue._cachedForbiddenRoute.includes(to.path)) {
   next({
   name: 'notFound'
   });
  } else {
   next();
  }
  }
 }),
]

但话说回来,任何实现思路,前端获取的接口数据想篡改还是能绕过去的,所以还是得后端再防一层

项目实现思路

项目实现不太复杂,但要照顾到的地方很多

  • 基本路由
  • 动态路由
  • 多层嵌套路由
  • 多层嵌套动态路由
  • meta替代品
  • 文件不符合规则的友好处理
  • 命名转换统一
  • node中原生fs模块十分不友好

要考虑的小细节还挺多的,特别是当路由过于复杂的情况

但node的 fs 的坑点是我没有想到的,特别是在跨平台上,所以我们舍弃了使用原生的 fs 模块,用 chokidarfs-extra 替代了 fs 的部分功能

前段时间也在学习 vue 的ast语法树,所以学习了下思路去尝试构建一棵ast,不过方法还是有区别的,vue构建语法树是通过正则拆分了 元素开始标签 元素属性 元素字符 元素结束标签 等然后拼接而成的,拼接的过程特别复杂,这个项目会简单很多,直接通过文件读取递归遍历目录就可以生成一棵 ast

详解Vue路由自动注入实践

然后通过语法树去构建字符串的 router.js ,构建的过程还比较麻烦,最后将构建好的字符串写入文件就大功告成了

项目还需要完善的地方

单元测试

现在的单元测试覆盖率已经100%了,但我觉得仍然有比较多稍微复杂的情况没有写到,之后会不仅看单元测试覆盖率,而是按想到需要测试得功能点去补充完整

测试环境

项目接入的是 circleci ,没法在 windows 下测试,平常用的开发环境也是 mac ,所以测试环境方面之后还要去研究研究其他可以支持windows的ci工具,并对不同node版本进行测试

其实现在在 windows 下也有一个bug,但我发现 nuxt 也有这个bug,所以感觉可能这不是一个bug或许是一个feature,之后也会去提一个 issue 去请教一下,也不知道是不是我电脑的问题,简单说就是 fs.watch 去监听文件目录的时候(但这里其实用的是 chokidar ,不过都一样)当去改变之前已有的文件目录的名字是改不了的, windows 下会提示你什么当前文件被引用了,需要结束掉进程这个文件名才能被修改

更友好的支持

项目目前支持的是node版本> 8.15.1,仅支持 webpack4 ,之后会支持 webpack3 和即将到来的 webpack5

更多的功能

除了刚才提到的一个简单路由的列子和设置忽略项,我们还支持了 vue-router 的其他核心功能,包括 动态路由 嵌套路由 全局路由守卫 meta替代品 等其他功能,相关功能点都写在了我们开源仓库的文档中,详细的用法和注意事项,可以访问我们的 github仓库 ,如果觉得项目还不错的话,可以给我们点一颗小星星,当然如果你在使用中发现了和预期不太一样的情况或者bug可以随时给我们提 issue

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Prototype String对象 学习
Jul 19 Javascript
setTimeout和setInterval的区别你真的了解吗?
Mar 31 Javascript
可自己添加html的伪弹出框实现代码
Sep 08 Javascript
jquery为页面增加快捷键示例
Jan 31 Javascript
js脚本分页代码分享(7种样式)
Aug 19 Javascript
JS实现的倒计时效果实例(2则实例)
Dec 23 Javascript
JS实现添加,替换,删除节点元素的方法
Jun 30 Javascript
深入理解JavaScript函数参数(推荐)
Jul 26 Javascript
解决ajax不能访问本地文件问题(利用js跨域原理)
Jan 24 Javascript
详解webpack 入门总结和实践(按需异步加载,css单独打包,生成多个入口文件)
Jun 20 Javascript
详解webpack + vue + node 打造单页面(入门篇)
Sep 23 Javascript
解决微信小程序云开发中获取数据库的内容为空的方法
May 15 Javascript
在Vue项目中使用jsencrypt.js对数据进行加密传输的方法
Apr 17 #Javascript
js的继承方法小结(prototype、call、apply)(推荐)
Apr 17 #Javascript
详解JavaScript的内存空间、赋值和深浅拷贝
Apr 17 #Javascript
Vue源码探究之虚拟节点的实现
Apr 17 #Javascript
ES6知识点整理之数组解构和字符串解构的应用示例
Apr 17 #Javascript
ES6知识点整理之对象解构赋值应用示例
Apr 17 #Javascript
ES6知识点整理之函数对象参数默认值及其解构应用示例
Apr 17 #Javascript
You might like
关于PHP中操作MySQL数据库的一些要注意的问题
2006/10/09 PHP
如何在PHP程序中防止盗链
2008/04/09 PHP
解决PHP在DOS命令行下却无法链接MySQL的技术笔记
2010/12/29 PHP
10 个经典PHP函数
2013/10/17 PHP
php倒计时出现-0情况的解决方法
2016/07/28 PHP
php socket通信简单实现
2016/11/18 PHP
js对象数组按属性快速排序
2011/01/31 Javascript
javascript温习的一些笔记 基础常用知识小结
2011/06/22 Javascript
利用百度地图JSAPI生成h7n9禽流感分布图实现代码
2013/04/15 Javascript
分享JavaScript与Java中MD5使用两个例子
2015/12/23 Javascript
莱鸟介绍javascript onclick事件
2016/01/06 Javascript
Bootstrap导航条可点击和鼠标悬停显示下拉菜单
2016/11/25 Javascript
webpack构建vue项目的详细教程(配置篇)
2017/07/17 Javascript
详解Vue用cmd创建项目
2019/02/12 Javascript
vue的注意规范之v-if 与 v-for 一起使用教程
2019/08/04 Javascript
浅析webpack-bundle-analyzer在vue-cli3中的使用
2019/10/23 Javascript
详解Vue 数据更新了但页面没有更新的 7 种情况汇总及延伸总结
2020/05/28 Javascript
js 数组当前行添加数据方法详解
2020/07/28 Javascript
解决ant design vue 表格a-table二次封装,slots渲染的问题
2020/10/28 Javascript
跟老齐学Python之使用Python查询更新数据库
2014/11/25 Python
Python多进程并发(multiprocessing)用法实例详解
2015/06/02 Python
Python上传package到Pypi(代码简单)
2016/02/06 Python
Python数据分析之双色球中蓝红球分析统计示例
2018/02/03 Python
用Python将一个列表分割成小列表的实例讲解
2018/07/02 Python
Linux 修改Python命令的方法示例
2018/12/03 Python
Python变量访问权限控制详解
2019/06/29 Python
解决Python3用PIL的ImageFont输出中文乱码的问题
2019/08/22 Python
pyecharts动态轨迹图的实现示例
2020/04/17 Python
使用sublime text3搭建Python编辑环境的实现
2021/01/12 Python
python中四舍五入的正确打开方式
2021/01/18 Python
应用艺术专业个人的自我评价
2014/01/03 职场文书
事业单位财务人员岗位职责
2015/04/14 职场文书
2015年仓库管理员工作总结
2015/04/21 职场文书
Netty结合Protobuf进行编解码的方法
2021/06/26 Java/Android
css3新特性的应用示例分析
2022/03/16 HTML / CSS
《原神》新角色演示“神里绫人:林隐泓洄” 宠妹狂魔
2022/04/03 其他游戏