没有打包工具,那么组织管理代码就会变得非常麻烦,并且不利于新语法的使用
webpack看了听了很多次,趁着最近准备面试,再次回忆记录下来
下面是webpack的常规配置和使用
为什么需要Webpack?
在现代前端开发中,我们通常需要处理大量的JavaScript模块、样式文件、图片等资源。手动管理这些资源不仅费时费力,还容易出错。Webpack作为一个强大的静态模块打包工具,可以:
- 将分散的模块打包成少数几个优化过的文件
- 自动处理文件之间的依赖关系
- 转换、优化各种前端资源
- 提供开发服务器和热更新功能,加速开发流程
从最简单的配置开始
先创建一个基本的Webpack配置文件,初始化了一个Node.js项目(pnpm init
)并安装了Webpack(pnpm install webpack webpack-cli -D
)。
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js', // 入口文件,文件树的根节点
output: {
path: path.resolve(__dirname, 'dist'), // 输出文件的目录
// 主JavaScript文件命名模式
// [name]: 入口名称,默认为main
// [contenthash:8]: 基于内容生成的8位hash值,内容改变时hash值改变
// .js: 文件扩展名
filename: '[name].[contenthash:8].js' // 输出的文件的名称
},
mode: 'development' // 开发模式
}
这个配置告诉Webpack从src/index.js
开始构建我们的应用,并将结果输出到dist/xxx.js
。很简单,但实际项目远比这复杂,我们需要处理各种类型的文件,js,css,img等。
配置为hash值的原因是为了避免浏览器缓存问题,hash值会随着文件内容的变化而变化,这样可以确保用户总是加载最新的文件
配置完成之后,就可以在package.json中添加执行脚本:
{
"scripts": {
"build": "webpack",
"dev": "webpack serve --mode development",
"prod": "webpack --mode production"
}
}
执行pnpm dev, 或者直接执行 npx webpack 后能看见输出的内容:
js资源的打包就完成了
让Webpack加载各种资源
在开发的需求里面,最终需要在客户的浏览器上去渲染展示:那么离不开三件套 html css 和 js
为了提高开发效率,往往会使用高版本的语法,使用更适合开发的特定格式文件。对此最终都需要转换为浏览器能够识别的内容,如下:
JavaScript 降级
客户的开发环境各种各样的,如何处理高版本语法就是一个问题
es5往后的js版本,包含很多浏览器尚未完全支持的特性,比如import export。我们可以使用Babel来转换这些代码:
// 安装依赖:pnpm install -D babel-loader @babel/core @babel/preset-env
module.exports = {
// ...
module: {
rules: [
{
test: '/\.js$/', // 匹配所有的 .js 文件
exclude: '/node_modules/', // 排除所有的 node_modules 文件夹,因为它们已经被编译过了
use: {
loader: 'babel-loader',
options: {
presets: ['@babel-preset-env'] // 使用预设转换为ES5代码
}
}
}
]
}
}
这样配置后,你就可以放心使用ES6+的特性了,Webpack会处理兼容性问题,比如将import
转换为require
,将箭头函数转换为普通函数等。
上面是显式的使用了babel去做js的转换,实际上webpack在处理模块时会将ES模块语法(import/export)转换为CommonJS模块语法(require/module.exports),即使没在使用babel-loader。这是webpack的默认行为,它需要兼容node.js 的模块系统
思考点🤔: babel的设计原理是什么?编译原理吧,符合某种的规范的是不是都可以转换为AST抽象语法树。日常语言可以抽象为语法树吗? 答案是:自然语言具有歧义性;语义理解和上下文有关,而AST通常是上下文无关的;隐含信息需要常识或者背景知识来理解;非结构化的自然语言难以用AST表示,而编程语言通常是结构化的
加载样式文件
网页少不了css文件,在webpack中处理css内容也很简单:
// 安装: pnpm install -D css-loader style-loader
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader', // 将CSS注入到DOM中
'css-loader' // 解析CSS文件中的@import和url()
]
}
]
}
};
现在你可以在JavaScript中直接导入CSS文件了:import './styles.css'
,Webpack会负责将这些样式应用到页面上,如果你使用了Sass或Less,只需要安装相应的loader(如sass-loader
或less-loader
)并在配置中添加即可。
在webpack构建过程中,module.rules 数组中的规则顺序非常重要,总的来说,遵循以下规则:
- 从上到下,从左到右
- 对于同一个rule中的use数组,loader的执行顺序是从右到左的
- 对于rules数组中的多个rule,执行顺序的是从下到上(后面的规则先执行)
- 优先级规则:
- enforce: 'pre' 的loader最先执行
- 普通loader(没有enforce属性)其次执行
- enforce: 'post' 的loader最后执行
上面两个css的loader的执行顺序如下:
- css-loader先执行,将CSS转换为包含CSS字符串的JS模块
- style-loader后执行,生成将CSS注入DOM的JavaScript代码
这种设计体现了"单一职责原则",每个loader只负责一种转换,通过组合使用实现完整功能。类似的设计模式在许多前端工具中都能看到,如Babel的插件系统、PostCSS的处理器链等
这种设计体现了 "单一职责原则",每个loader只负责一种功能,通过组合使用实现完整功能,可以简化调试和维护。类似的设计模式在babel的插件系统也能看见,比如babel/preset-env只负责将ES6+转换为ES5,而babel/plugin-transform-runtime则负责处理运行时的polyfill和helper函数
style-loader 的工作原理
style-loader接收css-loader处理后的结果,将CSS样式插入到DOM中:
- 生成注入代码:创建JavaScript代码,用于将CSS内容注入到
<style>
标签中 - 运行时注入:在浏览器运行时,动态创建
<style>
元素并将CSS内容插入到其中 - 添加到DOM:将创建的样式标签添加到HTML文档的
<head>
部分
简化的实现逻辑类似于:
// 简化的style-loader实现原理
const css = require("./style.css"); // 这里获取css-loader的输出
const styleElement = document.createElement("style");
styleElement.innerHTML = css;
document.head.appendChild(styleElement);
图片和静态资源怎么办?
处理图片、字体等资源也很简单,Webpack 5内置了资源模块功能:
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
type: "asset/resource",
generator: {
// 为图片资源单独设置输出路径和文件名
filename: "images/[name].[hash:8][ext]",
},
},
// 为字体文件设置命名模式
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: "asset/resource",
generator: {
filename: "fonts/[name].[hash:8][ext]",
},
},
]
}
}
如果你使用的是 webpack4,可以使用file-loader
和 url-loader
来处理图片
// 安装:pnpm install -D file-loader url-loader
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
limit: 8192, // 小于8k的图片会被转为base64格式的文件
name: '[name].[hash].[ext]', // 输出文件名
outputPath: 'images/' // 输出目录
}
}
]
}
]
}
}
这个配置使你可以直接导入图片和字体:import logo from './logo.png'
webpack图片打包效果,图片模块返回的是图片资源的一个资源地址:
常用好用插件
以下是一些常用的webpack的插件:
自动生成HTML文件:html-webpack-plugin插件
插件作用:
- 自动引入打包后的bundle.js资源
- 支持模板引擎,可以自定义HTML结构
插件用法:
// 安装依赖: pnpm install -D html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
// HTML模板路径
template: './src/index.html',
// 输出文件名
filename: 'index.html',
// HTML标题
title: '我的'
})
]
};
配置完成后,webpack 会自动将打包后的文件注入到HTML中,省去手动修改,它还可以在模版中使用变量,来动态生成模版,比如:
<title><%= htmlWebpackPlugin.options.title %></title>
参考文档: npm:www.npmjs.com/package/htm… webpack:webpack.docschina.org/plugins/htm…
自动清理构建目录:clean-webpack-plugin插件
插件作用:
- 每次构建前自动清理dist目录,避免文件堆积
插件用法:
// 安装依赖: pnpm install -D clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin(), // 清理dist文件夹,告别手动删除的烦恼
]
};
参考文档: npm:www.npmjs.com/package/cle… webpack:webpack.docschina.org/plugins/cle…
开发环境配置:让调试变得轻松
上面是一个资源的打包配置,但是在实际开发中,我们期望看见的是一个完整的页面,而不是静态资源文件。Webpack提供了一个开发服务器,可以让我们在本地快速预览和调试代码web
调试工具如下:
使用webpack-dev-server
插件作用:
- 提供一个开发服务器,自动刷新页面
- 支持热模块替换(HMR),修改代码后无需刷新页面
插件用法:
// 安装依赖: pnpm install -D webpack-dev-server
module.exports = {
mode: 'development',
devtool: 'source-map', // 生成source map方便代码调试
devServer: {
static: {
directory: path.join(__dirname, 'public'), // 静态资源目录
},
hot: true, // 启用热模块替换(HMR),修改代码后无需刷新页面
port: 8080, // 开发服务器端口
open: true // 自动打开浏览器
}
};
现在运行npx webpack serve
,就能启动一个开发服务器,代码变更会即时反映在浏览器中,大大提升开发效率
npx 的作用: npx 是 npm 5.2.0 版本引入的工具 它允许我们执行 node_modules/.bin 目录下的可执行文件,而无需全局安装或直接指定路径 它会优先查找本地安装的包,如果找不到才会临时下载并执行
如图:
至此就可以做简单开发了,并且修改代码后会自动刷新页面,里面看见代码效果
webpack的使用非常简单,牛的,喝口水庆祝一下