主要参考
just do it
前置条件
安装最新稳定版node,最好是node8以上版本
安装
yarn init -y yarn add webpack --dev yarn add webpack@<version> --dev yarn add webpack-cli --dev
注意:
- yarn init 的 -y 参数表示不需要交互式操作
- webpack-cli是webpack4必须的
所谓零配置
webpack的零配置不是真正意义的零配置,零配置是跑不起来的
执行命令
运行webpack命令可以进行打包:
webpack
一般会用别名如build指代webpack命令,写在package.json中的scripts里,:
{
"name": "webpack-learning",
"version": "1.0.0",
"main": "index.js",
"author": "geekzl",
"license": "MIT",
"scripts": {
"build":"webpack"
},
"devDependencies": {
"webpack": "^4.28.4",
"webpack-cli": "^3.2.1"
}
}
这样可以通过运行yarn build执行webpack命令,别名命令的意义在于简化输入的命令,如:
"scripts": {
"dev": "node build/dev-server.js",
"prod": "cross-env NODE_ENV=production node build/dev-server.js",
"build": "node build/build.js"
},
执行命令报错
上述webpack命令报错:

这是因为,webpack项目默认src下的内容是入口文件,默认输出目录是dist,因此至少要有一个src文件夹,我们可以将目录结构写成:

其中index.js中随便写一句js代码,如console.log(“first log”)
再次运行yarn build:

此时,只有一个要求使用配置文件配置mode的警告,目录下生成了打包后的dist文件夹和打包后的文件main.js:


小结
这说明,webpack默认要找到src下的js打包压缩混淆输出到dist文件夹下
修改默认输入输出路径
方法1:修改webpack命令
"scripts": {
"dev": "webpack --mode development ./foo/src/js/index.js --output ./foo/main.js",
"build": "webpack --mode production ./foo/src/js/index.js --output ./foo/main.js"
}
- 这样既可以改变输入输出目录,也可以更改mode(消除上一步的警告信息)
- 更改mode表示更改webpack两种工作状态,对应开发模式环境和生成模式环境
- 开发环境需要本地服务器、代理等
- 生产环境不需要本地服务器,但需要压缩混淆
- 可以通过本方法的script来分别运行yarn dev和yarn build对比两者间的不同,即是否压缩导致的体积大小不同
方法2:添加配置文件webpack.config.js
避免了方法1添加参数到webpack命令中,直接在webpack.config.js中写:
const path = require('path');
module.exports = {
mode:'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
- mode设置为development或production也可以看到上一节yarn dev和yarn build的不同效果
- path是node自带路径处理函数库,path.resolve用来合成当前目录+dist构成的绝对路径
webpack4零配置体现
- entry 默认设置为 ./src/index.js
- output 默认设置为 ./dist/main.js
- production 和 development 两种模式(直接使用命令webpack是production模式)
其他功能的配置方法
常规需求
显然,只有上面配置是不够的,我们的需求还有:
- js的处理:转换 ES6 代码,解决浏览器兼容问题
- css的处理:编译css,自动添加前缀,抽取css到独立文件
- html的处理:复制并压缩html文件
- dist的清理:打包前清理源目录文件
- assets的处理:静态资源处理
- server的启用:development - 模式下启动服务器并实时刷新
怎么做
- webpack的扩展性体现在它的插件架构上,通过扩展插件完成复杂功能
- webpack本身只能处理js文件,要完成对其他文件的处理,需要“第三方插件”
- “第三方插件”在webpack里由module、plugins等选项配置
- module意为模块,是webpack模块化工具的核心,它通过一个个loader来完成模块的功,,loader 用于对模块的源代码进行转换。loader可以支持对多种语言和预处理器的模块化,并且在bundle中引入这些依赖
- plugins是webpack的骨架,webpack本身也建立在我们在webpack.config.js中用到的插件系统上;当然插件也完成loader不能完成的工作
逐项配置
1. 使用babel支持ES6的转码
请参考英文官网,中文官网翻译较慢
babel官网链接
(1)安装
yarn add babel-loader @babel/core @babel/preset-env --dev
安装3个插件,第一个是webpack的babel插件;第二个是babel的node API,是原babel拆分出的node api包;第三个是babel的官方推荐转义规则包
(2)使用
- 每个loader的配置最好是在webpack.config.js中module选项下新增一个rule实现
- loader也支持cli或者import后内联,但那对代码侵入较大,不应该是工具应该做的事
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
- test代表正则匹配,即匹配所有js和mjs文件,mjs扩展名表示ES6的模块文件。mjs出现在node环境,node原支持的commonjs模块语法生成cjs文件,ES6模块语法生成mjs
- babel中文官网的有关babelrc文档表明,你需要在项目根目录下写.babelrc文件。在webpack项目中可以不写,因为webpack的babel-loader的use选项中options就完成了同样功能,.babelrc文件仅用于babel配置项过多不适宜写在webpack配置里的时候。故以下两种配置意义相同:
配置一:在webpack.config.js中
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: {
browsers: ['> 1%']
}
}
]
],
babelrc: false
}
}
}
]
配置解析:
- presets是babel配置项,babel还有plugins等配置项
- presets的语法可参考官网文档
- targets内规则表示支持市面上市场占有率多于1%的浏览器,更多配置参考文档
- babelrc规则表示默认不自动查找项目根目录下.babelrc文件;babelrc这个命名中的rc是linux范畴概念,表示run commond,即运行时命令,通常在程序运行状态下被调用
配置二:在webpack.config.js中和项目根目录下.babelrc中
<!--webpack.config.js-->
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
<!--.babelrc中-->
{
presets: [
[
'@babel/preset-env',
{
targets: {
browsers: ['> 1%', 'last 2 versions']
}
}
]
]
}
配置解析:
.babelrc中的内容是由webpack原配置中options内容拆分迁移过来的
(3)进阶使用
仅有上述babel功能是不够的,因为以上功能只负责将ES6的语法糖转变为ES5语法,但ES6新定义的API无法转换,如promise,这时需要兼容处理,即polyfill。polyfill意为垫片、填充物,指用于实现浏览器并不支持的原生API的代码。
- polyfill安装和使用
yarn add babel-polyfill --dev
在js文件中引入即可使用
import 'babel-polyfill'
- 这样导致污染全局环境,且引入了babel-polyfill的完整包
- 在有webpack等开发框架的前提下,可以使用babel-runtime和 babel-plugin-transform-runtime插件实现根据用到的代码按需要引用polyfill的相应函数,打包后比全局引入polyfill的包小
- 没有webpack工具的项目可以像这样直接使用babel-polyfill
- polyfill插件化非侵入实现
(1)首先安装辅助插件:
yarn add @babel/runtime @babel/plugin-transform-runtime --dev
- babel-runtime插件用polyfill重写的api代码,是一个充满polyfill的包
- babel-plugin-transform-runtime用来链接原始代码文件和babel-runtime中的代码,用来按需加载需要转义的ES6 api,实现按需加载polyfill的方法
(2)然后修改配置文件:
在webpack.config.js或者babelrc的babel配置的options中添加一行
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": ["> 1%", "last 2 versions"]
}
}
]
],
"plugins": ["@babel/transform-runtime"] // 添加插件
}
提到插件,必须说明babel的编译原理:
- parser:通过 babylon 解析成 AST。
- transform[s]:All the plugins/presets ,进一步的做语法等自定义的转译,仍然是 AST。
- generator: 最后通过 babel-generator 生成 output string。
可以看到,plugins和presets都是加强转义处理的。
2. html文件动态生成js
(0)简介
- 第1节中,我们将入口src/index.js文件编译为出口dist/main.js文件
- 但一般的开发流程是根目录有一个文件目录存放html,通常是这样:
./html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script src="./dist/bundle.js"></script>
</body>
</html>
这里js直接写死了
- 实际开发时,我们需要的是script能动态生成并链接到webpack输出的bundle文件上
- 这个功能由webpack插件HtmlWebpackPlugin完成
- 补充说明:由于类似webpack构建工具直接完成了html与js的映射,结果就是没有显式引入js的html语句;学习vue/cli时会遇到这种疑惑,看起来html模板和js入口文件好像没有直接关联
(1)安装和配置
yarn add html-webpack-plugin --dev
在webpack.config.js中
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 这里引入
module.exports = {
// mode: 'development',
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: []
},
plugins: [new HtmlWebpackPlugin()] // 这里使用
};
运行yarn build,可以看到在dist下生成了一个html文件,其中js关联了最终输出bundle.js:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
(2)HtmlWebpackPlugin插件配置
- 以下是根据官方options配置的常用选项:
plugins: [
new HtmlWebpackPlugin({
title: 'My App', // 改默认title为My App
filename: 'assets/admin.html' // 更改默认index.html名字为dist下assets目录下admin.html
template: 'html/index.html',
hash: true, // 是否生成的js带hash串
inject:'body' // js插入的body中,可选head等
})
]
其中,模板template是开发时的重要选项,该选项表示以根目录下html/index.html为模板插入最终生成的bundle.js
- 配置多个html入口
写多个插件实例即可:
plugins: [
new HtmlWebpackPlugin({
title: 'My App', // 改默认title为My App
filename: 'assets/admin.html' // 更改默认index.html名字为dist下assets目录下admin.html
template: 'html/index.html',
hash: true, // 是否生成的js带hash串
}),
new HtmlWebpackPlugin()
new HtmlWebpackPlugin()
]
3. css处理与合并
(1) 基础配置
需要用到:
- css-loader: 使webpack能解析css,即将css装载到js中成为webpack模块
- style-loader: 使解析的css套入html中,即把css放入html的style标签内
yarn add css-loader style-loader --dev
webpack.config.js中:
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
注意:use中的读取顺序是从右往左
(2) 基础配置实例
- css-loader作用是把import/require方式引入的css填入js中,对css中的@import和url()(通常是background-image的url)引入并像import/require那样解析成一个js模块
- 因此css引入方式如下,这样引入能被webpack模块化处理:
require('./css/index.css');
import './css/other.css';
./css/index.css
.demo {
font-size: 20pt;
color: blue;
text-align: center;
/* background-image: url('../img/animal.jpeg'); */
}
此时background-image注释掉,因为尽管css-loader能处理url,但url资源是图片,还需要引入其他loader来处理
./css/other.css
.demo {
background-color: lightblue;
font-size: 2rem;
}
@import url(./inner.css);
./css/inner.css
.demo {
font-size: 20px;
color: red;
text-align: center;
}
此时运行yarn build生成的html文件是这样的:

只有js,没有css,因为css已经被打包进bundle.js文件中
打开该html文件,样式已经存在,通过右键检查页面可以看到style-loader生成的页面css:

共有三个style文件,分别是上面引入的require和import,同时可以看到css-loader将other.css中的@import也解析为一个单独的style标签
注意: 不要去“查看源代码”,style标签并没有直接静态生成到html中
总结:该方法是css的style标签引入方式,生成的css在bundle.js中,查看页面后发现在html文档的head中的style标签中
(3) style-loader生成link标签
- 除了将生成的css放到bundle.js中,也可以单独提取成css文件,即css的link引入方式
- webpack.config.js中更改style-loader为style-loader/url,css-loader作用是提取css到js中,因此也要去掉换成file-loader:
安装
yarn add file-loader —D
配置:
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader/url', 'file-loader' ]
}
]
}
file-loader可以解析项目中的url引入
,此时页面能生成link标签,但并没有样式,如下:

上图可见,link中css的生成地址是错误的,只有文件名系统无法找到css文件地址。产生问题的根源在,file-loader默认的生成css目录是dist目录,而目前为止html的配置文件在./dist/html/admin.html中,这时候需要配置file-loader(file-loader官网传送)的publicPath,如:
use: [
'style-loader/url',
{
loader: 'file-loader',
options: {
publicPath: __dirname + '/dist/'
}
}
]
更好的办法是,在webpack.config.js的出口处配置公共资源路径publicPath:
output: {
filename: 'bundle.js',
publicPath: __dirname + '/dist/',
path: path.resolve(__dirname, 'dist')
},
use: [ 'style-loader/url', 'file-loader' ]
此时生成link标签正常:

dist目录正常:

当然,也可以通过file-loader的选项配置(file-loader官网传送)生成的单独css文件的规则,如:
{
loader: 'file-loader',
options: {
// name: '[path][name].[ext]'
name: '[name][hash:5].[ext]',
outputPath: 'css/'
}
}
构建后的dist目录结构:

(4) css提取
- css提取需求
- (2)的css存在于js中,生成的style中的css也没有合并
- (3)的link引入的外部css也没有统一
- 构建的生产环境css要求合并为一个文件且有压缩
- 安装与配置
安装:
yarn add mini-css-extract-plugin css-loader -D
webpack.config.js配置:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{
test: /\.css$/,
exclude: /(node_modules|bower_components)/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:5].css',
chunkFilename: '[id].[contenthash:5].css'
})
]
- 实际用法
- 一般来说,开发环境配置和生产环境配置是不同的
- 如基于webpack封装的vue脚手架vue-cli2采用了两套配置,分别对应开发环境和生产环境
- webpack4也可以根据环境变量配置为:
module.exports = (env, argv) => {
const devMode = argv.mode !== 'production'
// ... 省略无关配置
{
test: /\.css$/,
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader'
]
}
}
其中devMode可以获取当前mode参数,来确定devMode是开发环境或生产环境
- 根据环境变量的做法也可以是这样:
package.json中:
"scripts": {
"dev": "webpack-dev-server",
"prod": "NODE_ENV=production webpack -p"
},
webpack.config.js 文件中:
const devMode = process.env.NODE_ENV === 'production'
(5) css-loader配置及其他
- css-loader配置(官网传送)
css-loader除了将css填入js中,还具备的常用功能有:
-
解析css路径-root
默认不转义以/开头的url,即url(/image.png) => url(/image.png){ loader: 'css-loader', options: { root: '.' } }配置了root选项之后,url(/image.png) => require(’./image.png’),会在/前添加root的值
-
css压缩
注意,css-loader的压缩功能已经去掉,中文官网滞后,参考英文官网 -
sourceMap
详细介绍参考sourceMap一节options:{ sourceMap: true } -
css module功能
options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' }
注意: css-module开启之后,js中要显式写cssmodules作用域标识符local/global
:local(.className) {
background: red;
}
转义为:
._23_aKvs-b8bW2Vg3fwHozO {
background: red;
}
- css预处理器loader、自动补全浏览器前缀等功能
- css预处理插件sass-loader
yarn add sass-loader node-sass -D
webpack.config.js:
{
test: /\.scss$/,
exclude: /(node_modules|bower_components)/,
use: ['style-loader','css-loader','sass-loader']
}
注意: sass-loader加入后,要记得更改匹配的正则表达式为.scss
- css兼容性有关插件
如浏览器兼容性前缀补全安装
yarn add post-loader autoprefixer postcss-import postcss-cssnext cssnano -D
webpack.config.js中配置:
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
},
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: (loader) => [
require('postcss-import')({ root: loader.resourcePath }),
require('postcss-cssnext')(),
require('autoprefixer')(),
require('cssnano')()
]
}
}
'sass-loader',
],
importLoaders代表在 css-loader 前应用的 loader 的数量
ident是一个标识符,命名为唯一值即可,用来启用plugins中的require语法
postcss插件介绍:
- import @import形式css内联
- cssnext 允许使用未来的css语法
- cssnano 优化css包大小
- autoprefixer 自动补全css浏览器兼容前缀
安装插件后build,有警告提示autoprefixer功能已经被postcss-cssnext实现,可以删掉autoprefixer
(6) 第3节最后的实用配置可以写成这样
webpack.config.js的modules中:
{
{
test: /\.scss$/,
use: [
devMode !== 'production' ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2,
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: loader => [
require('postcss-import')({
root: loader.resourcePath
}),
require('postcss-cssnext')(),
require('cssnano')()
]
}
},
'sass-loader'
]},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'url-loader',
options: {
// 这里的options选项参数可以定义多大的图片转换为base64
limit: 1000, // 表示小于1000Byte的图片转为base64,大于1000Byte的是路径
outputPath: 'imgages', //定义输出的图片文件夹
name: '[name].[ext]'
}
}
]
}
}
webpack.config.js的涉及的plugins中:
plugins:[
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:5].css',
chunkFilename: '[id].[contenthash:5].css'
}),
]
4. 图片处理
(1) 普通图片处理
background-image url中的图片css-loader不能解析,需要使用file-loader
yarn add file-loader -D
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}
]
}
(2) 图片按需处理
小图片生成base64编码,大图片直接保存,这需要url-loader插件
{
loader: 'url-loader',
options: {
limit: 8192,
outputPath: 'imgages', //定义输出的图片文件夹
name: '[name].[ext]'
}
},
注意:
- url-loader相当于file-loader增强版,配置url-loader时需要删除file-loader的图片配置;但url-loader依赖file-loader,故不能删除file-loader包
- url-loader不能工作在style-loader/url和file-loader作用的link css模式下(测试3(3)配置不可用,原因待寻)
(3) 字体处理
file loader和url-loader可以接受任何类型文件并处理,故字体的配置可以这样:
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
}
5. web服务器与代理
(1)本地web服务器安装与配置
安装:
yarn add webpack-dev-server -D
webpack支持的所有配置传送,这里列出常用配置:
module.exports = {
// ...省略其他配置
devServer: {
port: 9000,
https: false,
//contentBase: './html',
historyApiFallback: false,
open:true,
inline: true,
hot: false,
proxy: {
'/api1': 'http://localhost:3000',
'/api2': {
target: 'https://other-server.example.com',
secure: false,
changeOrigin: true
},
'/api3': {
target: 'http://localhost:3000',
pathRewrite: { '^/api3': '' }
}
}
},
}
| 参数 | 说明 |
|---|---|
| port | 本地端口号 |
| https | 是否启用https |
| contentBase | 本地服务器作用的根目录----注意: 指的是localhost/[contentBash] 形式访问HtmlWebpackPlugin中filename: 'html/index.html’定义的路径html |
| historyApiFallback | 设为true后SPA本地刷新不出错,以当前目录index.html为首页,可配置 |
| open | 设为true则配置生效后自动打开相应网址 |
| inline | 文件修改自动刷新,注意:这不是热更新 |
| hot | 热更新HMR |
| proxy | 代理配置,secure允许https,changeOrigin是跨域配置,pathRewrite是匹配后url重写,用于多个不同前缀的url代理到不同地址 |
| publicPath | 不设置时默认与output中的publicPath相同,设置后将webpack-dev-server生成的运行时代码(存放在内存中)放到这个目录下,官方推荐设置成与output的publicPath相同 |
(2)实际项目配置-配置分离
实际开发中,我们只需要开发环境使用本地服务器,生产环境代码不需要服务器(当然也可以配置),我们通常在package.json中这样配置:
"scripts": {
"start": "NODE_ENV=development webpack-dev-server --mode development",
"build": "NODE_ENV=production webpack --mode production"
}
- 并删除webpack.config.js中的mode:'development’代码
- NODE_ENV参数要写在webpack前面
- webpack.config.js配置文件中通过node环境变量 process.env.NODE_ENV取到值
(3)本地服务器原理
- 本地服务器原理
- 使用yarn start运行后,本地node server伺服的文件不存在于硬盘上,而是内存中
- 这与yarn build后生成文件存储到硬盘上的dist文件夹不同
- 几种重要路径配置的区分(详细解释传送)
- output的path指的是生成项目的本地绝对路径,生产模式下是必须的;开发模式因为生成在内存中,故与这个配置无关;
- output的publicPath表示的是打包生成的index.html文件里面引用资源的前缀,各种loader加载的路径前缀都受该选项控制;如css引入资源的路径补全等,output.publicPath,不仅可以影响虚拟目录的取值(通过缺省devserver.publicPath),也影响利用html-webpack-plugin插件生成的index.html中引用的js、css、img等资源的引用路径,它会自动在资源路径前面追加设置的output.publicPath。
- devServer的publicPath可以看作是devServer对生成目录dist设置的虚拟目录,devServer首先从devServer.publicPath中取值,如果它没有设置,就取output.publicPath的值作为虚拟目录,如果它也没有设置,就取默认值 “/”
- devServer的contentBase是用来区分不同静态文件的,意思是本地服务器提取静态资源的目录位置,与HtmlWebpackPlugin的html地址有关
- 配置实例
实例一
output: {
filename: 'js/bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'dist')
},
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/html/index.html',
}),
此时:

- 本地服务器作用于项目根目录
- webpack打包虚拟地址在本地服务器作用的项目根目录

实例二
output: {
filename: 'js/bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'dist')
},
new HtmlWebpackPlugin({
filename: 'html/index.html', // 生成index.html位置在html目录中
template: 'src/html/index.html',
}),
此时,webpack打包虚拟目录是服务器根目录,打开显示的也是项目根目录

说明在服务器根目录没找到index.html,这时的地址应该是:

实例三
output: {
filename: 'js/bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'dist')
},
devServer: {
port: 9000,
publicPath: '/dist/',
},
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/html/index.html',
}),
结果:

localhost/dist/目录能找到虚拟目录位置,但css没能渲染到/dist/虚拟目录下
实例四
output: {
filename: 'js/bundle.js',
publicPath: '/assets/',
path: path.resolve(__dirname, 'dist')
},
devServer: {
port: 9000,
publicPath: '/dist',
},
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/html/index.html',
}),
资源目录前缀是assets,虚拟目录前缀dist:

css仍然渲染失败
推荐实例
资源前缀为web服务器根目录,虚拟目录缺省也为web服务器根目录,htmlWebpackPlugin的html模板生成位置也是服务器根目录,三者统一,最容易调试:
output: {
filename: 'js/bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'dist')
},
devServer: {
port: 9000,
//contentBase: path.join(__dirname, "src/html"),
},
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/html/index.html',
}),
devServer的contentBase一般是绝对路径,只用来访问本地静态文件,如以上配置中的src/html里的静态index.html
(4)服务器自动刷新与热更新
- 配置说明
| options配置 | 配置作用 | 解析 |
|---|---|---|
| inline:false | 配置成iframe刷新模式 | 整个页面刷新,会添加额外iframe到页面结构中 |
| inline:true | 配置成inline刷新模式 | 整个页面刷新,推荐做法 |
| hot:true | 开启热更新HMR | 局部更新,组件级更新,推荐做法 |
- 配置注意事项
如果直接配置,会提示缺组件,并不会像官网说的自动添加相应插件,报错:

