基于 Vue CLI 搭建脚手架。
本文作者:阿商、周泽斌、高智恒
脚手架的目标
统一化:统一模块输入输出
能够快速生成适用于自身的新项目的目录模板(项目脚手架)。
提升开发效率,减少相同功能的组件重复开发(模块脚手架)。
标准化:建立模型标准,统一认知
多个Team实现统一的标准化,降低因不同的项目规范和标准带来的理解成本。
自动化:减少人员参与减少成本,提高交付效率
提高项目的构建效率,减少打包依赖。
脚手架的搭建
团队目前技术栈主要使用 Vue ,所以脚手架也是基于 Vue CLI 的基础上搭建的。
Vue CLI 官方文档:https://cli.vuejs.org/zh/guide/installation.html 。
1、全局安装 vue-cli
全局安装完 vue-cli 后,就可以在命令行中使用 vue 命令了。熟悉 vue-cli 的小伙伴们都知道,在开启一个新项目前需要使用 vue init webpack xxx 来快速生成一个模板项目。
但是在实际的开发过程中,我们往往会根据团队或者业务的需要对目录结构进行调整,制定符合业务逻辑的项目模板。
2、创建自定义的项目目录结构模板
- 首先我们需要知道 vue init 都支持哪些命令参数。
可以看到,vue init -c 命令是可以支持克隆 Git 远程仓库代码到本地的。
我们这里的项目目录结构主要参考了 Vue 官方的 Webpack 模版: https://github.com/vuejs-templates/webpack 。
如图:
- 修改 template的项目目录结构,上传到远程仓库。
我们只需要将 vuejs-templates/webpack 项目克隆到本地,然后将 template 文件夹下的目录结构改成我们自定义的目录结构,并封装内部通用逻辑,最后将修改完成后的代码上传到我们的Git 远程仓库。
脚手架的使用
目前团队基于 vue-cli 搭建了 3 种脚手架用于不同的业务场景:
项目脚手架
后台脚手架
模块脚手架
1、项目脚手架
项目脚手架主要是用于快速创建一个项目,内部会包含基础的 vue 和团队自用的一些组件,例如 edu-axios(通过 galaxyDependencies 依赖引入,后续会介绍),还会集成用来依赖内部组件的 webpack 插件,用于解析依赖关系。
2、后台脚手架
后台脚手架是基于项目脚手架的,主要是用于快速创建一个后台管理系统项目(包含登录界面、左侧菜单结构、右侧对应的内容展示等)。
使用方式如下:
my-project 生成的目录结构如下:
yarn install 后,通过 yarn dev 启动项目,浏览器自动打开 http://localhost:9090/,呈现页面如下:
- http://localhost:9090/#/login 登录页面
- http://localhost:9090/#/index 首页
现在一个符合团队标准的后台管理系统的项目目录结构就创建完成了,使用者无需关心项目结构的搭建过程,只需要专心写业务逻辑即可。
3、模块脚手架
模块脚手架主要是用于快速开发公共功能组件,每个公共组件都是一个单独的项目。
项目中会集成用来依赖内部组件的 webpack 插件,用于解析依赖关系。并且会提供 build 逻辑,用于生成复合 galaxy 依赖关系的 depends.map.json 文件和真实需要上传的文件。
提示:通过模块脚手架创建的模块项目最终打包编译生成的是符合 UMD 规范的模块,并且在打包编译的过程中会生成一个 depends.map.json 文件(当前项目的模块依赖分析,会包含所有打包产物,css、js,和当前模块所依赖的 externals 等等),例如:
之后通过 Jenkins 自动部署的时候,Jenkins 会将 depends.map.json 和打包产物分别上传到 CDN 上。
(这里只需要知道在打包编译的过程中会生成 depends.map.json 即可,具体作用会在下文【Webpack 之 externals 在脚手架中的实践】提到)
使用方式如下:
my-component 生成的项目目录结构如下:
接下来使用者就可以专注于功能组件的封装了,无需重复创建格式相同的目录结构。
Webpack 之 externals 在脚手架中的实践
首先先来了解下什么是 webpack externals。
externals 的作用:防止将 import 的包在构建的时候打包到 js 文件中,而是在运行时再从外部去获取这些拓展依赖。
详细请看 Webpack 文档对 externals 的介绍:https://webpack.js.org/configuration/externals/ 。
而我们将我们的模块都以 externals 的方式引入,提高了构建的效率。
具体是如何引入的?
每个脚手架构建的项目都在 package.json 中集成了 galaxyDependencies ,它是一个内部依赖组件,项目构建过程中,webpack 通过插件拿到某一个依赖对应版本的 depends.map.json,然后异步地去请求拿到他并解析其依赖,插入到当前项目的externals 中,后续会以 eduAxios 为例进行介绍。
下面会介绍脚手架构建的项目中具体包含了哪些代码。
加载 galaxy.config.js
在脚手架项目目录结构(template)中会有一个 galaxy.config.js 文件,里面主要是预置了一些构建配置,代码如下:
其中 {{ name }} 是使用脚手架在创建项目过程中动态注入的变量,代表的是当前项目名称,如同 package.json 中一样:
当我们使用 vue init -c true git.lagou.com:fe-formwork/edu-component my-component 来创建项目的时候,在命令行会提示我们输入当前项目的 Project name 、Project description、Author 等。
这些输入的值会自动注入到 package.json 和 galaxy.config.js 中对应的 {{ name }} 、{{ description }} 、{{ author }} 模板变量中。
添加模块依赖
当我们的项目/模块依赖于其他自定义模块的时候,引入方式有以下两种:
这里以 eduAxiosComponent 模块为例,eduAxiosComponent 基于 axios 进行封装,自身使用增加了一层拦截,可以用于统一拦截。
1、在 package.json galaxyDependencies 对象中添加模块名和对应版本号。
2、启动项目,在启动过程中,会使用 webpack 插件分析依赖将 eduAxiosComponent 对应的 1.0.1 版本的所有依赖(包括自身打包产物和它当前的依赖)都插入到 externals 中。因为模块打包的方式是 umd,所以可以用 import 或者 window 去访问,如下:
如何分析依赖
vue.config.js 中引用了 galaxy.config.js 中暴露的配置对象,当项目编译的时候,vue.config.js文件会被@vue/cli-service 自动加载。
vue.config.js 代码如下:
这里用到了一个webpack 自定义插件:galaxyfetchdependsplugin 。
galaxyfetchdependsplugin 在 webpack 编译之前(beforeCompile hooks)主要做了以下事情:
请求 package.json 中 galaxyDependencies 指定的所有依赖模块的 depends.map.json。
将请求得到的 depends.map.json 中的模块名称以键值对的形式注入到 webpack compiler 对象的 externals 中。
将依赖的模块资源与当前 webpack 编译的资源文件合并去重(当前仅对重复字符串进行了去重,后续会指定重复规则,例如协议、版本、min包导致的重复等)。
将当前项目依赖的资源写入到 dist/depends.map.json。
重写 webpackhtmlplugin 插件的 templateParameters 的方法, 将所有依赖的资源写入到到 html 中。
Jenkins部署
通过 jenkins 部署的时候,每次都会将最新的 dist/depends.map.json 以及打包产物上传到 CDN,当其他项目/模块对当前模块引用的时候,只需要按照上面的步骤引入即可。
总结
带来的便利
项目脚手架和模块脚手架能够让团队开发人员在开启一个新项目或者封装一个新模块之前快速地创建项目结构目录,降低了重复搭建的成本,提高了开发效率,使得各个团队之间有了统一的标准。
对业务的影响
项目脚手架在对原有的臃肿的、前后不分离的、多项目进行单独的项目拆分时,能够快速的创建符合团队要求的项目结构目录,开发人员可以直接上手,无需更多的搭建和理解成本。
模块脚手架在对原有项目公共模块进行抽取拆分时,可以快速创建模块项目模板,加上 webpack 的 externals 配置在脚手架中的实现,使得编译完的项目体积更小,依赖更清晰,模块版本更加稳定,构建更高效。
后续的影响
后续团队会陆续对 C 端、B 端项目进行拆分,拆分完的项目完全前后端分离,通过 Jenkins 自动部署,再由前端 Nginx 统一映射拆分后的项目。而前端脚手架则是为后续的项目拆分奠定了坚实的基础。
推荐阅读:
点个“在看”和“赞”吧,
毕竟我是要成为前端网红的人。