一、什么是Vue CLI
一个基于Vue.js快速开发的系统,使用之后我们开发的页面将是一个完整的项目
二、Vue CLI优势
- 通过 vue/cli搭建交互式的项目脚手架
就是通过命令来下载相关依赖 - 通过@vue/cli + @vue/cli-service-global 快速开始零配置原型开发
- 运行时依赖 @vue/cli-serve
- 可升级
- 基于webpack构建,并带有合理的配置
webpack将项目打包成编译好的项目源码,然后部署到服务器上使用 - 可通过配置文件进行配置
修改配置文件达到不同的配置环境 - 可通过插件进行扩展
- 一个丰富的官方插件集合
Nodejs Vue VueRouter webpack yarn - 一套完全图形化的创建和管理Vue.js项目的用户界面
三、插件
概述
vue cli使用的是一套基于插件的架构,插件可以修改webpack内部配置。项目创建过程中绝大部分特性是通过插件来实现的
在现有的项目中安装插件
- 每个cli插件都包含一个生成器(用来创建文件)和一个运行时插件(用来调整webpack核心配置和注入命令)
- vue add eslint === vue add cli-plugin-eslint
这个命令将 @vue/eslint 解析为完整的包名 @vue/cli-plugin-eslint,然后从 npm 安装它,调用它的生成器 - 当插件在项目之外的package.json文件里
可以在自己项目的 package.json 里设置 vuePlugins.resolveFrom 选项指向包含其它 package.json 的文件夹
项目本地的插件
四、Parset
概述
是一个json对象其中包含了创建项目所需预定义选项和插件,目的是让用户无需在命令提示中选着他们
Preset 插件的版本管理
允许插件的命令提示
- 使用parset之后vue cli会假设所有插件选项都在parset中声明过了
- 通过在插件选项设置"prompts": true来让parset只声明所需插件同时允许用户通过插件注入的命令提示
远程Preset
- 通过git repo将一个preset分享出去
- repo包含
preset.json: 包含 preset 数据的主要文件(必需)。
generator.js: 一个可以注入或是修改项目中文件的 Generator。
prompts.js 一个可以通过命令行对话为 generator 收集选项的 prompts 文件
加载文件系统中的 Preset
五、CLI服务
使用命令
npm run serve
yarn serve
npx vue-cli-service serve
vue-cli-service serve
- 用法
vue-cli-service serve [options] [entry] - 选项
--open 在服务器启动时打开浏览器
--copy 在服务器启动时将 URL 复制到剪切版
--mode 指定环境模式 (默认值:development)
--host 指定 host (默认值:0.0.0.0)
--port 指定 port (默认值:8080)
--https 使用 https (默认值:false)
- 特点
- 此命令会启动一个开发服务器(基于webpack-dev-server)附带热重载模块(Hot-Module-Replacement)
- 也可使用 vue.config.js 里的devServer字段配置开发服务器
- [entry] 是指定入口(默认值:src/main.js,TS:src/main.ts)
vue-cli-service build
- 用法
vue-cli-service build [options] [entry|pattern] - 选项
--mode 指定环境模式 (默认值:production)
--dest 指定输出目录 (默认值:dist)
--modern 面向现代浏览器带自动回退地构建应用
--target app | lib | wc | wc-async (默认值:app)
--name 库或 Web Components 模式下的名字 (默认值:package.json 中的 "name" 字段或入口文件名)
--no-clean 在构建项目之前不清除目标目录的内容
--report 生成 report.html 以帮助分析包内容
--report-json 生成 report.json 以帮助分析包内容
--watch 监听文件变化
- 特点
- 此命令会在dist/目录下产生一个可用于生产环境的包,带有JS/CSS/HTML 的压缩,和为更好的缓存而做的自动的 vendor chunk splitting。它的 chunk manifest 会内联在 HTML 里
vue-cli-service inspect
- 用法
vue-cli-service inspect [options] [...paths] - 选项
--mode 指定环境模式 (默认值:development) - 特点
可以通过此命令来省察vue cli项目的webpack config
npx vue-cli-service help
查看所有注入的命令
缓存和并行处理
-
cache-loader
会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 node_modules/.cache 中——如果你遇到了编译方面的问题,记得先删掉缓存目录之后再试试看 -
thread-loader
会在多核 CPU 的机器上为 Babel/TypeScript 转译开启
Git Hook
配置时无需 Eject
通过 vue create 创建的项目无需额外的配置就已经可以跑起来了。插件的设计也是可以相互共存的,所以绝大多数情况下,你只需要在交互式命令提示中选取需要的功能即可。
不过我们也知道满足每一个需求是不太可能的,而且一个项目的需求也会不断改变。通过 Vue CLI 创建的项目让你无需 eject 就能够配置工具的几乎每个角落
问题
- vendor chunk splitting?
webpack打包过程中生成的js文件都叫一个chunk/vendorchunk,假设在dist目录下会生成很多个Chunk文件,那如果有2个以上的文件需要lodash,那么就需要对lodash进行Code Spliting,也就是单独生成一个lodash.js这样的文件来实现代码分割 - chunk manifest?
- yorkie?
用于自定义git hook
详解如何在项目中使用git Hooks(husky、yorkie) - Eject?
将某些配置抽离出来
六、浏览器兼容性
browserslist
通过.browserslistrc文件指定了项目目标浏览器的范围。其中的值会被@babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀
Polyfill
概述
Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能
useBuiltIns: 'usage'
一个默认的vue cli项目会使用@vue/babel-preset-app,它通过 @babel/preset-env 和 browserslist 配置来决定项目需要的 polyfill
默认情况下,它会把useBuiltIns: 'usage' 传递给 @babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化
然而,这也意味着如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来
如果有依赖需要 polyfill,你有几种选择
- 如果该依赖基于一个目标环境不支持的ES版本撰写
其添加到 vue.config.js 中的 transpileDependencies 选项。这会为该依赖同时开启语法转换和根据使用情况检测 polyfill - 如果该依赖交付了ES5代码并显式地列出了需要的polyfill
使用@vue/babel-preset-app 的 polyfills 选项预包含所需要的 polyfill (es.promise是默认包含的) - 如果该依赖交付 ES5 代码,但使用了 ES6+ 特性且没有显式地列出需要的 polyfill (例如 Vuetify)
使用 useBuiltIns: 'entry' 然后在入口文件添加 import 'core-js/stable'; import 'regenerator-runtime/runtime';。这会根据 browserslist 目标导入所有 polyfill,这样你就不用再担心依赖的 polyfill 问题了
构建库或是 Web Component 时的 Polyfills
使用 Vue CLI 来构建一个库或是 Web Component 时,推荐给 @vue/babel-preset-app 传入 useBuiltIns: false 选项。这能够确保你的库或是组件不包含不必要的 polyfills。通常来说,打包 polyfills 应当是最终使用你的库的应用的责任
现代模式
- 起因
因为如今大部分现代浏览器都已经支持了原生的ES2015,所以为了支持更老的浏览器而为它们交付笨重的代码是一种浪费,vue cli提供了一个现代模式来解决这个问题 - vue-cli-service build --modern
Vue CLI会产生两个应该版本:
一个现代版的包,面向支持ES modules的现代浏览器
一个旧版的包,面向不支持的旧浏览器
现代版的包会通过 <script type="module"> 在被支持的浏览器中加载;它们还会使用 <link rel="modulepreload"> 进行预加载。
旧版的包会通过 <script nomodule> 加载,并会被支持 ES modules 的浏览器忽略。
一个针对 Safari 10 中 <script nomodule> 的修复会被自动注入。
问题
- babel?
是js编译器,将es2015+的代码转换为js向后兼容的代码版本 - ES modules?
一种标准,通过<script type="module">设置之后,就可以使用其标准执行js代码
七、HTML和静态资源
HTML(index文件)
public/index.html 文件是一个被html-webpack-plugin处理的模块。在构建的过程中资源链接会被自动注入。Vue CLI也会自动注入resource hint以及构建过程中处理的js和css文件资源链接
插值
因为index文件被用作模板,所以你可以使用lodash template语法插入内容
<%= VALUE %> 用来做不转义插值; <%- VALUE %> 用来做 HTML 转义插值; <% expression %> 用来描述 JavaScript 流程控制
Preload
是一种resource hint,用来指定页面加载后很快会被用到的资源默认情况下,一个Vue CLI应用会为所有初始化渲染需要的文件自动生成preload提示
这些提示会被@vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('preload') 进行修改和删除
Prefetch
是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件 (通过动态 import() 按需 code splitting 的产物) 自动生成 prefetch 提示
这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('prefetch') 进行修改和删除
构建一个多页应用
不是每个应用都需要是一个单页应用。Vue CLI 支持使用 vue.config.js 中的 pages 选项构建一个多页面的应用。构建好的应用将会在不同的入口之间高效共享通用的 chunk 以获得最佳的加载性能
处理静态资源
- 在 JavaScript 被导入或在 template/CSS 中通过相对路径被引用。这类引用会被 webpack 处理。
- 放置在 public 目录下或通过绝对路径被引用。这类资源将会直接被拷贝,而不会经过 webpack 的处理
从相对路径导入
引用静态资源时,其会被包含进入webpack依赖图中
在其编译过程中,所有诸如 、background: url(...) 和 CSS @import 的资源 URL 都会被解析为一个模块依赖
在其内部,我们通过 webpack 的 Assets Modules 配置,用版本哈希值和正确的公共基础路径来决定最终的文件路径,并将小于 8KiB 的资源内联,以减少 HTTP 请求的数量
URL转换规则
绝对路径:保留不变
.开头:会作为一个相对模块请求被解释且基于你的文件系统中的目录结构进行解析
~开头:其后内容都会作为一个模块请求被解析。你甚至可以引用Node模块中的资源
@开头:作为一个模块请求被解析。它的用处在于 Vue CLI 默认会设置一个指向 <projectRoot>/src 的别名 @。(仅作用于模版中)
public文件夹
放在public文件夹下的静态资源都会被简单复制,不经过webpack
推荐将资源作为你的模块依赖图的一部分导入,这样它们会通过 webpack 的处理并获得如下好处:
- 脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求。
- 文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误。
- 最终生成的文件名包含了内容哈希,因此你不必担心浏览器会缓存它们的老版本。
何时使用 public 文件夹
- 你需要在构建输出中指定一个文件的名字
- 你有上千个图片,需要动态引用它们的路径
- 有些库可能和 webpack 不兼容,这时你除了将其用一个独立的 <script> 标签引入没有别的选择
问题
-
html-webpack-plugin?
当使用 webpack打包时,创建一个 html 文件,并把 webpack 打包后的静态文件自动插入到这个 html 文件当中 -
resource hint?
资源预加载: 当前将要获取资源的列表 通过当前页面或应用的状态、用户历史行为或 session 预测用户行为及必需的资源 -
lodash?
是一个一致性、模块化、高性能的 JavaScript 实用工具库 -
Code Splitting?
其实就是把代码分成很多很多块( chunk )
- 分离业务代码和第三方库
- 按需加载
八、CSS相关
概述
Vue CLI 项目天生支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 在内的预处理器
引用静态资源
编译后的CSS都会通过css-loader来解析其中的url()引用,并将这些引用作为模块请求来处理,这意味着你可以根据本地的文件结构用相对路径来引用静态资源
如果想引用npm依赖中的文件或想用webpack alias,则需要在路径前面加上~
预处理器
可以在创建项目时选择,也可以手动安装相应的webpack loader
# Sass
npm install -D sass-loader sass
# Less
npm install -D less-loader less
# Stylus
npm install -D stylus-loader stylus
自动化导入
如果你想自动化导入文件(用于颜色、变量、mixin......),你可以使用style-resources-loader
这里有一个关于 Stylus 的在每个单文件组件和 Stylus 文件中导入 ./src/styles/imports.styl 的例子。(你也可以选择使用 vue-cli-plugin-style-resources-loader)
PostCSS
你可以通过.postcssrc或任何postcss-load-config支持的配置源来配置PostCSS 也可以通过vue.config.js中的css.loaderOptions.postcss配置postcss-loader
默认开启了autoprefixer,如果要配置目标浏览器,可以使用package.json的browserslist字段
关于 CSS 中浏览器前缀规则的注意事项
在生产环境构建中,Vue CLI 会优化 CSS 并基于目标浏览器抛弃不必要的浏览器前缀规则。因为默认开启了 autoprefixer,你只使用无前缀的 CSS 规则即可
CSS Modules
使用方式
在.vue文件中可以通过<style module>来使用
在.js文件中通过导入 xx.module.(css|less|sass|scss|styl)

