详解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 相关文章推荐
一个对于js this关键字的问题
Jan 09 Javascript
取得窗口大小 兼容所有浏览器的js代码
Aug 09 Javascript
JavaScript实现自己的DOM选择器原理及代码
Mar 04 Javascript
javascript验证身份证号
Mar 03 Javascript
JS实现网页Div层Clone拖拽效果
Sep 26 Javascript
JavaScript实现拖拽元素对齐到网格(每次移动固定距离)
Nov 30 Javascript
angular+ionic 的app上拉加载更新数据实现方法
Jan 16 Javascript
cocos creator Touch事件应用(触控选择多个子节点的实例)
Sep 10 Javascript
使用ionic(选项卡栏tab) icon(图标) ionic上拉菜单(ActionSheet) 实现通讯录界面切换实例代码
Oct 20 Javascript
Vue 中mixin 的用法详解
Apr 23 Javascript
Angular单元测试之事件触发的实现
Jan 20 Javascript
微信小程序开发之获取用户手机号码(php接口解密)
May 17 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中通过Ajax如何实现异步文件上传的代码实例
2011/05/07 PHP
php fputcsv命令 写csv文件遇到的小问题(多维数组连接符)
2011/05/24 PHP
php+ajax导入大数据时产生的问题处理
2014/06/11 PHP
PHP实现无限极分类图文教程
2014/11/25 PHP
php获取CSS文件中图片地址并下载到本地的方法
2014/12/02 PHP
Yii中CGridView禁止列排序的设置方法
2016/07/12 PHP
让低版本浏览器支持input的placeholder属性(js方法)
2013/04/03 Javascript
Jquery原生态实现表格header头随滚动条滚动而滚动
2014/03/18 Javascript
js 获取时间间隔实现代码
2014/05/12 Javascript
跟我学习javascript的作用域与作用域链
2015/11/19 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之主动触发事件和模拟冒泡处理
2015/11/24 Javascript
学习javascript面向对象 理解javascript对象
2016/01/04 Javascript
JavaScript学习笔记之数组去重
2016/03/23 Javascript
使用jQuery制作Web页面遮罩层插件的实例教程
2016/05/26 Javascript
javascript中数组(Array)对象和字符串(String)对象的常用方法总结
2016/12/15 Javascript
ES6正则表达式的一些新功能总结
2017/05/09 Javascript
Bootstrap table学习笔记(2) 前后端分页模糊查询
2017/05/18 Javascript
vue.js中过滤器的使用教程
2017/06/08 Javascript
三种Webpack打包方式(小结)
2018/09/19 Javascript
使用vue实现各类弹出框组件
2019/07/03 Javascript
Vue + Scss 动态切换主题颜色实现换肤的示例代码
2020/04/27 Javascript
微信小程序通过websocket实时语音识别的实现代码
2020/08/19 Javascript
vue实现点击出现操作弹出框的示例
2020/11/05 Javascript
原生JavaScript实现幻灯片效果
2021/02/19 Javascript
python实现多线程采集的2个代码例子
2014/07/07 Python
Django中针对基于类的视图添加csrf_exempt实例代码
2018/02/11 Python
python对常见数据类型的遍历解析
2019/08/27 Python
基于python实现操作redis及消息队列
2020/08/27 Python
python 制作网站筛选工具(附源码)
2021/01/21 Python
介绍下WebSphere的安全性
2013/01/31 面试题
房产代理公证处委托书
2014/04/04 职场文书
教师节演讲稿
2014/05/06 职场文书
会计专业应届生自荐信
2014/06/28 职场文书
干部作风建设年活动剖析材料
2014/10/23 职场文书
职场新人刚入职工作总结该怎么写?
2019/05/15 职场文书
Redis Lua脚本实现ip限流示例
2022/07/15 Redis