我正在参加「掘金·启航计划」
Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。本文主要写了Webpack的核心概念和一些基础配置。
Webpack 系列专栏地址 进入
本文demo地址查看
Webpack 基础
创建项目
- 初始化
npm init -y
v4
版本之后使用webpack需要安装核心包webapack
和 命令行工具包webpack-cli
,使用局部安装的方式
npm install webpack@4.43.0 webpack-cli@3.3.12 -D
版本相关
webpack的v5.x
版本发布日期是 2020.10
,v4.x
相对稳定,本文也是基于v4
版本。
v4
和v5
配置项相似,学会了v4
再去接触v5
也能很轻松的过度。
上面安装了webpack
和 webpack-cli
的指定版本,安装的时候需要注意命令行工具包 webpack-cli
比 核心包 webapack
会低一个版本。 比如本文安装的webapack
v4
版本 对应的webpack-cli
就是 v3
版本,如果要使用v5
版本,那么安装的就是 webpack@5
webapck-cli@4
。
打包
- 创建
/src/index.js
console.log('hello webpack');
- 执行打包命令:
npx webpack
- 也可以在
package.json
中配置命令:
"scripts": {
"build": "webpack"
}
这样可以通过 npm run build
命令打包。
打包完成,会在根目录下生成dist
目录,以及dist/index.js
文件。
webpack 配置文件
默认配置文件webpack.config.js
,webpack
是基于nodejs
的,所以可以在配置文件中使用nodejs的语法。
const { resolve } = require("path");
module.exports = {
// 入口
entry: "./src/index.js",
// 出口
output: {
// 生成资源存放的位置;path 必须是绝对路径,默认是 dist目录
path: resolve(__dirname, "./build"),
// 生成的资源叫什么
filename: "index.js",
},
};
设置 打包的模式mode
,mode
可选值有 none
production
development
,
mode: "development",
执行 npm run build
打包。
分析 bundle 文件
关键字说明
bundle
文件: 经过webpack
打包出来的资源文件,如 dist目录下的文件。chunk
:代码片段,bundle
包含chunk
。
分析 bundle
在 development
模式下打包,会有大量的注释方便我们读取bundle文件的内容。
通过查看bundle文件/build/index.js
,可以知道webpack
打包完成是自执行函数,结构像下面这样:
(function(){
// ...
})({
// ...
})
自执行函数的参数是一个对象{}
称为依赖图谱
它记录了模块路径以及该模块被打包编译生成的chunk
。
依赖图谱
是一个对象结构,它的 key
是模块的路径 ./src/index.js
, value
是该模块(index.js)被被编译后的 代码片段chunk
。
如上图:chunk
存在于 eval
语句中,原材料交给webpack打包之后,都被打包进了这个eval
函数中执行,截图中可以看到这个函数只有一行,但是这个文件总共有100多行,其他又都是什么内容?如下图:
这些内容可以理解为是webpack的基础模板,称为webpackBootstrap
,里面定义了很多基础函数对象等,这是为了保证eval
运行时变量函数不会缺失。不管会不会用到webpackBootstrap
,它会被打包进来。
原材料中的export
等编译后会被替换成 __webpack_exports__
__webpack_require__
类似的对象或方法,这些方法就是存在于webpackBootstrap
中,这样遇到可能不支持的import export
等语法环境的浏览器也可以正常运行。
webpack
的主要能力是做项目构建,可以应用复杂场景,打包公共库只是他的一个基础能力。而rollup
的核心能力就是放在打包公共库上面 可以进行tree shaking
等优化。两者重心不一样。
分析 chunk
关键概念
chunk
:代码片段,一个module对应一个chunkchunks
:chunk组 (包含至少一个chunk(module))chunkName
:chunk的有效名称bundle
:打包出来的文件module
:模块,entry入口中键值对应的文件所使用的模块
打包多页面
上面的配置文件中的入口entry
的值是一个字符串,此时是spa单页面应用。将entry
的值变为对象,打包就会成为mpa多页面应用。
新建 /src/a.js
,在index.js
中导入使用:
// a.js
export const name = "kongcodes";
// index.js
import { name } from "./a.js";
console.log("hello webpack");
console.log(name);
新建 /src/login.js
:
// login.js
console.log('login');
修改入口配置文件
// 入口
// entry: "./src/index.js",
entry: {
index: "./src/index.js",
login: "./src/login.js",
},
根据上面说的关键概念,可以将index入口可以理解为:
bundle | chuckName | module | chunks |
---|---|---|---|
index.js文件 | index | ['index.js', 'a.js'] | [chunk, chunk] |
- 一个
bundle
对应一个chunkName
(chunks: []
) - 一个
chunksName
(chunks
) 包含至少一个module
(chunk
) - 一个模块
module
对应一个chunk
修改出口配置文件
入口entry
变成多个,output
也要调整为多个出口。可以使用[name]
占位符, 会被替换为有效的值,也就是入口的key
值。
// 出口
output: {
// 生成资源存放的位置;path 必须是绝对路径,默认是 dist目录
path: resolve(__dirname, "./build"),
// 生成的资源叫什么
filename: "[name].js",
},
执行打包命令,build目录下多出 index.js
login.js
两个文件。
占位符:
[name]
会被替换为有效的值,也就是入口的key
值。
Plugin
html-webpack-plugin
html-webpack-plugin
插件将为你生成一个 HTML5 文件, 在 body 中使用 script
标签引入你所有 webpack 生成的 bundle。
安装:
npm install html-webpack-plugin@4 -D
新建 /public/index.html
/public/login.html
两个文件,作为打包项目的模板页面。
html-webpack-plugin
插件的配置:
const htmlWebpackPlugin = require("html-webpack-plugin");
plugins: [
new htmlWebpackPlugin({
template: "./public/index.html",
filename: "index.html",
}),
new htmlWebpackPlugin({
template: "./public/login.html",
filename: "login.html",
}),
],
由于是多页面项目,所以要写两个配置对象。执行打包命令,打包完成,build目录下多出两个文件 index.html
login.html
,并且自动引入了js文件。但是引入的js文件并不是对应的,而是将两个js文件都引入了,并没有区分开。
下面通过为插件添加chunks
配置项解决这个问题,让html模板能区分引入不同的js文件。
new htmlWebpackPlugin({
template: "./public/index.html",
filename: "index.html",
chunks: ["index"],
}),
new htmlWebpackPlugin({
template: "./public/login.html",
filename: "login.html",
chunks: ["login"],
}),
再次执行打包命令,发现不同的html模板,根据chunks
配置项的不同引入了各自对应的bundle文件。
clean-webpack-plugin
目录清理插件
npm install clean-webpack-plugin -D
配置:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
plugins: [
// ...
new CleanWebpackPlugin(),
],
Loader
遇到非 js
非 json
的文件就需要用到 loader
编译。
css-loader
新建/src/style/index.css
,在index.js
中引入。
安装 css-loader
npm install css-loader@5 -D
配置:
module: {
rules: [
{
test: /\.css$/,
use: "css-loader",
},
],
},
上述配置项的含义是:当编译时遇到以css结尾的文件时,就把它交给css-loader
处理。只使用css-loader
只是将css代码序列化,还需要配合style-loader
使用。
style-loader
安装:
npm install style-loader@2 -D
配置:多个loader
作用于同一个模块时,要将use
字段改为数组形式,并且带有执行顺序,自后往前的顺序进行编译。
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
上述配置,表示编译时先使用css-loader
将css代码序列化,之后使用style-loader
通过dom操作在html的head标签中创建<style></style>
并将css-loader
处理过的css塞到这个标签中。
MiniCssExtractPlugin
上面使用style-loader
相当于内联的方式使用css,MiniCssExtractPlugin
插件可以样式抽取并生成独立的css文件。
安装:
npm install --save-dev mini-css-extract-plugin@1
使用:
const minicssPlugin = require("mini-css-extract-plugin");
plugins: [
// ...
new minicssPlugin({
filename: "index.css",
}),
],
使用 minicssPlugin
自带的loader:
module: {
rules: [
{
test: /\.css$/,
// use: ["style-loader", "css-loader"],
use: [minicssPlugin.loader, "css-loader"],
},
],
},
执行打包命令,查看打包完成的目录,目录多出了index.css
文件,并在index.html
中使用<link />
标签引入。