手摸手,带你用vue撸后台 系列五(v4.0新版本)

前言

vue-element-admin 从 2017.04.17提交第一个 commit 以来,维护至今已经有两年多的时间了了,发布了四十多个版本,收获了三万多的 stars,远远的超出了自己的预期。距离上次手摸手系列教程也已经过去了很久,主要因为:作为一个个人开源项目,维持它已经很难了,所以真的没啥时间写详细的教程了,光是维护项目 文档 就让我很头疼了。也有不少人建议我出付费教学视频,但我个人还是更愿意把这个时间投入到维护开源项目之中吧。

本篇教程主要是趁着vue-element-admin发布了 v4.0 新版本,首先来简单说一下4.0版本做了哪些改动和优化。后半部分则会分享一些新的思考和一些小技巧吧。之前几篇手摸手文章都差不多两年前的了,但随着技术的不断发展迭代,很多之前的不能解决的问题也是都是有了新的解决方案的,同时也会出现一些新的问题和挑战。

4.0 做了什么

首先大概说一下4.0版本做了些什么,通过 pull request 可以看出这是一次比较大的升级,有大概 170 多次的 commits,200 多个文件的改动。其中最大的改变是接轨 vue 社区,直接通过 vue-cli来进行构建,省去了很多额外繁琐的配置(下文会介绍),并修改了之前 mock 数据的方案,本地改用 mock-server 来解决之前mockjs带来的各种问题。同时增加了 jest 单元测试,使用了async/await,增加了可视化配置权限,增加了自定义布局等等,优化了原先addRoutes的权限方案,支持不刷新页面更新路由等等功能。具体的可看 github release。接下来我们着重来分析一下这几个功能。

vue-cli@3

本身配置方面没有啥特别好说的,官方文档已经写得很详细了。这次更新基本上就是基于 webpack-chain 把之前的 webpack 配置迁移了一遍,因为vue-cli帮你做了很多默认配置,所有可以省去一些代码。当然这种out-of-the-box的工具利弊也很明显,它能快速上手,大部分简单场景无需任何额外配置基本就能用了。但对于复杂度高的或者自定义性强的项目来说,配置复杂度可能没有减少太多。它要求你不仅要对 webpack 或者相关工程化的东西很很熟悉,你还要对vue-cli做的一些默认配置和参数也有有一定了解,时不时要去看一下源码它到底干了啥,有的时候它的一些 plugin 出现了问题还不太好解决。而且说实话 webpack-chain 的书写也是有些门槛的,大部分情况下我也很难保证自己的配置写对的,还好官方提供了inspec功能,能让配置简单了不少。当你想知道自己的 vue-config.js 里的配置到底对不对的时候,你可以在命令行里执行vue inspect > output.js,它会将你最终生成的config展现在output.js之中,不过它默认显示的是开发环境的配置。如果你想查看其它环境的配置可以通过vue inspect --mode production > output.js。在写构建配置的时候这个功能很有帮助,同时也能帮助你了解vue-cli在构建时到底帮你做了些什么。

其它还有些需要注意的如:环境变量 必须以VUE_APP_开头啊,怎么设置polyfill啊,怎么配置各种各样的loader啊,就不展开了,文档或者社区都有很多文章了。具体配置可以参考 vue.config.js

这里还有一个黑科技,看过我之前文章的小伙伴应该还有印象,我一般在开发环境是不使用路由懒加载的,因为这样会导致热更新速度变慢,具体的可以看之前的 文章,在vue-cli@3中可以更简单的实现,你只要在.env.development环境变量配置文件中设置VUE_CLI_BABEL_TRANSPILE_MODULES:true就可以了。它的实现逻辑和原理与之前还是一样的,还是基于 plugins babel-plugin-dynamic-import-node 来实现的。之所以在vue-cli中只需要设置一个变量就可以了,是借用了vue-cli它的默认配置,它帮你代码都写好了。通过阅读 源码 可知,vue-cli会通过VUE_CLI_BABEL_TRANSPILE_MODULES这个环境变量来区分是否使用babel-plugin-dynamic-import-node,所以我们只要开其它就可以。虽然它的初衷是为了单元测试的,但正好满足了我们的需求。

总的来说,vue-cli对于大部分用户来说还是省去了一些繁琐的配置的。如果你使用本项目的话,基本也不需要做其它过多的额外配置的。

redirect 刷新页面

在不刷新页面的情况下,更新页面。这个 issue 两年前就提出来了,之前的文章里面也提供了一个 解决方案。在这里分享一下,我目前使用的新方案。

// 先注册一个名为 `redirect` 的路由
// 手动重定向页面到 '/redirect' 页面
const { fullPath } = this.$route
this.$router.replace({
  path: '/redirect' + fullPath
})

当遇到你需要刷新页面的情况,你就手动重定向页面到redirect页面,它会将页面重新redirect重定向回来,由于页面的 key 发生了变化,从而间接实现了刷新页面组件的效果。

addRoutes && removeRoutes

看过我之前文章的人肯定知道,我目前 vue 项目的权限控制都是通过 addRoutes来实现的。简单说就是:用户登录之后会返回一个权限凭证Token,用户在根据这个Token去问服务端询问自己的权限,辟如服务端返回权限是['editor'],前端再根据这个权限动态生成他能访问的路由,再通过addRoutes进行动态的路由挂载。具体的代码可见 permission.js

