Skip to main content

Webpack 初探(一)

创建 Demo(使用 lerna)#

这里使用 lerna 创建一个 Monorepo,不了解 Monorepolerna 的同学可以看一下这篇文章:大型前端项目管理 - Monorepo

执行以下命令:

yarn global add lerna
mkdir demo && cd demo
lerna init

得到一个 Monorepo 的项目,目录结构是这样的:

.
├── lerna.json
├── package.json
└── packages

然后在 packages 目录下创建一个 basic 文件夹,意味着这是一个基础 Demo。

packages/basic 目录下创建三个文件:index.htmlindex.jsfunc.js

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>basic webpack demo</title>
</head>
<body>
<div id="app"></div>
<script src="./bundle.js"></script>
</body>
</html>
func.js
function show(content) {
document.querySelector('#app').innerHTML = `Hello, ${content}!`;
}
module.exports = show;
index.js
const show = require('./func');
show('Webpack');

这个时候还没有 bundle.js 这个文件,所以这个时候网页还不能正常显示内容。

稍后需要使用 Webpackindex.jsfunc.js 打包,生成最终需要的 bundle.js

执行以下命令:

cd packages/basic
yarn init
yarn add webpack webpack-cli --dev

初始化 basic,并安装好 Webpack,接下来需要对 Webpack 进行配置并打包生成最终需要的 bundle.js

basic 中创建 Webpack 的配置文件 webpack.config.js

webpack.config.js
const path = require('path');
module.exports = {
// 开发者工具 不需要开发调试
devtool: false,
// 开发模式 不进行代码压缩
mode: 'development',
// 入口文件
entry: './index.js',
output: {
// 输出文件名称
filename: 'bundle.js',
// 输出文件路径
path: path.join(__dirname, './'),
},
};

然后打开 package.json,添加一条 npm scripts

package.json
{
"name": "basic",
"version": "1.0.0",
"main": "index.js",
"repository": "https://github.com/wjq990112/Learning-Webpack",
"author": "wjq990112",
"license": "MIT",
"scripts": {
"build": "webpack"
},
"devDependencies": {
"webpack": "^5.19.0",
"webpack-cli": "^4.4.0"
}
}

执行以下命令:

yarn build

使用 Webpackindex.jsfunc.js 进行打包。

打包完成后 basic 中就会多生成一个 bundle.js。这个时候再使用浏览器打开 index.html,这个时候网页就显示出 Hello, Webpack!

美化 Demo(使用 loader)#

基本的 Demo 已经创建好了,接下来对这个网页做一下美化,让 Hello, Webpack! 这段文字水平居中。

Webpack 拥有 loader 机制,可以使用 loader 将非 JavaScript 的文件转换为可以在 JavaScript 使用的代码。

basic 创建一个样式文件 index.css

index.css
#app {
text-align: center;
}

index.js 中引入:

index.js
require('./index.css');
const show = require('./func');
show('Webpack');

这个时候执行 yarn build 会报错,因为 Webpack 原生不支持 CSS,需要使用对应的 loaderCSS 做转换。

执行以下脚本:

yarn add style-loader css-loader --dev

然后修改 webpack.config.js

webpack.config.js
const path = require('path');
module.exports = {
// 开发者工具 不需要开发调试
devtool: false,
// 开发模式 不进行代码压缩
mode: 'development',
// 入口文件
entry: './index.js',
output: {
// 输出文件名称
filename: 'bundle.js',
// 输出文件路径
path: path.join(__dirname, './'),
},
module: {
rules: [
{
// 正则匹配后缀名为 .css 的文件
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
};

这里通过正则匹配了所有后缀名为 .css 的文件,将 CSS 文件交给 css-loaderstyle-loader 进行处理。

  • use 的执行顺序是从后向前执行
  • loader 支持传入参数,有两种方式:
    1. UrlSearchParams
    2. Options Object

所以这里的应该是先将 .css 结尾的文件交由 css-loader 进行处理,再交由 style-loader 将其注入到 bundle.js 中,通过 DOM 操作在 <head></head> 标签中注入样式。

执行以下命令:

yarn build

现在进行打包就不会报错了,打开 bundle.js 会发现其中有一段非常长的处理样式的代码,将样式代码以 <style></style> 标签的形式注入到了 <head></head> 标签中。

回到页面,Hello, Webpack! 实现了水平居中。打开控制台,可以看到 <head></head> 标签中多了一个 <style></style> 标签,其中的内容就是 index.css 当中的内容。

核心代码:

function insertStyleElement(options) {
var style = document.createElement('style');
var attributes = options.attributes || {};
if (typeof attributes.nonce === 'undefined') {
var nonce = true ? __webpack_require__.nc : 0;
if (nonce) {
attributes.nonce = nonce;
}
}
Object.keys(attributes).forEach(function (key) {
style.setAttribute(key, attributes[key]);
});
if (typeof options.insert === 'function') {
options.insert(style);
} else {
var target = getTarget(options.insert || 'head');
if (!target) {
throw new Error(
"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.",
);
}
target.appendChild(style);
}
return style;
}

但是使用这样的方式将样式代码注入到 HTML 中有两个问题:

  1. 增加了 DOM 操作
  2. bundle.js 代码体积增大

既然有这样的问题,能不能将 CSS 文件单独抽取出来,不将其打包至 bundle.js 中呢?

优化 Demo(使用 plugin)#

这个时候就需要对 bundle.js 做一些优化,将 CSS 文件与 bundle.js 分离,然后可以单独引入 CSS 样式文件。

Webpack 拥有 plugin 机制,可以使用 plugin 在打包过程中的某个阶段对代码进行处理。

要分离样式文件,就需要使用到一个 pluginMiniCssExtractPlugin

执行以下命令:

yarn add mini-css-extract-plugin --dev

安装 MiniCssExtractPlugin,然后再对 webpack.config.js 做一些修改:

webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// 开发者工具 不需要开发调试
devtool: false,
// 开发模式 不进行代码压缩
mode: 'development',
// 入口文件
entry: './index.js',
output: {
// 输出文件名称
filename: 'bundle.js',
// 输出文件路径
path: path.join(__dirname, './'),
},
module: {
rules: [
{
// 正则匹配后缀名为 .css 的文件
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
plugins: [new MiniCssExtractPlugin()],
};

再执行 yarn build,会发现在当前目录下多了一个 main.css

回到 index.html 中,将分离出来的 main.css 单独引入:

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>basic webpack demo</title>
<link rel="stylesheet" href="./main.css" />
</head>
<body>
<div id="app"></div>
<script src="./bundle.js"></script>
</body>
</html>

再回到页面中,Hello, Webpack! 也实现了水平居中。打开控制台,可以看到样式 main.css 被正确引入了。

Last updated on by wjq990112