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,通过<style module>以开箱即用的方式在*.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