我正在参加「掘金·启航计划」
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 />标签引入。