想要去掉文件名中的.module,可以在vue.config.js中的css.requireModuleExtension为false
希望自定义CSS Module的类名,可以通过vue.config.js中的css.loaderOptions.css选项来实现
向预处理器 Loader 传递选项
如果你想向webpack的预处理器loader传递选项。你可以使用vue.config.js中的css.loaderOptions选项
问题
1.webpack alias?
设置路径别名
2.autoprefixer?
给css自动加上适应不同浏览器的前缀
3.为啥要使用CSS Module?
对 CSS 类名和选择器限定作用域的一种方式(类似于命名空间)
可以保证单个组件的所有样式:
1 .集中在同一个地方
2. 只应用于该组件
3. 解决 CSS 全局作用域的问题

九、webpack 相关
简单的配置方式
在vue.config.js中的configureWebpack选项提供一个对象,该对象会被webpack-merge合并入最终的webpack配置
如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象
链式操作
介绍
Vue CLI内部的webpack配置是通过webpack-chain 维护的。这个库提供了一个webpack原始配置的上层抽象,使其可以定义具名的loader规则和具名插件,并可以在后期进入这些规则对其选项进行修改
它允许我们更细粒度的控制其内部配置
修改Loader选项
添加一个新的Loader
替换一个规则里的Loader
内联的 SVG 文件使用 vue-svg-loader 而不是加载这个文件
修改插件选项
审查项目的webpack配置
起因
因为@vue/cli-service对webpack配置进行了抽象,所以理解配置中包含的东西会比较难,尤其是当你打算自行对其调整的时候
使用命令
vue inspect
该命令会解析webpack的配置,包括链式访问规则和插件的提示打印到stdout
重定向输出文件
vue inspect > output.js
输出的不是一个有效的webpack配置文件,而是一个用于审查的被序列化的格式
以一个文件的方式使用解析好的配置
有些外部工具可能需要通过一个文件访问解析好的 webpack 配置,比如那些需要提供 webpack 配置路径的 IDE 或 CLI。在这种情况下你可以使用如下路径
<projectRoot>/node_modules/@vue/cli-service/webpack.config.js
该文件会动态解析并输出 vue-cli-service 命令中使用的相同的 webpack 配置,包括那些来自插件甚至是你自定义的配置
问题
- vue inspect?
可以查看webpack相关配置的命令
- webpack-chain?
提供链式的 API 来创建和修改webpack 配置
十、模式和环境变量
模式
种类
development: 用于vue-cli-service serve
test: 用于vue-cli-service test:unit
production: 用于vue-cli-service build 和 vue-cli-service test:e2e
--mode
其可以覆写默认模式
vue-cli-service build --mode development (使用开发环境变量)
NODE-ENV
场景
运行vue-cli-service命令时,所有环境变量都从对应的环境文件中载入
如果文件内部不包含NODE_ENV变量,它的值将取决于模式
例如,在 production 模式下被设置为 "production",在 test 模式下被设置为 "test",默认则是 "development"
作用
将决定您的应用运行的模式,是开发,生产还是测试,因此也决定了创建哪种 webpack 配置
例子
例如通过将 NODE_ENV 设置为 "test",Vue CLI 会创建一个优化过后的,并且旨在用于单元测试的 webpack 配置,它并不会处理图片以及一些对单元测试非必需的其他资源。
同理,NODE_ENV=development 创建一个 webpack 配置,该配置启用热更新,不会对资源进行 hash 也不会打出 vendor bundles,目的是为了在开发的时候能够快速重新构建。
当你运行 vue-cli-service build 命令时,无论你要部署到哪个环境,应该始终把 NODE_ENV 设置为 "production" 来获取可用于部署的应用程序
环境变量
在项目根目录放置以下文件来指定环境变量
一个环境文件只包含环境变量的“键=值”对
FOO=bar
VUE_APP_NOT_SECRET_CODE=some_value
特征
为避免公开机器上可能具有相同名称的私钥:
只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中
被载入的变量:
将会对vue-cli-service的所有命令、插件和依赖可用
环境文件加载优先级:
为一个特定模式准备的环境文件 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级。
s
此外,Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写。
.env 环境文件是通过运行 vue-cli-service 命令载入的,因此环境文件发生变化,你需要重启服务
Staging模式
假设有两个文件
// .env
VUE_APP_TITLE=My App
// .env.staging
NODE_ENV=production
VUE_APP_TITLE=My App (staging)
构建应用
vue-cli-service build 会加载可能存在的 .env、.env.production 和 .env.production.local 文件然后构建出生产环境应用
vue-cli-service build --mode staging 会在 staging 模式下加载可能存在的 .env、.env.staging 和 .env.staging.local 文件然后构建出生产环境应用
总结
这两种情况下,根据 NODE_ENV,构建出的应用都是生产环境应用,但是在 staging 版本中,process.env.VUE_APP_TITLE 被覆写成了另一个值
在客户端侧代码中使用环境变量
只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中 `你可以在应用的代码中这样访问它们
console.log(process.env.VUE_APP_SECRET)`
在构建过程中,process.env.VUE_APP_SECRET 将会被相应的值所取代。在 VUE_APP_SECRET=secret 的情况下,它会被替换为 "secret"
除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量
NODE_ENV - 会是 "development"、"production" 或 "test" 中的一个。具体的值取决于应用运行的模式
BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径
所有解析出来的环境变量都可以在 public/index.html 中以 HTML 插值中介绍的方式使用
只在本地有效的变量
有的时候你可能有一些不应该提交到代码仓库中的变量,尤其是当你的项目托管在公共仓库时。这种情况下你应该使用一个 .env.local 文件取而代之。本地环境文件默认会被忽略,且出现在 .gitignore 中。
.local 也可以加在指定模式的环境文件上,比如 .env.development.local 将会在 development 模式下被载入,且被 git 忽略
问题
1.vendor bundles? `webpack build后生成的app、vendor、manifest
vendor则是通过提取公共模块插件来提取的代码块(webpack本身带的模块化代码部分)
manifest则是在vendor的基础上,再抽取出要经常变动的部分,比如关于异步加载js模块部分的内容`
2.webpack.DefinePlugin?
十一、构建目标
概述
当你运行 vue-cli-service build 时,你可以通过 --target 选项指定不同的构建目标。它允许你将相同的源代码根据不同的用例生成不同的构建
应用模式
简介
应用模式
作用
- index.html 带有注入资源和resource hint
- 第三方库会被分到一个独立包以便更好的缓存
- 小于8KiB的静态资源会被内联在js中
- public 中的静态资源会被复制到输出目录中
库模式
关于IE兼容性
在库模式中,项目的publicPath是根据主文件的加载路径动态设置的(用以支持动态的资源加载能力)
但是这个功能用到了 document.currentScript,而IE浏览器并不支持这一特性
所以要支持的话,得使用库之前先在页面上引入current-script-polyfill
对Vue的依赖
在库模式中,vue是外置的。这意味着包中不会有vue
如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载vue,否则就会回退到一个全局的vue变量
可以在build命令中添加--inline-vue来避免此行为
vue-cli-service build --target lib --inline-vue
将一个单独的入口构建为一个库
vue-cli-service build --target lib --name myLib [entry]
构建一个库会输出
-
dist/myLib.common.js:一个给打包器用的 CommonJS 包 (不幸的是,webpack 目前还并没有支持 ES modules 输出格式的包)
-
dist/myLib.umd.js:一个直接给浏览器或 AMD loader 使用的 UMD 包
-
dist/myLib.umd.min.js:压缩后的 UMD 构建版本
-
dist/myLib.css:提取出来的 CSS 文件 (可以通过在 vue.config.js 中设置 css: { extract: false } 强制内联)
Vue vs. JS/TS 入口文件
当使用一个 .vue 文件作为入口时,你的库会直接暴露这个 Vue 组件本身,因为组件始终是默认导出的内容。
然而,当你使用一个 .js 或 .ts 文件作为入口时,它可能会包含具名导出,所以库会暴露为一个模块。也就是说你的库必须在 UMD 构建中通过 window.yourLib.default 访问,或在 CommonJS 构建中通过 const myLib = require('mylib').default 访问。如果你没有任何具名导出并希望直接暴露默认导出,你可以在 vue.config.js 中使用以下 webpack 配置
Web Components 组件
将一个单独的入口构建为一个Web Components组件 vue-cli-service build --target wc --name my-element [entry]
入口应该是一个*.vue文件。Vue CLI将会把这个组件自动包裹并注册为Web Components组件,无需在main.js里自行注册。也可以在开发时把main.js作为demo app单独使用
该构建会产生一个单独的js文件(及其压缩后的版本)将所有东西都内联起来
当这个脚本被引入网页时,会注册自定义组件<my-element>,其使用 @vue/web-component-wrapper 包裹了目标的 Vue 组件。这个包裹器会自动代理属性、特性、事件和插槽
这个模式允许你的组件的使用者以一个普通 DOM 元素的方式使用这个 Vue 组件

