如何使用 webpack ?
# webpack
# webpack 安装
首先第一步,保证你的 node 是 10 以上
node -v
接下来全局安装 webpack 以及 webpack-cli
npm i webpack webpack-cli -g
安装完成之后,可以查看版本:
webpack -v
webpack-cli -v
2
# Webpack 5 个核心概念
- Entry:指定我们的入口文件
- output:指定出口文件,打包好的文件放在哪个地方
- loader:webpack 本身只能打包 js 文件,使用 loader 来处理 js 以外的文件
- plugins:插件,提供代码压缩,生成 html ,自动清除打包后的文件等一系列功能。
- mode:webpack 对应的模式,分为development 和 production。
# 快速入门示例
首先初始化一个项目
npm init -y
在项目里面安装 webpack 以及 webpack-cli
npm i webpack webpack-cli
接下来创建 src 目录,下面创建 index.js 以及 data.json 这两个文件。
// index.js
import data from './data.json'
console.log(data);
function add(x, y) {
return x + y;
}
console.log(add(1, 2));
// data.json
{
"name" : "william",
"age" : 18
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
接下来使用如下命令进行打包:
webpack ./src/index.js -o ./build/built.js --mode=development
接下来我们使用 production 模式来进行打包
webpack ./src/index.js -o ./build/built.js --mode=production
可以看到,开发模式下代码会进行一个压缩。
webpack 本身只能处理 js 或者 json,不能处理 js 以外的资源。
# webpack 开发环境配置
打包样式资源。通过上面的快速入门示例,我们知道 webpack 只能处理 js 文件,js 以外的不能够处理,这个时候,我们就需要 loader
需要安装的 loader 如下:
npm i style-loader css-loader
需要在项目根目录下面创建 webpack.config.js
// 在项目根目录下面创建 webpack.config.js
// 这是 webpack 的配置文件
const { resolve } = require('path');
module.exports = {
// 入口文件
entry: './src/index.js',
// 出口文件
output: {
filename: 'built.js', // 输出文件名
path: resolve(__dirname,'build')
},
// loader 的配置
module : {
rules: [
{
test : /\.css$/,
// 由于有多个 loader,所以 use 对应的是一个数组
// 数组里面的 loader 前后顺序是有关系的
// 使用loader 的顺序是从后往前
// css-loader:将 css 代码变成字符串,放入到 js 代码里面
// style-loader : 创建 style 标签,将 js 中的样式添加到 head 里面生效
use : ['style-loader','css-loader']
}
]
},
plugins : [],
mode : 'development'
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
使用 webpack 命令即可打包。
如果要使用 css 预处理器(less、sass、stylus),也需要安装对应的 loader,如下:
npm i less less-loader -D
在 webpack 下面的配置如下:
//...
{
test : /\.less$/,
use : ['style-loader','css-loader','less-loader']
}
//...
2
3
4
5
6
# 打包 html 资源
如果要打包 html 资源,需要使用 html-webpack-plugin 这个插件。
npm i html-webpack-plugin -D
接下来在 webpack 配置文件里面进行配置。
//...
plugins : [
// 自动为你生成一个 html 文件,并且引入你打包好的 js 文件
new htmlWebpackPlugin({
template : './src/index.html'
})
],
//...
2
3
4
5
6
7
8
这里顺便再介绍一个插件,clean-webpack-plugin
使用方法和上面的 html 插件一样
npm i clean-webpack-plugin -D
在 webpack 中进行配置:
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
//...
plugins : [
// 自动为你生成一个 html 文件,并且引入你打包好的 js 文件
new htmlWebpackPlugin({
template : './src/index.html'
}),
new CleanWebpackPlugin() // 自动清除上一次的打包
],
//...
2
3
4
5
6
7
8
9
10
11
# 打包图片
首先,我们在 src 目录下面放入 3 张图片。
接下来,在 index.html 中添加 3 个盒子来引入图片。
<body>
<p>this is a test</p>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
</body>
2
3
4
5
6
需要安装两个 loader ,命令如下:
npm i url-loader file-loader -D
注:需要安装的 loader有两个,file-loader 和 url-loader,url-loader 需要依赖于 file-loader
webpack 配置如下:
//...
{
test : /\.(jpg|png|gif)$/,
loader : 'url-loader',
options : {
// 图片大小如果小于 8kb,就会采用 base 64进行处理
// 优点:减少请求数量
// 缺点:css 体积大小会变大
limit : 8* 1024,
name : '[hash:10].[ext]'
}
}
//...
2
3
4
5
6
7
8
9
10
11
12
13
使用 webpack 命令进行打包,至此,图片可以成功进行打包了。
但是,我们图片的使用还有一种情况。
<body>
<p>this is a test</p>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<img src="./angular.jpg" alt="图片未显示">
<script src="built.js"></script></body>
2
3
4
5
6
7
这种情况下,我们发现图片无法正常显示。这个时候需要一个 loader,html-loader。
Webpack 配置如下:
//...
{
test: /\.html$/,
// 处理 html 文件的 img 图片(负责引入 img,从而能够被 url-loader 进行解析)
loader : 'html-loader'
}
//...
2
3
4
5
6
7
# 打包其他资源
在 webpack 中,要打包 html、css、js、图片以外的资源,使用 file-loader。这里以字体为例。
首先需要从 iconfont 上面去下载要用的 图标,接下来在 index.html 里面使用
<body>
<p>this is a test</p>
<span class="iconfont icon-yue"></span>
<span class="iconfont icon-jieqian"></span>
<span class="iconfont icon-huanqian"></span>
<span class="iconfont icon-zhuanzhang"></span>
</body>
2
3
4
5
6
7
接下来在 webpack 配置文件里面进行配置,配置如下:
{
// 排除 css/js/html/less 这些资源
exclude:/\.(css|js|html|less|json)$/,
loader : 'file-loader',
options : {
name :'[hash:5].[ext]'
}
}
2
3
4
5
6
7
8
# devServer
devServer 是一个开发环境下的 Web 服务器,能够为我们提供实时加载的服务。
npm i webpack-dev-server -D
安装好之后,在 webpack 下面进行如下的配置:
// ...
plugins : [],
mode : "production",
// devServer 的作用就是自动运行打包好的项目
// devServer 之所以速度快,是因为打包是打包在内存里面的
devServer : {
// 打包好的项目位于什么位置
contentBase : resolve(__dirname,'build'),
compress : true, // 是否压缩
port : 3000,
open : true // 自动打开浏览器
}
2
3
4
5
6
7
8
9
10
11
12
整个开发环境下的配置,就介绍完毕。
# webpack 生产环境下的配置
- css 的处理
- js 的处理
- html 的处理
# css 的处理
# 1. css 提取单独文件
在生产环境下面,我们会考虑将 css 代码单独提取出来生成 css 文件,而不是像开发环境一样以 style 的形式放入 html 里面。
要提取 css ,可以使用一个插件 mini-css-extract-plugin。
npm i mini-css-extract-plugin -D
接下来就需要在 webpack 配置文件中进行配置。
// 1. 首先需要引入进来
const miniCssExtractPlugin= require('mini-css-extract-plugin');
// 2. 接下来在 plugins 那个地方直接使用即可
//...
plugins : [
//...
new miniCssExtractPlugin({
filename : 'css/built.css'
})
]
//...
// 3. 接下来就是处理 css 的 loader 那个位置
// 这个loader不再使用 style-loader,而是使用 miniCssExtractPlugin 所提供的 loader 来进行处理
use: [miniCssExtractPlugin.loader, 'css-loader']
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2. css 的兼容性
在开发环境下,我们不需要考虑css 的兼容性,一旦到了生产环境,就必须要考虑兼容性问题。
主要是css3,一些老的浏览器,不支持最新的css3某些样式,所以我们需要添加前缀。
- IE:-ms-
- firefox:-moz-
- chrome:-webkit-
这里,我们来演示使用 css 后处理器做css的兼容性处理。
比较有名的 css 后处理器有 post-css。
npm i postcss-loader postcss-preset-env -D
- postcss-loader:负责做 css 的兼容性处理
- postcss-preset-env:找到 package.json 里面 browserlist 的配置,browserlist 的配置指定了 css 要兼容的版本。
接下来在 webpack 中进行配置:
use: [
miniCssExtractPlugin.loader,
'css-loader',
{
loader : 'postcss-loader',
options : {
postcssOptions : {
ident : 'postcss',
// 设置该插件的目的是为了去读取 package.json 里面的 browselist 的配置信息
plugins : ['postcss-preset-env']
}
}
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
Webpack 中配置完毕后,在 package.json 里面做 browserslist 的配置:
"browserslist" : {
"development" : [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production" : [
">0.2%",
"not dead",
"not op_mini all"
]
}
2
3
4
5
6
7
8
9
10
11
12
# 3. css 的压缩
在生产环境下,我们还需要对 css 进行一个压缩。以便能够得到更快的请求速度。
npm i optimize-css-assets-webpack-plugin -D
下载完成之后,老规矩,仍然是在 webpack 的 plugins 数组里面配置一下即可。
plugins : [
//...
new optimizeCssAssetsWebpackPlugin()
]
2
3
4
# js 的处理
- js 的语法检查
- js 兼容性处理
- js 的压缩
# 1. js 语法检查
说到 js 的语法检查,就要提到一个工具 ESlint(jslint)
在进行团队开发项目的时候,我们往往要统一整个团队的编码风格,这个时候,就可以使用诸如 eslint 这样的代码风格检查工具来帮助我们做语法检查。
npm i eslint eslint-loader eslint-config-airbnb-base eslint-plugin-import -D
接下来,我们就在 webpack 中进行 eslint 的配置。
module : {
//...
{
test : /\.js$/,
exclude : /node_modules/,
loader : 'eslint-loader'
},
}
2
3
4
5
6
7
8
接下来,我们需要指定我们的编码风格是什么。
所以,我们在 package.json 里面进行配置。
"eslintConfig" : {
"extends" : "airbnb-base"
}
2
3
如果想要自动修复错误,可以在配置 loader 的时候添加一个 options,如下:
{
test : /\.js$/,
exclude : /node_modules/,
loader : 'eslint-loader',
options : {
fix : true // 自动修复代码
}
},
2
3
4
5
6
7
8
如果想要 eslint 不做某一行的代码检查,添加如下的代码:
// eslint-disable-next-line
console.log(data);
2
# 2. js 兼容性处理
一说到 js 的兼容性处理。就不得不提到大名鼎鼎的 babel(通天塔)。
可以将你的 es6+ 的语法转换为 es5、es3的语法。
例如,在index.js 中,写入如下的代码:
const add = (x, y) => x + y;
在打包的时候,我们发现书写的箭头函数,打包出来仍然是箭头函数。
那么,为了做 js 的兼容性处理,我们需要将这些很新的语法转换为 es5 的语法。
所以需要使用到 babel。
npm i babel-loader @babel/core @babel/preset-env -D
接下来,就需要在 webpack 配置文件中进行配置。
{
test : /\.js$/,
exclude : /node_modules/,
loader : 'babel-loader',
options : {
presets : ['@babel/preset-env']
}
},
2
3
4
5
6
7
8
打包之后,可以看到箭头函数就已经被转换为了es5的普通函数。
但是,这个@babel/preset-env只能转换基本语法,有一些高级语法无法进行转换。比如 promise。
例如在index.js 中添加 promise语法,如下:
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 2000);
});
2
3
4
5
打包完成后,会发现 Promise 并没有转换。
这里需要安装 core-js
npm i core-js
安装完成后,需要在 webpack 的配置文件里面进行配置:
module : {
rules : [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', // 只能转译一些简单的语法
// 后面我们又安装了 core-js
{
// 按需加载
useBuiltIns: 'usage',
// 指定 core-js 版本
corejs: {
version: 3
},
// 要兼容的浏览器的版本
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10'
}
}
]
]
}
},
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 3. js 的压缩
js 的压缩不需要安装其他额外的插件。只需要将 webpack 的配置里面的 mode 由 development 修改为 production 即可。
module.exports = {
// ...
mode : 'production'
}
2
3
4
# html 的处理
除了 css、js 文件需要压缩以外,html 也需要压缩。html 压缩直接使用之前安装的 html-webpack-plugin 插件,进行简单配置即可。
plugins : [
new htmlWebpackPlugin({
template: './src/index.html',
minify : {
collapseWhitespace : true,
removeComments : true
}
})
]
2
3
4
5
6
7
8
9
至此,整个webpack 生产环境下面的配置就介绍完毕了。
# webpack 环境的性能优化相关知识
一个是开发环境的性能优化,一个生产环境的性能优化。
开发环境性能优化
- 优化打包速度 - HMR
- 优化代码调试
- source-map
生产环境性能优化
- 优化构建速度
- 优化代码运行的性能
# 开发环境性能优化
# 优化打包速度
这里介绍一个 HMR。英语全称 hot module replacement。翻译成中文就是“热模块替换”。
默认情况下,使用 devServer 启动应用之后,修改任意一个文件,所有的模块全部重新加载。
那么,这个地方,我们就可以进行性能上面的优化。
css 的 HMR 功能是由 style-loader 来实现的。所以,我们要开启 css 的 HMR 功能,只需要做两个步骤:
首先第一步,在 devServer 里面设置 hot 为 true
devServer: {
// 打包好的项目位于什么位置
contentBase: resolve(__dirname, 'build'),
compress: true, // 是否压缩
port: 3000,
open: true, // 自动打开浏览器
// 开启了 HMR 功能
// css 的 HMR 功能是由 style-loader 来实现的
hot : true
}
2
3
4
5
6
7
8
9
10
第二步,因为依赖于 style-loader,所以重新打开 style-loader 来引入 css。
更新 html 文件,也存在一个问题,就是当我们更新了 html 内容之后,并没有实时进行更新。
要解决这个问题,可以更改入口文件,修改方式如下:
entry: ['./src/js/index.js','./src/index.html']
最后就是 js 文件。假设我们修改的是 index.js,该文件是所有 js 文件的入口文件,一旦修改index.js,下面的模块重新加载,这个是合理的。但是如果是index下面的模块发生更改,其他所有模块全部更新,这个是不合理。
要开启 js 文件的 HMR 功能,代码如下:
// 如果这个模块支持热更新
if(module.hot){
module.hot.accept('./test.js',function(){
// 该方法会监听 test.js 的变化,一旦发生变化,做回调函数里面书写的代码
// 这样的好处在于,其他模块不会重新打包构建
test();
})
}
2
3
4
5
6
7
8
# 开发环境下的代码调试
# source-map
所谓 source-map,指的是提供将源代码映射到构建后代码的一种技术。
解决的问题就是如果构建后的代码出错了,通过映射,我们可以追踪到源码。
要开启 source-map,很简单,只需要在 webpack 配置文件里面配置一下即可,如下:
devtool : 'source-map'
# 生产环境性能优化
# 优化打包构建的速度
# oneOf
当我们在对代码进行打包的时候,非 js 文件需要使用对应的 loader 来进行处理。
但是在 loader 的匹配上面,也有可以优化的地方。loader 是通过正则来进行匹配的。
test:/\.css$/
test:/\.(jpg|png|gif)$/
test:/\.js$/
2
3
但是,loader 在进行匹配的时候,会存在一个效率问题。一种文件类型在 loader 匹配上之后,还会进行多余的 loader 的匹配(虽然不会进去)。
所以,这里我们就可以通过 oneOf 设置,来设置一种文件类型匹配上一种 loader 之后,停止多余的匹配。
配置如下:
module : {
rules : [
{
oneOf : [
// 对应的 loader
{},
{},
{}
]
}
]
}
2
3
4
5
6
7
8
9
10
11
12
# babel 缓存
babel 的作用就是将 es6 的代码转换为 es5的代码,从而可以兼容更多的浏览器。
每一次babel在进行编译的时候,代码都要重新运行一次,我们可以开启 babel 缓存,这样第二次构建的时候,babel不需要在重新编译代码了。直接从缓存中获取编译好了的代码即可。
开启 babel 缓存的方法也非常简单,直接在 配置 babel-loader 的地方,开启即可,如下:
cacheDirectory : true // 开启 babel 的缓存,第二次构建项目的时候,直接从缓存里面获取编译好的代码
# externals
在我们进行打包的时候,往往第三方包不需要进行打包。
我们可以使用 externals 来指定要排除的包
module.exports = {
//...
externals : {
// 忽略的库名字 : npm 包的名字
jquery : 'jQuery'
}
}
2
3
4
5
6
7
通过上面的方式,我们可以在打包的时候排除 jquery,但是有一个新的问题,因为 jquery 没有打包进行,所以代码无法运行。
# dll
dll 英语全称 dynamic link library,翻译成中文就是“动态链接库”。
在 webpack 里面,支持使用 dll 来解决第三方库的打包解决方案。
它的思路和 externals 不一样,externals 是第三方包不打包,通过 cdn 的方式引入第三方包。
dll 是提前将第三方包进行打包,之后再打包我们的源码的时候,第三方包就不会再进行打包了。我们只需要在进行源码打包的时候,引入打包好的第三方包即可。
首先,既然要对第三方进行打包,就需要一个第三方包打包的配置文件。
配置文件如下:
// /webpack.dll.js
/*
使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
当你运行 webpack 时,默认查找 webpack.config.js 配置文件
需求:需要运行 webpack.dll.js 文件
--> webpack --config webpack.dll.js
*/
const { resolve } = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
// 最终打包生成的[name] --> jquery
// ['jquery'] --> 要打包的库是jquery
jquery: ['jquery'],
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins: [
// 打包生成一个 manifest.json --> 提供和jquery映射
new webpack.DllPlugin({
name: '[name]_[hash]', // 映射库的暴露的内容名称
path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
})
],
mode: 'production'
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
接下来,我在打包源码的时候,需要做两件事情。首先告诉 webpack 不需要打包 jquery,第二件事儿引入打包好的 jquery。
const webpack = require('webpack');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
plugins : [
// 先引入关联关系
new webpack.DllReferencePlugin({
manifest : resolve(__dirname,'dll/manifest.json')
}),
// 将这个资源引入到 html 里面
new AddAssetHtmlPlugin({
filepath : resolve(__dirname,'dll/jquery.js')
})
]
2
3
4
5
6
7
8
9
10
11
12
13
14