本文会从三个方面讲起:css
- cli3产生的缘由
- cli3中的最佳实践
- cli3的源码简析
1、cli3产生的缘由
cli1.x,2.x
看一下以前的vue-cli建立项目的方式:html
vue init webpack
复制代码
vue-cli 的缺点也很明显:vue
- 从多个模板中选择一个,模板间没有交集
- 一旦建立项目,就与vue cli没什么关系了,建立的配置并不能反馈到vue cli的上游
- 没有一个可视化页面能够来管理咱们的vue项目,同时项目依赖的更新也是一个难题
cli3的能力
上面简述了原有的cli的缺点,下面罗列了一些cli3的新的特色:node
- 去掉了cli2原有的config和build文件夹
- 提供了一些最佳实践,默认使用这些最佳实践,好比thread-loader的使用多个处理器来处理babel或者ts的编译,默认使用cache-loader等
- 虽然有了最佳实践,可是也不必定是咱们想要的。因此cli3提供了可配置化功能,即在根目录的vue.config.js,经过vueconfigjs,咱们能够依然具有了操做webpack,webpack-dev-server,loader,plugin的能力。
- cli3采用了插件机制,这里的插件机制的解析咱们后面会详细讲到。正是由于这个插件机制,让咱们更便于扩展,能够看到,目前已有590个插件,包括咱们常见的一些框架与技术,好比graphql。storybook,jest等
5. 快速原型开发,是相似react生态的umi,能够运行单个jsx文件。咱们可使用 vue serve 和 vue build 命令对单个 *.vue 文件进行快速原型开发。
6. cli3提供了图形化界面和命令行两种方式,经过图形化界面,咱们能够很直观的管理vue项目,启动编译测试项目,添加插件,更新安装依赖,更改部分配置等。值得一提的是,打包后也为咱们分析了包的大小(webpack-bundle-analyzer),若是包较大的话,会有warning提示咱们利用webpack的code-split去拆包,达到一个更好的性能。
cli3中的最佳实践
安利了一波vue-cli3以后,接下来咱们来看一下,咱们所说的vu-cli3为咱们集成的最佳实践究竟是什么呢?react
resource hint(preload-wepack-plugin)
这个截图是项目中打包后index.html中的状况。能够看到js是用link标签引入的。 那preoload和prefetch是什么呢?咱们来看一下官方的文档
这两个是resoutce hint,标识了浏览器对资源的处理方式,其余的reasource hint还有DNS Prefetch ,preconnect等。
在cli3中,这个功能的实现是采用了webpack的一个插件,
preload-wepack-plugin。也就是在插入script标签到index.html以前,获取chunk,并对资源的引入作了改变。
动态添加polyfill(browerslist):
默认状况下,它会把 useBuiltIns: 'usage'
传递给 @babel/preset-env
,这样它会根据源代码中出现的语言特性自动检测须要的 polyfill。这确保了最终包里 polyfill 数量的最小化。webpack
如何使用:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage"
}
]
]
}
复制代码
contenthash
webpack中对于输出文件名能够有三种hash值: hash chunkhash contenthash
这三者有什么区别呢?git
- hash: 每次修改任何一个文件,全部文件名的hash都将改变。因此一旦修改了任何一个文件,整个项目的文件缓存都将失效
- chunkhash: 根据不一样的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。将样式做为模块import到JavaScript文件中的,因此它们的chunkhash是一致的,这样就会有个问题,只要对应css或则js改变,与其关联的文件hash值也会改变,但其内容并无改变呢,因此没有达到缓存意义。
- contenthash: 是针对文件内容级别的,只有你本身模块的内容变了,那么hash值才改变
那么,使用这种contenthash模式,再结合咱们的http缓存,咱们能够作到针对文件级别的缓存。若是咱们的项目上线是使用一个非覆盖式发布,那这种模式是再适合不过了。es6
modern mode
什么是modern mode? 官网给出了一个友好的介绍 github
es6的包加载速度更快,包体积也更小。由于浏览器正在新的语法作性能优化。同时去掉了babel编译时添加的一些polyfill,体积更小。 这个也是基于babel的能力而拓展的一个功能,babel给咱们提供了
如何使用:
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": true
}
}
]
]
}
复制代码
实际上,vue-cli3还为咱们集成了不少的best-practice,例如web
- webpack的devtools在dev环境使用的cheap-module-eval-source-map,在prod环境使用的source-map
cli3源码简析
首先安利一下vue-cli3源码分析的文档。可是接下来对于源码的分析可能不会直接上代码,我更但愿经过流程图,以一个简单明了的方式,和你们阐明插件机制是如何实现的.
不一样于以前 1.x/2.x 的 vue-cli 工具都是基于远程模板去完成项目的初始化的工做,它属于那种大而全的方式,当你须要完成自定义的脚手架工具时,你可能要对 vue-cli 进行源码级别的改造,或者是在远程模板里面帮开发者将全部的配置文件初始化完成好。而 @vue/cli3 是基于插件机制的,它将原来的大而全的模板拆解为如今基于插件系统的工做方式,每一个插件只须要完成本身对于项目的拓展工做
vue-cli3主要由cli和cliservice组成。
整个插件系统当中包含2个重要的组成部分:
- @vue/cli,提供 cli 命令服务,例如vue create建立一个新的项目;
- @vue/cli-service,提供了本地开发构建服务
1. cli
cli为咱们提供了11个命令,这里重点会讲到vue create和vue add 命令
vue create
vue create是用来建立项目的,看一下vue create命令作了什么事情
- 首先会对项目名进行验证,使用的是validate-npm-package-name 这个包来验证。而后会判断项目名在当前目录中是否已经存在,并提供了三种方式来进行操做,分别是 重写,合并,取消。
- 接下来是获取预设,什么是预设呢,引用官方文档上的话,预设就是
Vue CLI 预设配置是一个包含建立新项目所需的预约义选项和插件的 JSON 对象,让用户无需在命令提示中选择它们。
也就是说,咱们上一次建立的项目配置,能够保留在一个文件(也就是vuerc)中,下一次建立时,就可使用这些配置,而不用从新在命令行中再次操做,再去选择一次。
这第二步就是获取咱们须要的配置。
使用到了inquirer
,inquirer.js
是一个nodejs模块,是用户与命令行交互的工具
3. 接下来是依赖安装。
在上一步中咱们已经知道了须要的插件,cli帮咱们生成pkg.json,并经过yarn或者npm安装依赖,这里使用了promise.race,经过获取同个npm包来看看是默认镜像仍是淘宝镜像返回更快,那个更快就使用哪一个。
4. 最后一步就是generator,也就是生成咱们的文件,又分别作了如下的操做.
- i. 调用各个插件,各个插件均可以对咱们的项目进行修改
- ii. 修改完了以后,合并配置(由于各个插件都拥有改变文件的能力,因此须要将改动合并起来)
- iii. 先将整个项目的文件在内存中生成好
- iv. 生成文件
vue add
以上讲的是关于vue create的,接下来咱们看一下vue add命令,add命令是用来添加插件的。经过add的命令,cli3会帮咱们完成插件的下载,安装以及执行插件所提供的 generator,咱们来看一下流程图
- 安装插件和依赖
- 加载generator: 能够来修改pkg.json中的字段,或者是基于ejs模板来修改文件,或者是生成一个新的文件
- 加载并调用Prompts:这是一个对话模块,底层使用 inquirer 进行展现。若是这个插件在其根目录包含一个 prompts.js,那么它将会用在该插件被初始化调用的时候。Inquirer.js会帮咱们解析 这些被解析的答案对象会做为选项被传递给插件的 generator
- run generator: 使用上一步的选项来生成,修改文件
2. cli-service
接下来咱们来看一下@vue/cli-service 内部是如何搭建整个插件系统的。就拿执行npm run serve启动本地开发服务来讲,大概流程是这样的:
- 实例化 Service类
- 在实例化 Service的过程中完成了两个比较重要的工做:
- 加载插件(resolvePlugin:加载@vue/cli-service 内部提供的插件以及项目应用当中须要使用的插件,内部插件又分为两类,一类插件在内部动态注册新的 CLI 命令,开发者便可经过 npm script 的形式去启动对应的 CLI 命令服务,一类插件主要是完成 webpack 本地编译构建时的各类相关的配置。cli-service 将 webpack 的开发构建功能收敛到内部来完成)
- Service 实例化完成后,调用实例上的 run方法来执行全部被加载的插件
总结: cli-service 将基于 webpack 的本地开发构建配置收敛至内部来实现,当你没有特殊的开发构建需求的时候,内部配置能够开箱即用,不用开发者去关心一些细节。固然在实际团队开发当中,内部配置确定是没法知足的,得益于插件构建设计,开发者不须要重构内部的配置,而是直接使用 @vue/cli-service 暴露出来的 API 去完成对于特殊的开发构建需求。
最后
- 举个插件的栗子: 传送门: vue-cli-plugin-element 尽管我以为vue-cli-plugin-element能够作得更好,好比自定义选择须要添加的组件,自定义主题色(vue ui的交互页面实际上是提供了颜色选择器的,以下图),模板选择等
2. webpack,vue-cli,vue都使用到了插件机制。面向接口编程,确实是很优秀的想法。这个也是插件模式的一种体现
插件(Plugin)模式向用户提供了一种扩展程序的接口,用户能够在程序本体以外,按照指定接口编写插件来为程序增长功能
参考
vueconf 2018
vue-cli 3.0 源码分析
Vue-cli@3.0 插件系统简析