注册多个 Web Components 组件的包
vue-cli-service build --target wc --name foo 'src/components/*.vue'
当你构建一个 Web Components 组件包的时候,你也可以使用一个 glob 表达式作为入口指定多个组件目标
当你构建多个 web component 时,--name 将会用于设置前缀,同时自定义元素的名称会由组件的文件名推导得出。比如一个名为 HelloWorld.vue 的组件携带 --name foo 将会生成的自定义元素名为 <foo-hello-world>
异步 Web Components 组件
当指定多个 Web Components 组件作为目标时,这个包可能会变得非常大,并且用户可能只想使用你的包中注册的一部分组件。这时异步 Web Components 模式会生成一个 code-split 的包,带一个只提供所有组件共享的运行时,并预先注册所有的自定义组件小入口文件。一个组件真正的实现只会在页面中用到自定义元素相应的一个实例时按需获取
vue-cli-service build --target wc-async --name foo 'src/components/*.vue'
现在用户在该页面上只需要引入 Vue 和这个入口文件即可

在构建时使用 vuex
问题
code-split?
十二、部署
通用手册
如果你用Vue CLI处理静态资源并和后端框架一起作为部署的一部分,那么你需要的仅仅是确保Vue CLI生成的构建文件在正确的位置,并遵循后端框架的发布方式即可
如果你独立于后端部署前端应用,也就是说后端暴露一个前端可访问的API,然后前端实际上是纯静态应用。那么你可以将dist目录里构建的内容部署到任务静态文件服务器中,单要确保正确的publicPath