Webpack系列(一)核心概念和基础配置

110 阅读6分钟

我正在参加「掘金·启航计划」

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

版本相关

webpackv5.x 版本发布日期是 2020.10v4.x相对稳定,本文也是基于v4版本。

v4v5配置项相似,学会了v4再去接触v5也能很轻松的过度。

上面安装了webpackwebpack-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.jswebpack是基于nodejs的,所以可以在配置文件中使用nodejs的语法。

const { resolve } = require("path");
module.exports = {
	// 入口
	entry: "./src/index.js",
	// 出口
	output: {
		// 生成资源存放的位置;path 必须是绝对路径,默认是 dist目录
		path: resolve(__dirname, "./build"),
		// 生成的资源叫什么
		filename: "index.js",
	},
};

设置 打包的模式modemode可选值有 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

捕获.PNG

如上图:chunk 存在于 eval 语句中,原材料交给webpack打包之后,都被打包进了这个eval函数中执行,截图中可以看到这个函数只有一行,但是这个文件总共有100多行,其他又都是什么内容?如下图:

捕获.PNG

这些内容可以理解为是webpack的基础模板,称为webpackBootstrap,里面定义了很多基础函数对象等,这是为了保证eval运行时变量函数不会缺失。不管会不会用到webpackBootstrap,它会被打包进来。

原材料中的export等编译后会被替换成 __webpack_exports__ __webpack_require__类似的对象或方法,这些方法就是存在于webpackBootstrap中,这样遇到可能不支持的import export等语法环境的浏览器也可以正常运行。

webpack 的主要能力是做项目构建,可以应用复杂场景,打包公共库只是他的一个基础能力。而rollup的核心能力就是放在打包公共库上面 可以进行tree shaking等优化。两者重心不一样。

分析 chunk

关键概念

  • chunk:代码片段,一个module对应一个chunk
  • chunks: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入口可以理解为:

bundlechuckNamemodulechunks
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文件都引入了,并没有区分开。

捕获.PNG

下面通过为插件添加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

遇到非 jsjson的文件就需要用到 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塞到这个标签中。

捕获.PNG

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 />标签引入。