Appearance
前言
前端构建工具 Webpack
最近特火,火到 Vue
/React
官方推出的脚手架都是基于 Webpck
打造的。
为了更了解 Webpack
,特意实打实地安装配置 Webpack
。对以后进阶学习也能夯实基础,现在一起学习入门级的 Webpack
吧!
认识 Webpack
先来观察应用 Webpack
能做的事:

从图中得出:Webpack
能打包所有 JS
脚本;能打包所有 style
样式;能打包所有图片;能打包所有预编译语言。通俗的理解就是能打包前端所有资源。
安装 Webpack
首先确保你已经安装了 Node.js
和 Git
。找到存放项目的目录,在该目录下初始化项目。在终端执行:
$ npm init
// 或者
$ npm init -y
$ npm init
// 或者
$ npm init -y
初始化后生成一个 package.json
文件,大致内容:
{
"name": "webpack",
"version": "1.0.0",
"description": "study-webpack",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"study-webpack"
],
"author": "yuan",
"license": "ISC"
}
{
"name": "webpack",
"version": "1.0.0",
"description": "study-webpack",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"study-webpack"
],
"author": "yuan",
"license": "ISC"
}
为了后续快速安装其他依赖,这里使用淘宝镜像。在终端执行:
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
接来下安装 Webpack
,在 npm
官网查询安装手册。在终端执行:
$ cnpm install --save-dev webpack
// 或者
$ yarn add webpack --dev
$ cnpm install --save-dev webpack
// 或者
$ yarn add webpack --dev
附:使用 yarn
语法安装,确保已经安装 yarn
。
注意:最新版本 Webpack
中 webpack-cli
从中分离了出来需要单独安装。在终端执行:
$ cnpm i webpack-cli --save-dev
$ cnpm i webpack-cli --save-dev
安装完 Webpack
之后需要其运行起来,得需要一个配置文件,其名称为 webpack.config.js
,不能为其他名称。如果是其他名称 Webpack
找不到该配置文件,就抛出错误提示。
运行 Webpack
查询官网手册后,填写 webpack.config.js
配置。对 entry
属性值和 filename
属性值进行简单修改,webpack.config.js
大致内容如下:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
module.exports
导出一个对象,其中:
entry
表示打包资源入口,该字段属性值可以是 String
/ Array
/ Object
。
output
表示打包资源出口,也就是经打包的资源从该口输出。
dist
是 Webpack
打包完成后存放资源的目录。
配置完内容后,在根目录下创建目录 src
,里面编写一个叫 index.js
脚本:
// index.js
document.write('Hello Webpack!');
// index.js
document.write('Hello Webpack!');
为了方便看效果,在根目录下创建一个 index.html
模版,并引入打包后的资源 bundle.js
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>入门webpack</title>
</head>
<body>
</body>
</html>
<script src="./dist/bundle.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>入门webpack</title>
</head>
<body>
</body>
</html>
<script src="./dist/bundle.js"></script>
使用预定义命令启动 Webpack
,可以在 package.json
文件中的 scripts
字段中添加命令。
// ...
"scripts": {
"start": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
}
// ...
// ...
"scripts": {
"start": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
}
// ...
在终端执行 cnpm start
打包完成后会看到在根目录下生成 dist
目录,里面包含 bundle.js
脚本,在浏览器端运行 index.html
可以看到页面输出 Hello Webpack
!。
到此完成了 Webpack
初步的打包。
执行 Webpack
打包时,终端执行输出一些信息:
Hash: 9d157b09dd8d37122dad
Version: webpack 4.42.1
Time: 560ms
Built at: 2020-04-19 11:48:36
Asset Size Chunks Chunk Names
bundle.js 961 bytes 0 [emitted] main
Entrypoint main = bundle.js
[0] ./src/index.js 31 bytes {0} [built]
Hash: 9d157b09dd8d37122dad
Version: webpack 4.42.1
Time: 560ms
Built at: 2020-04-19 11:48:36
Asset Size Chunks Chunk Names
bundle.js 961 bytes 0 [emitted] main
Entrypoint main = bundle.js
[0] ./src/index.js 31 bytes {0} [built]
Hash
表示当前文件打包生成的hash
值,文件改变,hash
值就会变。Version
表示项目当前安装Webpack
的版本。Time
表示项目打包所花费的时间。Build
表示项目打包日期,打包生成文件名称和文件大小Entrypoint
表示项目打包入口点。
即:
module.exports = {
entry: './src/index.js',
// ...
}
// 等价于
module.exports = {
entry: {
main: './src/index.js'
},
// ...
}
module.exports = {
entry: './src/index.js',
// ...
}
// 等价于
module.exports = {
entry: {
main: './src/index.js'
},
// ...
}
chunks
: 打包文件的id
,现在只有一个bundle.js
打包文件,有多个的时候,会有多个不同的chunk
。Chunk Names
: 打包文件的名字。
最后一行表示打包生成的文件路径。
大多数网站中都会使用缓存,减少页面加载时长。
Webpack
打包也可以做到这点,把之前的 bundle.js
改成带有 hash
值。
修改后的 webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[hash].js' // hash
}
};
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[hash].js' // hash
}
};
重新运行 cnpm start
打包完成后会看到根目录下生成 dist
目录,里面包含带 hash
值的资源。如果想缩短 hash
值,可以进行截取长度,比如:[hash:6]
。
如果想要优化打包后的资源和想使用 Webpack
强大的功能,请继续往下看。
认识 Plugin
plugin
是 Webpack
的核心,Webpack
自身的多数功能都是用这个插件接口,让 Webpack
打包变得极其灵活。
经过认识 Webpack
初次打包后,发现每次执行 cnpm start
打包完成后都会在 dist
目录中追加打包生成后的新资源。造成 dist
文件很大。这时 clean-webpack-plugin
就可以登场,帮助我们解决这个问题。
在 npm
官网搜索该插件,点击名称进入详情查看安装手册,在终端执行:
$ cnpm i --save-dev clean-webpack-plugin
$ cnpm i --save-dev clean-webpack-plugin
在 webpack.config.js
中添加该配置:
// ...
const { CleanWebpackPlugin } =require('clean-webpack-plugin');
module.exports = {
plugins: [
new webpack.ProgressPlugin(),
new CleanWebpackPlugin()
]
}
// ...
const { CleanWebpackPlugin } =require('clean-webpack-plugin');
module.exports = {
plugins: [
new webpack.ProgressPlugin(),
new CleanWebpackPlugin()
]
}
在终端执行 cnpm start
会看到上一次打包生成的资源自动删除后,重新创建新的打包资源。
如果在项目中要引入打包后的资源,并且该资源带有 hash
值时不易方便使用,脚本太多也不易区分,这时可以使用 Webpack
提供的 HTML
模版插件解决问题。
在 npm
官网搜索该插件,点名称进去查看安装手册,在终端执行:
$ cnpm i --save-dev html-webpack-plugin
$ cnpm i --save-dev html-webpack-plugin
安装成功后,在根目录 package.json
中的 devDependencies
里能看到该插件和该插件的版本。
在 Webpack
中配置该插件:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[hash:6].js'
},
plugins: [
new HtmlWebpackPlugin() // htmlPlugin
]
};
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[hash:6].js'
},
plugins: [
new HtmlWebpackPlugin() // htmlPlugin
]
};
在终端执行 cnpm start
会看到在根目录下生成 dist
目录,里面包含带 hash
值的资源和压缩过的 index.html
。
如果不想使用压缩过的资源,可以在 webpack.config.js
中进行配置:
// ...
module.exports = {
mode: "development",
// ...
}
// ...
module.exports = {
mode: "development",
// ...
}
根据 mode
参数 Webpack
会区分是生产环境还是开发环境。一般生成环境 mode
设置为 production
,开发环境设置为 development
。
设置完成后,在终端执行 cnpm start
打包完成后,然后在浏览器上运行 index.html
可以看到输出内容没变化,页面代码没有压缩。
如果想对 src/index.html
做一些调整,比如:修改 title
,创建多个模版文件,多个模版文件引入不同的脚本等等;只需要在 new HtmlWebpackPlugin()
中添加一些配置项就能解决。
比如修改 title
:
module.exports = {
// ...
new HtmlWebpackPlugin({
title: '学习webpack'
})
// ...
}
module.exports = {
// ...
new HtmlWebpackPlugin({
title: '学习webpack'
})
// ...
}
在终端执行 cnpm start
打包完成后,在浏览器上运行 index.html
会看到 title
的变化。
项目难免会美化页面,那么就得给页面添加一些样式,可以写在单独文件中,可以写在 .html
模版中,这时处理 CSS
可以使用 css-loader
解决问题。
认识 loader
loader
用于对模块的源代码进行转换。loader
可以在 import
或"加载"模块时预处理文件。可以将文件从不同的语言(如 TypeScript
)转换为 JavaScript
,或将内联图像转换为 data URL
。loader
甚至允许直接在 JavaScript
模块中 import CSS
文件。
①、处理 CSS
首先安装处理 CSS
相应的 loader
:css-loader
和 style-loader
。
在 npm
官网搜索该 loader
,点击名称进去查看安装手册,在终端执行:
$ cnpm i --save-dev css-loader style-loader
$ cnpm i --save-dev css-loader style-loader
css-loader
处理以.css
后缀的文件。style-loader
经过css-loader
处理过的CSS
插入到DOM
中。
安装成功后,在根目录 package.json
中的 devDependencies
里能看到该 loader
和该 loader
的版本。
在 webpack.config.js
中添加处理 CSS
的 loader
配置:
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
},
// ...
}
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
},
// ...
}
注意:use
选项顺序,先使用 css-loader
再使用 style-loader
。
项目中一般都是使用单独文件写入样式,这里使用以 .css
为后缀的文件负责控制页面样式:
*{ margin:0px;padding:0px;}
body{ background: red; }
*{ margin:0px;padding:0px;}
body{ background: red; }
在根目录 src/index.js
中引入该样式文件:
document.write('hello webpack')
require('./style.css')
document.write('hello webpack')
require('./style.css')
在终端执行 cnpm start
打包完成后,在浏览器上运行 index.html
,能看到页面背景色变红色。使用开发者工具也能看到页面插入 style
标签,style
标签里面嵌入刚刚写的样式:

随着项目复杂度的提升,控制页面的样式也很多;如果按照这样写法,页面会有一大段来控制样式,考虑到对后期的性能优化不友好,可以考虑把样式单独打包一个文件。
②、提取 CSS
新版本 Webpack4.x
建议使用 mini-css-extract-plugin
。
在 npm
官网搜索该插件,点击名称进去查看安装手册,在终端执行:
$ cnpm install --save-dev mini-css-extract-plugin
$ cnpm install --save-dev mini-css-extract-plugin
安装完该插件后,在根目录 package.json
中的 devDependencies
里能看到该插件和该插件的版本。
在 webpack.config.js
中的 module
选项和 plugin
选项中配置该插件:
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true,
},
},
'css-loader',
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: '学习webpack'
}),
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}
// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true,
},
},
'css-loader',
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: '学习webpack'
}),
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}
在终端执行 cnpm start
打包完成后,在浏览器上运行 index.html
,可以看到与之前效果一样;使用开发者工具能看到生成的样式文件 style.css
,之前嵌套在页面中的样式不见了。
效果如下:

大型项目中一般会选择应用预编译语言,这里使用 Sass
预编译语言。
在 nmp
官网搜索该 loader
,点击名称进去查看使用安装手册,在终端执行:
$ cnpm install --save-dev sass-loader node-sass
$ cnpm install --save-dev sass-loader node-sass
安装完毕后,在根目录 package.json
中的 devDependencies
里能看到该 loader
和该 loader
的版本。
在 webpack.config.js
中的 module
选项中配置:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
}
]
}
// ...
}
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
'style-loader',
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
}
]
}
// ...
}
同样在根目录 src
下创建一个专门存放 sass
样式文件:
// index.scss
$fontSize: 16px;
body{ font-size:$fontSize;}
// index.scss
$fontSize: 16px;
body{ font-size:$fontSize;}
在根目录 src/index.js
中引入该 scss
文件:
document.write('hello webpack')
require('./style.css')
require('./index.scss')
document.write('hello webpack')
require('./style.css')
require('./index.scss')
在终端执行 cnpm start
打包完成后,在浏览器上运行 index.html
,能看到页面字体变化。
③、打包图片
打包处理图片使用 url-loader
和 file-loader
。
在 npm
官网搜索该 loader
,点击名称进去查看安装手册,在终端执行:
$ cnpm install url-loader file-loader --save-dev
$ cnpm install url-loader file-loader --save-dev
安装完该插件后,在根目录 package.json
中的 devDependencies
里能看到该 loader
和该 loader
的版本。
在 webpack.config.js
中的 module
选项中配置:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(png|jpe?g|gif)$/i,
loader: 'file-loader',
options: {
outputPath: 'images',
name: '[1]-[name].[ext]'
},
}
]
},
// ...
}
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(png|jpe?g|gif)$/i,
loader: 'file-loader',
options: {
outputPath: 'images',
name: '[1]-[name].[ext]'
},
}
]
},
// ...
}
在 style.css
样式文件中引入一张图片作为背景图:
*{ margin:0px;padding:0px;}
body{ background-color: red; }
body{ background: url('./webpack1-3.jpg') repeat-x; }
*{ margin:0px;padding:0px;}
body{ background-color: red; }
body{ background: url('./webpack1-3.jpg') repeat-x; }
在终端执行 cnpm start
打包完成后,在浏览器上运行 index.html
,能看到之前的背景色被改变了。
技术不断创新,前端技术领域也是如此。新项目中越来越喜欢使用 ES6
作为处理 JS
页面数据交互,接下来继续打包 ES6
。
④、打包 ES6
打包处理 ES6
使用 babel-loader
、babel-core
、babel-preset-env
、babel-preset-es2015
。
在 npm
官网搜索该 loader
,点击名称金进去查看安装手册,在终端执行:
$ cnpm i babel-loader babel-core babel-preset-env babel-preset-es2015 --save-dev
$ cnpm i babel-loader babel-core babel-preset-env babel-preset-es2015 --save-dev
附:如果运行出错可以安装:@babel/core
,@babel/preset-env
;其中babel-loader
转换 js
加载器;@babel/core
为 babel
的核心模块;@babel/preser-env
将 ES6
转为 ES5
;babel-preset-es2015
将部分 ES6
转化成 ES5
语法。
安装完该插件后,在根目录 package.json
中的 devDependencies
里能看到该 loader
和该 loader
的版本。
在 webpack.config.js
中的 module
选项中配置:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
// ...
}
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
// ...
}
项目中处理页面数据交互一般都是存放单独脚本文件,因此在根目录下中的 src
目录下创建一个脚本文件 es6.js
:
let today = '今天天气很好';
alert(today);
let today = '今天天气很好';
alert(today);
在根目录 src/index.js
中引入该脚本文件:
document.write('hello webpack')
require('./style.css')
require('./es6.js')
document.write('hello webpack')
require('./style.css')
require('./es6.js')
此时运行 cnpm install
肯定会报错,因为需要设置 babel
。在根目录下创建 .babelre
大致内容大致如下:
{
'presets':['env']
}
{
'presets':['env']
}
这时在终端执行 cnpm start
打包完成后,在浏览器上运行 index.html
,能看到页面弹出的语句。
目前为止打包生成的 index.html
模版每次都得重新刷新页面。为了提高开发效率 Webpack
提供了开启服务热更新替换,不用刷新界面就能实现热更新。下面实现自动开启服务热更新。
devServer
查看 Webpack
手册安装相关模块,在终端执行:
$ cnpm i --save-dev webpack-dev-server
$ cnpm i --save-dev webpack-dev-server
安装成功后,在根目录 package.json
中的 devDependencies
里能看到 dev-server
和 dev-server
的版本
在 webpack.config.js
中配置:
// ...
const webpack = require('webpack')
module.exports = {
// ...
devServer: {
contentBase:path.resolve(__dirname, 'dist'),
host: 'localhost',
port:8090,
open: true, // 自动打开浏览器
hot: true // 热更新
}
}
// ...
const webpack = require('webpack')
module.exports = {
// ...
devServer: {
contentBase:path.resolve(__dirname, 'dist'),
host: 'localhost',
port:8090,
open: true, // 自动打开浏览器
hot: true // 热更新
}
}
修改 package.json
文件中的 scripts
字段里面的 start
属性对应的属性值:
// ...
"scripts": {
"start": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ...
// ...
"scripts": {
"start": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ...
也可以添加新的自定义启动 Webpack
命令,如 run
命令:
// ...
"scripts": {
"start": "webpack",
"run": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ...
// ...
"scripts": {
"start": "webpack",
"run": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ...
在终端执行 cnpm start
或者 cnpm run
会看到浏览器自动打开生成的 index.html
。然后在 styl.css
中添加样式,如:font-size:30px
页面会自动更新并显示最新内容。
项目中或许会用到 jQuery
库或者 Vue
,那么如何使用呢?继续往下看。
快捷导入
首先在 npm
官网查询要安装的包,如 jQuery
,在终端执行:
$ cnpm install --save-dev jquery
$ cnpm install --save-dev jquery
安装完毕之后,在根目录 package.json
中的 devDependencies
里能看到 jQuery
和 jQuery
的版本。
在 webpack.config.js
中的 plugins
选项中配置:
modules.exports = {
// ...
plugins: [
// ...
new webpack.ProvidePlugin({
$: 'jquery',
})
]
// ...
}
modules.exports = {
// ...
plugins: [
// ...
new webpack.ProvidePlugin({
$: 'jquery',
})
]
// ...
}
为了方便看效果,在根目录下的 src
中创建新的文件,如:es5.js
:
// es5.js
$('body').text('Hello World!!!')
// es5.js
$('body').text('Hello World!!!')
在根目录 src/index.js
中引入该脚本:
document.write('hello webpack')
require('./style.css')
require('./es6.js')
require('./es5.js')
document.write('hello webpack')
require('./style.css')
require('./es6.js')
require('./es5.js')
这时在页面上能看到 Hello World!!!
字样。到此为止一个入门级带有热更新的 Webpack
学习完毕。
总结
到这里入门级 Webpack
就算结束了。以上内容从认识 Webpack
到使用各种 loader
和各种 plugins
打包生成资源应用在项目中,认识了基本的使用语法和应用各种配置,也为以后进阶的学习夯实基础。
针对不同的项目,还有更多的 Webpack
打包细节需要调优,如:如何减少搜索文件;如何提高 loader
的打包速度;如何排除项目中无用的打包文件等等。在接下来的时间里,慢慢研究与学习。