深入理解学习webpack
在之前的文档中只是列出了webpack
的配置文件,这样做只是说能够使用它,并没有理解webpack的配置文件中各项key:value的具体作用和详细配置选项。所以在深入学习vue之前,有必要学习webpack的理念。
写在前面
webpack 核心概念
将模块按照依赖和规则打包成符合生产环境的前段资源,还可以将按需加载的模块进行代码分割,等到实际需要时,再异步加载。通过loader转化,所有的资源都可以视为模块。CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。
安装
使用Node.js的LTS最新版本,这样能保证不缺失webpack
的功能或相关package
包。
本地局部安装:
1 | # 安装 latest release |
全局安装:
1 | npm install -g webpack |
注意:不推荐全局安装 webpack。这会锁定 webpack 到指定版本,并且在使用不同的 webpack 版本的项目中可能会导致构建失败。但是全局安装可以在命令行调用 webpack 命令。
【补充】npm install 安装模块参数说明:
1 | -g, --global 全局安装(global) |
npm 相关的更多命令参考这篇文章:npm常用指令详解
然后在根目录下创建一个 webpack.config.js 文件后,你可以通过配置定义webpack的相关操作。
入口(Entry)
顾名思义,告诉webpack
从哪开始,遵循依赖关系。
webpack.config.js
的配置项
单个入口(简写)语法:1
2
3module.exports = {
entry: './src/main.js'
};
对象语法:1
2
3
4
5
6module.exports = {
entry: {
app: './src/main.js',
vendor: ['vue']
}
};
将vue作为库vendor打包,业务逻辑代码作为app打包,实现了多个入口,同时也可以将多个页面分开打包。
多页面应用程序通常使用对象语法构建。对象语法是“可扩展的 webpack 配置”,可重用并且可以与其他配置组合使用。这是一种流行的技术,用于将关注点(concern)从环境(environment)、构建目标(build target)、运行时(runtime)中分离。然后使用专门的工具(如webpack-merge)将它们合并。
注:vue-cli 生成的模板中build文件夹下有四个配置文件:
- webpack.base.conf.js:基本配置
- webpack.dev.conf.js:开发阶段配置
- webpack.prod.conf.js:准生产阶段配置
- webpack.test.conf.js:测试配置
后三个文件通过webpack-merge插件合并了基本配置,将不同环境下的配置拆分多个文件,这样更加方便管理。
出口(Output)
控制 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。
在 webpack 中配置output 属性的最低要求是,将它的值设置为一个对象,包括以下两点:
- output.filename:编译文件的文件名;
- output.path对应一个绝对路径,此路径是你希望一次性打包的目录。
单个入口:
1 | const path = require('path'); |
多个入口:
如果你的配置创建了多个 “chunk”(例如使用多个入口起点或使用类似CommonsChunkPlugin 的插件),你应该使用以下的替换方式来确保每个文件名都不重复。
- [name] 被 chunk 的 name 替换。
- [hash] 被 compilation 生命周期的 hash 替换。
- [chunkhash] 被 chunk 的 hash 替换。
1 | const path = require('path'); |
!!!关于出口项的配置,单出口的path设置过之后发现无效,先放下,过后研究。
加载器(Loaders)
loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你在 JavaScript 中 require() CSS文件!
在你的应用程序中,有三种方式使用 loader:
主要说明一下使用webpack.config.js配置,使用loader需要在module的rules下配置相应的规则,以css-loader的webpack.config.js为例说明:
1 | module.exports = { |
以下的三种配置方式等效:
1 | {test: /\.css$/, use: 'css-loader'} |
注:loader/query可以和options可以在同一级使用,但是不要使用use和options在同一级使用。
CSS样式分离
对CSS文件的打包,可以像其他模块一样将CSS引入avaScript 代码中,同时用css-loader(像 JS 模块一样输出 CSS),也可以选择使用ExtractTextWebpackPlugin(将打好包的 CSS 提出出来并输出成 CSS 文件)。[这个尚未尝试过]
引入CSS:1
import 'bootstrap/dist/css/bootstrap.css';
安装css-loader和style-loader:1
npm install --save-dev css-loader style-loader
在 webpack.config.js 中配置如下:1
2
3
4
5
6
7
8module.exports = {
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
}
}
资源路径处理
图片文件不是一个 JavaScript 文件,你需要配置 Webpack 使用file-loader或者url-loader去处理它们。使用它们的好处:
- file-loader 可以指定要复制和放置资源文件的位置。更方便的管理图片文件,使用相对路径而不用担心部署URL时的问题。使用正确的配置,Webpack 将会在打包输出中自动重写文件路径为正确的URL。
- 当图片文件小于更定的阈值时,会将文件转换为内联的 base-64 URL。这会减少小文件的 HTTP 请求。如果文件大于该阈值,会自动的交给 file-loader 处理。
安装 file-loader 和 url-loader:1
npm install --save-dev file-loader url-loader
配置说明:
1 | { |
插件(Plugins)
loader 仅在每个文件的基础上执行转换,而插件常用于(但不限于)在打包模块的“compilation”和“chunk”生命周期执行操作和自定义功能(更多关于自定义功能[https://doc.webpack-china.org/concepts/plugins/])
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,你需要使用 new 创建实例来调用它。
生产环境构建
对于Vue来说,在生产环境构建过程中压缩应用代码和使用Vue生产环境部署-删除警告,去除Vue.js的警告。参考vue-loader文档中的配置说明:
1 | if (process.env.NODE_ENV === 'production') { |
开发过程中使用这些配置的设置显得十分的琐碎,这是我们不愿意看到的,所以需要使用环境变量动态构建,我们也可以使用两个分开的 Webpack 配置文件,一个用于开发环境,一个用于生产环境,类似于vue-cli中使用 webpack-merge 合并配置的方式。
[!!!没看懂] 可以使用 Node.js 模块的标准方式:在运行 webpack 时设置环境变量,并且使用 Node.js 的process.env来引用变量。NODE_ENV变量通常被视为事实标准(查看这里)。使用cross-env包来跨平台设置(cross-platform-set)环境变量。
安装cross-env:
1 | npm install --save-dev cross-env |
设置package.json中的scripts字段:
1 | "scripts": { |
这里使用了cross-env插件,cross-env使得你可以使用单个命令,而无需担心为平台正确设置或使用环境变量。
模块热替换
该功能会在应用程序运行过程中替换、添加或删除模块,无需重新加载页面。在独立模块变更后,无需刷新整个页面,就可以更新这些模块,极大地加速了开发时间。
使用 webpack-dev-server 插件,可以提供一个服务器和实时重载功能。这是一个小型的node.js Express服务器。
安装 webpack-dev-server:1
npm install --save-dev webpack-dev-server
使用 webpack-dev-server ,方式如下:
1 | webpack-dev-server --open |
上述命令自动在浏览器中打开 http://localhost:8080。
webpack.config.js配置:1
2
3
4
5
6
7
8
9
10
11
12module.exports = {
...
devServer: {
historyApiFallback: true, // 任意的 404 响应都替代为 index.html
hot: true, // 启用 webpack 的模块热替换特性
inline: true // 启用内联模式
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
...
}
更多的配置说明:DevServer
提取CSS文件
extract-text-webpack-plugin
可以将vue
文件内的<style>
提取,以及JavaScript中导入的CSS提取为单个CSS文件。具体配置文档见:extract-text-webpack-plugin。
安装:1
npm install --save-dev extract-text-webpack-plugin
配置:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}
同时支持配置生成多个CSS文件,这样可以将业务逻辑代码和引用的样式组件库分离。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const ExtractTextPlugin = require('extract-text-webpack-plugin');
// Create multiple instances
const extractCSS = new ExtractTextPlugin('stylesheets/[name]-one.css');
const extractLESS = new ExtractTextPlugin('stylesheets/[name]-two.css');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: extractCSS.extract([ 'css-loader', 'postcss-loader' ])
},
{
test: /\.less$/i,
use: extractLESS.extract([ 'css-loader', 'less-loader' ])
},
]
},
plugins: [
extractCSS,
extractLESS
]
};
clean-webpack-plugin
在编译前,删除之前编译结果目录或文件:
1 | npm install --save-dev clean-webpack-plugin |
配置:1
2
3plugins: [
new CleanWebpackPlugin(['dist'])
]
这样构建的时候可以自动删除之前编译的代码。
解析(Resolve)
这些选项能设置模块如何被解析。webpack 提供合理的默认值,但是还是可能会修改一些解析的细节。
1 | resolve: { |
使用最多的就是别名(alias)和自动解析确定的扩展(extensions),例如上面的@可以代替项目中src的路径,例如:1
import tab from '@/components/tab.vue'
例如引用src/components目录下的tab.vue组件,不需要通过../
之类的计算文件相对路径。这里的extensions可以实现引入模块时不加文件的扩展名:
1 | import tab from '@/components/tab' |
至此回顾了项目devDependencies
依赖中常用的模块:1
2
3
4
5
6
7
8webpack
css-loader / style-loader //CSS样式分离
file-loader / url-loader //资源路径处理
cross-env //!!!没有看懂,暂时搁置
webpack-dev-server //模块热替换,node服务器使用
extract-text-webpack-plugin //提取css文件,将业务逻辑与样式组件库分离
clean-webpack-plugin //构建前删除之前编译的代码
resolve alias/extensions 代替src的路径及忽略文件扩展名
webpack中如何使用ES6?
暂时缺省。
webpack中如何使用vue?
介绍webpack中vue相关的插件。
Vue2文件比较
npm安装:1
npm install --save vue
vue2 经过2.2版本升级后,文件变成了8个:
| UMD | CommonJS | ES Module |
| :———: | :——–: | :——–: | :——–: |
| 独立构建 vue.js | vue.common.js | vue.esm.js
| 运行构建 vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js
vue.min.js 和 vue.runtime.min.js 都是对应的压缩版。
- AMD:异步模块规范
- 没有单独提供AMD模块的版本,但是UMD版本中进行了包装,可以直接用作AMD模块,使用方法如下:
1 | define(["Vue"],function(Vue) { |
- CommonJS:node中常用的模块规范,通过require引入模块,module.exports导出模块。
1 | ... |
- UMD: 通用模块规范兼容了AMD和CommonJS,同时还支持老式的“全局”变量规范:
1 | (function (global, factory) { |
- ES Module:ES6在语言标准的层面上,实现的模块功能。模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
1 | ... |
总结:
- vue.js 和 vue.runtime.js 可以直接 CDN 引用;
- vue.common.js和vue.runtime.common.js可以使用Webpack1 / Browserify 打包构建;
- vue.esm.js和vue.runtime.esm.js可以使用Webpack2 / rollup 打包构建。
vue有两种构建方式,独立构建和运行时构建。主要的区别在于前者包含模板编译器而运行构建不包含。模板编译器的功能是将模板字符串编译为纯 JavaScript 的渲染函数。如果组件中使用 template 选项,那就需要编译器。
- 独立构建包含模板编译器并支持 template 选项。它也依赖于浏览器的接口的存在,所以不能使用它来为服务器端渲染。
- 运行时构建不包含模板编译器,因此不支持 template 选项,只能用 render 选项,但即使使用运行时构建,在单文件组件中也依然可以写模板,因为单文件组件的模板会在构建时预编译为 render 函数。运行时构建比独立构建要轻量30%,只有 17.14 Kb min+gzip大小。
独立构建方式可以这样使用template选项:
1 | import Vue from 'vue' |
使用ES Module规范,默认NPM包导出的是运行时构建。为了使用独立构建在webpack配置中添加以下的别名alias:
1 | resolve: { |
vue-loader
安装:
1 | npm install --save-dev vue-loader vue-template-compiler |
vue-loader依赖于 vue-template-compiler
。
vue-loader
是一个 Webpack 的 loader,可以将以下格式编写的Vue组件转换为JavaScript模块。vue-loader提供的部分特性:
- ES6默认支持;
- 允许对Vue组建的组成部分使用其他webpack loaders,比如对
<style>
使用SASS和对<template>
使用Jade; - .vue文件中允许自定义节点,然后使用自定义的loader处理它们;
- 把
<style>
和<template>
中的静态资源当作模块来对待,并使用 Webpack loaders 进行处理; - 对每个组件模拟出 CSS 作用域;
- 支持开发期组件的热重载;
在webpack中,所有的预处理器需要匹配对应的loader。vue-loader
允许使用其他Webpack loaders
处理Vue组件的某一部分。它会根据lang属性自动判断出要使用的loaders。
上一节提到的extract-text-webpack-plugin
提取css,vue中style标签之间的样式提取的办法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
css: ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader' // <- 这是vue-loader的依赖,所以如果使用npm3,则不需要显式安装
})
}
}
}
]
},
plugins: [
new ExtractTextPlugin("app.css")
]
}
pug 模板
pug 其实就是jade。
vue-loader里对于模板的处理方式略有不同,因为大多数webpack模板处理器(如pug-loader)会返回模板处理函数,而不是变异的HTML字符串,使用原始的 pug 替代 pug-loader:1
npm install pug --save-dev
使用:
1 | <template lang="pug"> |
如果使用 vue-loader@<8.2.0,还需要安装
template-html-loader
。
PostCSS
安装vue-loader时默认安装了postcss,由vue-loader处理的 CSS 输出,都是通过PostCSS进行作用域重写,还可以为PostCSS 添加自定义插件,例如autoprefixer或者CSSNext。
在webpack中使用postcss,需要下载postcss-loader:1
npm install --save-dev postcss-loader
cssnext
cssnext可以将最新的CSS语法转换为兼容性更强的CSS,所以不必等待各种浏览器的兼容。
安装:1
npm install --save-dev postcss-cssnext
[!!!这一部分配置值得商榷]
postcss.config.js:1
2
3
4
5module.exports = {
plugins: [
require('postcss-cssnext')
]
}
webpack.config.js:1
2
3
4
5
6
7
8
9
10module.exports = {
module: {
loaders: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
}
}
cssnext 依赖了autoprefixer,所以我们无需显式下载autoprefixer。更多关于postcss的插件可以看这里:postcss plugins。
在这一部分回顾了有关vue的webpack知识:1
2
3
4
5
6vue
vue-loader //将符合格式要求的Vue组件转换为JavaScript模块
vue-template-compiler //vue-loader依赖于它,将<template>中的静态资源作为模块使用
pug //模板处理
postcss-loader //vue-loader处理的css输出,都是由它进行作用域重写
postcss-cssnext // PostCss的插件之一,可以处理CSS书写的浏览器兼容问题
webpack2 开启eslint校验
利用ESlint规范代码,ESlint和webpack继承,在babel编译代码开始前,进行代码规范检测。这里使用的是javascript-style-standard风格的校验。
主要依赖几个包:
1 | eslint —— 基础包 |