需要去掉hot配置项,更改script命令,增加hot选项:
"start": "NODE_ENV=development webpack-dev-server --mode development --hot --open",
这样webpack能自动找到相应plugin安装。
这时更改css能局部刷新,但更改js可能仍然是整个页面刷新
- 热更新的原理
热更新的原理是websocket连接,服务器将变更的文件通知浏览器
6. sourceMap
(1)基本配置
- webpack.config.js中devtool选项控制sourceMap及其规则
- devtool有很多值,不同值影响构建速度
- 开发环境一般用inline-source-map
- 生产环境使用source-map或者不使用sourceMap功能
- 项目较大需要研究构建速度构建文件体积的影响,需要对比权衡多种devtool选项
devtool: 'inline-source-map'
注意: 这个选项仅对js有效,css的sourceMap需要单独设置
(2)css的sourceMap
在webpack.config.js中,css-loader和sass-loader中都要设置sourceMap选项:
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: true
// modules: true,
// localIdentName:
// '[path][name]__[local]--[hash:base64:5]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: loader => [
require('postcss-import')({
root: loader.resourcePath
}),
require('postcss-cssnext')(),
require('cssnano')()
]
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
7. 过期文件清除
(1) 原因
根据webpack.config.js中的css提取插件配置
new MiniCssExtractPlugin({
filename: '[name].[contenthash:5].css',
chunkFilename: '[id].[contenthash:5].css'
})
每次改动代码文件,会出现无用的打包文件,如:

只有最新的hash文件才会被用到,其他文件已经过期不再使用。不止css,也包括js文件也存在这个问题,因此需要在每次build构建前清除输出目录的内容
(2) 配置方法
安装:
yarn add clean-webpack-plugin -D
配置:
const CleanWebpackPlugin = require('clean-webpack-plugin');
let pathsToClean = [
'dist',
]
module.exports = {
...
plugins: [
new CleanWebpackPlugin(pathsToClean)
],
...
};
webpack进阶功能
1. 代码分离
简介
通常我们一个入口生成一个bundle,但一个bundle可能很大,如果能合理拆分并行加载将提高页面的加载速度
常见方法三种
- 手动拆分
- 避免重复
- 侵入式按需拆分
(1)手动拆分
entry: { index: './src/index.js', other: './src/other.js' },
output: {
filename: 'js/[name].bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'dist')
},
注意: output的输出必须写[name]占位符,否则打包的两个文件同名会覆盖
- 这种方法就是写两个入口文件,分别打包为两个bundle.js
- 缺点明显:
- 重复引用,可能两者用到的公共引用会被各自重复打包
- 不能互相之间有关联,两个文件必须是完全独立的,否则就有相互引用的问题
- 实际上这样拆分是很“粗糙”的处理,一般不这么做
(2)避免重复(Prevent Duplication)
- 基于SplitChunks插件(webpack 4.x以前使用CommonsChunkPlugin)
- “避免重复”是官方用词,做的工作其实是将第三方包或者库独立生成bundle.js
a. 配置方法(与entry同级选项)
entry: { index: './src/index.js' },
output: {
filename: 'js/[name].bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'dist')
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
b. 打包结构:

可以看到,第三方包都被单独打包到vendos~index中,源文件index.js打包生成的index.bundle.js只有18.2KB
c. 生成文件的目录结构

可以看到,第三方依赖作为单独bundle使用
d. SplitChunks插件解析
以上基于SplitChunks插件的默认配置,这种配置下才分割代码块(chunk),即:
1. New chunk can be shared OR modules are from the node_modules folder 共享的或者来自npm包的代码块
2. New chunk would be bigger than 30kb (before min+gz) 分割前大于30k的公共模块,如果只是几k的公共代码,是不会分割提取的
3. 在按需加载时,并行请求数量小于等于5
4. 在初始化页面加载时,请求数量小于等于3
实际上相当于如下默认条件:
optimization {
splitChunks: {
chunks: "all",
minSize:30000,
minChunks: 1, // 最小 chunk ,默认1
maxAsyncRequests: 5, // 最大异步请求数, 默认5
maxInitialRequests : 3, // 最大初始化请求书,默认3
automaticNameDelimiter: '~',// 打包分隔符
name:true, // 使下面用到的包的重命名起作用
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
hellolodash:{
test: /lodash/,
priority:0,
name:'lodashlib'
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
name:'lib' // 生成的js/vendor~index.bundle.js改为js/lib.bundle.js
}
}
}
}
以上关键配置说明:
- optimization是webpack的优化选项,splitChunks对全局生效根据规则分离公共代码;针对不同的要提取的公共部分进行单独设置,需要在cacheGroups中单独命名键值对,键是自己命名的,主要是test规则去匹配,值一般是配置选项,上面hellolodash是一个例子,可以将lodash包单独分离出来
- chunks常用有三种选项,推荐使用all
- initial:表示对异步引入的模块不处理
- async:表示只处理异步模块(默认)
- all:无论同步还是异步,都会处理
- miniSize是30k,只有共用组件大于这个值才会被分割抽取,否则抽取意义不大,多了http请求
- 默认有两个cacheGroup,一个为vendors用于处理第三方依赖库;一个是default(处理当module被引用等于或超过2次时情况)
- 默认优先级都是负的,可以保证我们设置的自定义缓存组优先起作用
该插件可以更改默认条件实现更灵活的配置,参见参考文档,这里介绍些常用选项:
1. minSize(默认是30000):形成一个新代码块最小的体积
2. name(打包的chunks的名字):字符串或者函数(函数可以根据条件自定义名字)
3. priority :缓存组打包的先后优先级
(3)动态插入-动态加载依赖
- 简介
- 一般配置到第(2)节就可以了,因为分离了manifest、第三方包、公共代码、自己写的源码,已经基本完成分割任务,一般自己写的源码不会太大。
- 前两节内容是Bundle Split,本节讲的是Code Split,上面切割包,这里切割源代码
- 动态加载依赖方法有两种
- webpack的API-require.ensure()
commonjs语法,传送门
const { getData } = require('./main')
let arr = [1,2,123,21,3,21,321,1]
getData('./index.html')
setTimeout(() => {
require.ensure(['./math'], function(require) {
const { findMaxIndex } = require('./math')
console.log(findMaxIndex(arr))
})
}, 3000)
当执行到require.ensure时,./math依赖才会被加载,如果math中还有依赖,也会被执行,这就是动态依赖加载;动态加载本质还是webpack创建script标签来实现的;原理上,webpack 在编译时,会静态地解析代码中的 require.ensure(),同时将模块添加到一个分开的 chunk 当中。这个新的 chunk 会被 webpack 通过 jsonp 来按需加载。
import('lodash').then(_ => {
// Do something with lodash (a.k.a '_')...
})
使用这种方法在代码中写业务时,webpack会在语法静态分析阶段直接把import的内容打包为独立的chunk,在浏览器运行到这一行代码时,就会自动请求这个资源,实现异步加载
注意:
- ES6的动态加载要求import(url)中url是相对路径,import()匹配符总是相对于被调用的文件
- 如果报错import不支持,可以按错误提示安装插件
yarn add @babel/plugin-syntax-dynamic-import -D
在.babelrc中增加插件
"plugins": ["@babel/plugin-syntax-dynamic-import"]
- 通过注释添加chunk名
import( /* webpackChunkName: "my-chunk-name" */ './a.js').then(module => {
console(module);
}).catch(err => {
console(err.message);
});

