Vue-CLI
vue-cli
是一种基于vue.js
进行快速开发的完整系统,通过@vue/cli
+ @vue/cli-service-global
快速开始零配置原型开发,能够用于 快速原型设计、简化应用程序搭建、进行高效项目管理。它由主要的三个组件组成:
通过
@vue/cli
搭建项目脚手架。cli
是一个全局安装的npm
包,提供了终端里的vue
命令,可以由此创建一个项目脚手架(vue create
)、构建新想法的原型(vue serve
)及图形化界面管理项目(vue ui
)。运行时依赖
@vue/cli-service
。它是一个npm
包,局部安装在每个@vue/cli
创建的项目中,构建于webpack
和webpack-dev-server
之上。项目内部的vue-cli-service
命令,提供serve
、build
、inspect
实现开发、打包、检查内部webpack
配置功能。通过
cli
插件进行扩展。cli
插件是向vue
项目提供可选功能的npm
包,名字以@vue/cli-plugin-(内置插件)
或vue-cli-plugin-(社区插件)
开头,开发中通过vue add
命令添加。在项目中运行vue-cli-serviec
命令时,会自动解析并加载package.json
中列出的所有cli
插件。
开发
安装
安装命令
npm install -g @vue/cli
检查版本
vue --version //3.10.0
快速原型开发
安装全局依赖
npm install -g @vue/cli-service-global
使用vue serve
和vue build
命令对单个*.vue
文件进行快速原型开发
vue serve fileName //开发环境下为js/vue文件启动一个服务器
vue build fileName //生产环境零配置构建一个js/vue文件
创建项目
运行以下命令创建一个项目,可以根据提示选取默认配置(babel
+eslint
)和手动选择需要的特性
vue create 项目名
配置
webpack基础相关
基本概念
webpack
是一个js
的静态模块打包工具。对于webpack
来说,一切文件皆为模块,处理应用程序时会在内部构建一个依赖图,会从main.js
出发,识别出源码中的模块化导入语句,递归地找出入口文件的所有依赖,将其打包生成一个或多个bundle
。
| 概念 | 用途 |
| :-------- | :--------|
|entry
|webpack
将这个模块作为构建内部依赖的开始|
|module
|webpack
中一切皆模块|
|chunk
| 一个chunk
由多个模块组合而成,用于代码合并与分割|
|loader
|模块转换器,将非js
内容按需转换。test
用于标识被转换的文件,use
指示文件被转换时应该使用的loader
,并且在数组中从右到左地解析执行|
|plugin
|解决loader
无法实现的其他事情,是一个具有apply
方法的js
对象,apply
方法会被webpack complier
调用,且complier
对象可在整个编译声明周期访问。由于插件可以携带参数/选项,必须在webpack
配置中,向plugin
传入new
实例。如html-webpack-plugin
为应用程序生成一个html
文件,并自动注入所有生成的bundle
|
|output
|告诉webpack
在哪里输出它所创建的bundle
,以及如何命名这些文件,生成文件默认放置在./dist
文件夹中|
//ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin'
class ConsoleLogOnBuildWebpackPlugin{
apply(compiler){//compiler hook的tap方法的第一个参数,应为驼峰式命名的插件的名称,建议为此使用一个常量,以便可以在所有hook中复用
complier.hooks.run.tap(pluginName, compilation = {
console.log('webpack构建过程开始')
})
}
}
配置示例
// build/webpack.base.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: './src/js/main.js'
module: {
rules:[
{ test: /\.vue$/, use: 'vue-loader'}
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
],
output: {
path: path.resolve(__dirname,'dist'),
filename: 'js/[name].js'
}
}
解析原理
在使用
webpack
构建的应用程序中,主要有三种类型的代码:
- 与业务有关的代码;
- 代码依赖的第三方库;
webpack
的runtime
和manifest
,管理所有模块交互。
runtime
,及伴随的所有manifest
数据,是指在浏览器运行过程中,webpack
用来连接模块化应用程序所需的所有代码,即指在模块交互时,连接模块所需的加载和解析逻辑,包括已经加载到浏览器中的连接模块逻辑和尚未加载模块的延迟加载逻辑。
当
compile
开始执行、解析和映射应用程序时,会保留所有模块的详细要点,这个数据集合称为manifest
,当完成打包并发送到浏览器时,runtime
会通过manifest
来解析和加载模块。无论选择哪种模块语法,import
和require
语句转换为__webpack_require__
方法,此方法指向模块标识符(module identifier
),通过使用manifest
中的数据,runtime
将能够检索这些标识符,找出每个标识符背后对应的模块。
通过使用内容散列(content hash
)作为bundle
文件的名称,在文件内容修改时,会计算出新的hash
,浏览器会使用新的名称加载文件,从而使缓存无效。这这样即使内容没有明显修改,某些hash
还是会改变,因为注入的runtime
和manifest
在每次构建后都会发生变化。
loader和插件相关
每个
cli
插件会包含一个(用来创建文件的)生成器和一个(用来调整webpack
核心配置和注入命令的)运行时插件。使用vue create
创建新项目时,部分插件会根据预设特性被预安装好,如果想在创建好的项目中安装一个插件,使用vue add @vue/eslint
,这个命令将@vue/eslint
解析为完整的包名@vue/cli-plugin-eslint
,然后从npm
安装它,调用它的生成器。
调整
webpack
配置最简单的方式是在vue.config.js
中的configureWebpack
选项提供一个对象,这个对象会被webpack-merge
合并入最终的webpack
配置。如果需要基于环境有条件地配置行为,或者想要直接修改配置,就换成一个函数(函数环境变量被设置之后懒执行),可以直接使用config
参数来修改webpack
中的配置,或者返回一个对象进行merge
处理。还可以使用chainWebpack
改变webpack
内部配置,这种方式是链式修改,而configureWebpack
倾向于整体替换和修改。
//vue.config.js
module.exports = {
configureWebpack: config => {
if(process.env.NODE_ENV === 'production'){
//为生产环境修改配置
} else {
//为开发环境修改配置
}
}
}
vue-cli
内部的webpack
配置是通过webpack-chain
维护,它可以定义具名的loader
规则和插件,并且可以在后期进入这些规则并对他们的选项进行修改。
loader
- 修改
loader
选项。对于css
相关loader
来说,推荐使用css.loaderOptions
而不是直接链式指定loader
,是因为每种css
文件类型都有多种规则,而css.loaderOprtions
可以确保通过一个地方影响所有规则。
//vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(optopns => {
//修改它的选项
return options
})
}
}
- 添加一个新的
loader
//vue.config.js
module.exports= {
chainWebpack: config => {
//GraphQL Loader
config.module
.rule('graphql')
.test(/\.graphql$/)
.use('ggraphql-tag/loader')
.loader('graphql-tag/loader')
.end()
}
}
- 替换一个规则里的
loader
//vue.config.js
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
//清除所有的loader,否则接下来的loader会附加在该规则的所有loader之后
}
svgRule
.use('vue-svg-loader')
.loader(vue-svg-loader)
//添加要替换的loader
}
插件
- 使用插件
//vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('assetToJson')
.use(AssetsWebpackPlugin, [
{/*配置项*/}
])
}
}
- 修改插件选项
//vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('html')
.tap(args => {
return [/*传递给html-webpack-plugin构造函数的新参数*/]
})
}
}
可以通过vue inspect
来确认变更,vue-cli-service
暴露了inspect
用于审查解析好的webpack
配置,全局的vue
可执行程序同样提供了inspect
命令,会将解析出来的webpack
配置、包括链式访问规则和插件的提示打印到stdout
,也可以将其输出重定向到一个文件以便进行查阅
vue inspect > output.js
page相关
相较于
vue cli2
,vue cli3
搭建的项目中没有config
目录和build
目录,默认封装了项目运行常用的webpack
常用配置,可以通过vue inspect plugins
查看默认插件及用途。添加自定义配置需要在根目录中手动添加vue.config.js
文件,它会被@vue/cli-service
自动加载。
在multi-page
模式下构建应用,每个page
应该有一个对应的js
入口文件,它的值是一个对象,key
是入口的名字,值是包括entry
(必选)、template
、filename
、title
、chunks
的字符串
//vue.config.js
module.export = {
pages: {
index: {
entry: 'src/index/main.js', //入口
template: 'public/index.html', //模板
filename: 'index.html', //在dist/index.html的输出
title: 'Index Page',
chunks: ['chunk-vendors','chunk-common','index'],//页面中包含的块,通常包含提取出来的通用块和依赖块
}
}
}
css相关
webpack
不支持原生解析css
文件,如果要支持非js
类型的文件,需要使用loader
机制,本质上是一个模块转换器,将模块内容按需转换。
css modules
是一个用于模块化和组合
css
的系统,若想在js
中导入css
或其他预处理文件,文件应以.module.(css|less|sass|scss|styl)
结尾,若想去掉文件名的.moudle
,设置vue.config.js
中css.modules
为true
,通过以开箱即用的方式在
*.vue
文件中使用CSS Modules
。具体使用
css.loaderOptions
向
webpack
的预处理器传递选项,支持的loader
有css-loader
、sass-loader
、less-loader
、postcss-loader
、stylus-loader
css.extract
是否将组件中的
css
提取到一个独立的css
文件中,而不是动态注入到css
的inline
代码中。生产环境下是true
,开发环境下是false
。
//vue.config.js
module.exports = {
css: {
modules: true
loaderOptions: {
sass: { //这里的选项会传递给sass-loader
data: `@import "~@varibles.css"` //@/是src/的别名,向sass样式传入共享的全局变量
},
postcss: { //这里的选项会传递给postcss-loader
plugins: [
require('postcss-pxtorem') //新增插件
]
}
}
}
}
babel配置相关
babel
是一个js
编译器,用于将ECMAScript 2015+
的版本代码转换为向后兼容的 js
语法,以便能够运行在当前和旧版本的浏览器中。在.babelrc
或babel.config.js
文件中,主要对presets
和plugins
进行配置。可以在plugins
中一个一个添加插件,还可以以preset
的形式启用一组插件,插件在presets
前运行。
presets
presets
是babel
插件集合的预设,告诉babel
将编译哪些类型的代码。可以接收参数,参数名由插件和参数对象组成一个数组,数组中的解析执行顺序是从后往前的。
babel-preset-env
可以根据开发者的配置,按需加载插件,使得支持的目标环境的版本获得对应的js
语法和浏览器功能支持,并且将语法和环境映射到对应的babel
转换插件和core-js polyfills
属性名 | 属性值 | 描述 |
---|---|---|
targets |
字符串/字符串数组/对象 | 描述项目的目标环境 |
loose |
布尔值 | false 生成更简单的ES5代码 、true 尽可能遵循ES6 的语义 |
modules |
'amd' / 'umd' / 'systemjs' / 'commonjs' / 'cjs' / 'auto' / 'false' |
启用将ES6 模块语法转换为其他模块类型 |
include |
数组 | 包含使用的plugin |
exclude |
数组 | 包含不使用的plugin |
useBuiltIns |
'usage' /'entry' /false |
设置babel-preset-env 如何处理polyfill ,分别为根据配置的浏览器兼容和代码中用到的API 进行polyfill 按需添加、引入浏览器不兼容的polyfill 、不对polyfill 做操作 |
如果没有配置一些规则,
babel
只默认转化js
句法,不转换新的API
,如Interator
、Generator
、Set
、Map
、Proxy
、Reflect
、Symbol
、Promise
等全局对象,一些定义在全局对象上的方法(全局静态函数)(如Object.assign
、Array.from
)以及一些实例方法Array.prototype.includes
不会转码,可以使用babel-polyfill
做兼容,这种做法会污染全局变量,并且把整个依赖包放进去,项目打包以后体积增大。参考
- 使用
@babel/preset-env
和useBuiltIns
在core-js
模块中直接引入参考 - 手动导入单个
polyfill
- 使用
babel-runtime
和babel-plugin-transform-runtime
。babel-runtime
是一个以模块化方式实现函数的包,会在输出中注入一些相同的代码导致重复。babel-plugin-transform-runtime
避免编译的输出中存在重复,能够为代码创建沙盒环境,防止污染全局范围。转换器将这些内置函数别名为core-js
,这样可以无缝使用这些内置函数不需要再使用polyfill
。
plugin
插件的执行从前往后执行,可以用 插件 转换代码,模块转换插件包括,参考
-
@babel/plugin-transform-modules-amd
AMD
规范非同步加载模块,允许指定回调函数。在浏览器环境,从服务端加载模块,必须采用非同步模式,浏览器一般采用AMD
规范。 -
@babel/plugin-transform-modules-commonjs
CommonJS
规范规定,每个模块内部,module
变量代表当前模块,这个变量是一个对象,它的exports
属性是对外的接口,加载某个模块,其实是加载该模块的module.exports
属性。加载模块同步,只有加载完成,才能执行后面的操作。一旦输出一个值,内部的变化就影响不到这个值。
Node
应用由模块组成,采用CommonJS
模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里定义的变量、函数、类都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载,在浏览器端,模块需要提前编译打包处理。ES6
模块是动态引用,并且不会缓存值,模块里的变量绑定其所在的模块,输出的是值的引用。 @babel/plugin-transform-modules-systemjs
@babel/plugin-transform-modules-umd