但这个方案一直是有一个弊端的。那就是动态添加的路由,并不能动态的删除。这就是导致一个问题,当用户权限发生变化的时候,或者说用户登出的时候,我们只能通过刷新页面的方式,才能清空我们之前注册的路由。之前老版本的 vue-element-admin就一直采用的是这种方式。虽然能用,但作为一个 spa,刷新页面其实是一种很糟糕的用户体验。但是官方也迟迟没有出相关的 remove api,相关 issue

后来发现了一种 hack 的方法,能很好的动态清除注册的路由。先看代码:

手摸手,带你用vue撸后台 系列五(v4.0新版本)_第1张图片

它的原理其实很简单,所有的 vue-router 注册的路由信息都是存放在matcher之中的,所以当我们想清空路由的时候,我们只要新建一个空的Router实例,将它的matcher重新赋值给我们之前定义的路由就可以了。巧妙的实现了动态路由的清除。
现在我们只需要调用resetRouter,就能得到一个空的路有实例,之后你就可以重新addRoutes你想要的路由了。完整的代码实例 router.js,resetRouter

Mock 数据

如果你在实际开发中,最理想的前后端交互方式当然是后端先帮我们 mock 数据,然后前端开发。但现实很骨感,总会因为种种原因,前端需要自己来 mock 假数据。尤其是我的几个开源项目,都是纯前端项目,根本没有后端服务。
在之前的文章中也介绍过,vue-element-adminvue-admin-template 使用的是 MockJS 和 easy-mock 这两个库。但实际用下来两者都有一些问题。

  • MockJs

    它的原理是: 拦截了所有的请求并代理到本地,然后进行数据模拟,所以你会发现 network 中没有发出任何的请求。但它的最大的问题是就是它的实现机制。它会重写浏览器的XMLHttpRequest对象,从而才能拦截所有请求,代理到本地。大部分情况下用起来还是蛮方便的,但就因为它重写了XMLHttpRequest对象,所以比如progress方法,或者一些底层依赖XMLHttpRequest的库都会和它发生不兼容,可以看一下我项目的 issues,就知道多少人被坑了。

    它还有一个问题:因为是它是本地模拟数据,实际上不会走任何网络请求。所以本地调试起来很蛋疼,只能通过console.log来调试。就拿vue-element-admin来说,想搞清楚 getInfo()接口返回了什么数据,只能通过看源码或者手动 Debug 才能知道。

  • Easy-Mock

    这个项目刚出的时候用的人比较少,还真的挺好用的。天然支持跨域,还是支持MockJs的所有语法,我在之前也推荐过。但因为用的人多了,它的免费服务会经常的挂,可以说天天挂。。。但毕竟人家这是免费的服务,也不能苛求什么,官方的建议是自己搭建服务。如果你的公司整体搭建一个这样的 mock 服务的话也是一个不错的选择。但大部分人可能还是没有这个技术条件的。

新方案

所以我一直在寻求一个更好的解决方案,我也去体验了其它很多 mock api 服务,如 mockapi、Mocky 等等。总之体验都不能满足我的需求。

v4.0版本之后,在本地会启动一个mock-server来模拟数据,线上环境还是继续使用mockjs来进行模拟(因为本项目是一个纯前端项目,你也可以自己搭建一个线上 server 来提供数据)。不管是本地还是线上所以的数据模拟都是基于mockjs生成的,所以只要写一套 mock 数据,就可以在多环境中使用。

该方案的好处是,在保留 mockjs的优势的同时,解决之前的痛点。由于我们的 mock 是完全基于webpack-dev-serve来实现的,所以在你启动前端服务的同时,mock-server就会自动启动,这里还通过 chokidar 来观察 mock 文件夹内容的变化。在发生变化时会清除之前注册的mock-api接口,重新动态挂载新的接口,从而支持热更新。有兴趣的可以自己看一下代码 mock-server.js。由于是一个真正的server,所以你可以通过控制台中的network,清楚的知道接口返回的数据结构。并且同时解决了之前mockjs会重写 XMLHttpRequest对象,导致很多第三方库失效的问题。

在本地开发环境中基于webpack-dev-serveafter这个middleware中间件,在这里自动读取你的 mock文件,模拟出 REST API,它最大的好处是,完全不需要什么额外的工作,完全基于webpack-dev-serve就能实现。如果你还是想单独启动一个serve也是可以的,完全可以引入一个express或者其它插件来启动一个 mock-serve。

我们模拟数据有了,现在要做的事情就是,将我们的接口代理到我们的 mock 服务上就好了,这里我们使用webpack-dev-serve自带的 proxy进行接口代理。

proxy: {
      // xxx-api/login => mock/login
      [process.env.VUE_APP_BASE_API]: {
        target: `http://localhost:${port}/mock`,
        changeOrigin: true,
        pathRewrite: {
          ['^' + process.env.VUE_APP_BASE_API]: ''
        }
      }
    }

snippets 自动生成代码片段

平时日常工作中,做最多的就是写业务模块和组件。当每次新开一个view或者component的时候都需要手动创建一个新.vue文件,然后再创建