2. 按标识符加载相应库插件-ProvidePlugin
功能
- 智能按需加载使用的库,没使用的不会打包进去
- 避免每个模块都引入import、require,根据是否有标识符webpack自动判断
使用
yarn add jquery underscore
不必安装ProvidePlugin,这是webpack内置插件
new webpack.ProvidePlugin({ //它是一个插件,所以需要按插件的用法new一个
$:'jquery', //接收名字:模块名
_:'underscore' //引入underscore库
}),
注意: webpack变量本身要再webpack.config.js中引入
3. webpack可视化查看器
- 安装
yarn add webpack-bundle-analyzer -D
- webpack.config.js配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
- 使用
yarn build
默认启动本地服务器,自动打开分析页面
- 更多插件功能选项请查看官方文档
重要插件选项有:
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false
})
openAnalyzer默认true自动打开分析页
analyzerMode分为server启服务,static生成静态html,disabled不使用三种
此项可以根据process.env.NODE_ENV环境变量来设置只在yarn build时开启
4. 缓存问题
简介
- webpack生成的最终文件如果只有文件名而不包含hash,浏览器通过命中缓存,以降低网络流量,使网站加载速度更快
- 但部署时候却可能得不到最新版本的文件
- 实现缓存的方法是不包含hash,如果包含hash则不会被缓存(相当于加载新文件)
实现
webpack.config.js配置:
output: {
filename: 'js/[name].[hash:5].bundle.js',
//chunkFilename: '[name].[chunkhash:6].js',
chunkFilename: '[name].js',
publicPath: '/',
path: path.resolve(__dirname, 'dist')
},
主要在分离包之后对不常用包直接命名,对常用包加hash的方法实现

评论 0
还没有评论,来抢个沙发吧。