- 工程化
- 前端工程化
- Webpack的介绍
- Webpack基础
- 简单示例
- 处理CSS样式
- 将样式文件提取到独立文件
- 处理 less/sass 样式
- 处理图片和字体
- 核心内容:
- 具体步骤:
- 示例1:在样式中引用图片
- 示例2:使用字体图标资源
- 下载若干字体图标,并将下载内容中的
iconfont.css放入项目的src/assets/fonts/目录">1、下载若干字体图标,并将下载内容中的iconfont.css放入项目的src/assets/fonts/目录 - 2、在
src/assets/main.css中引用iconfont.css - 3、在
index.html中测试字体图标 - 4、打包后访问页面,查看是否能成功看到字体图标
- 下载若干字体图标,并将下载内容中的
- 示例3:在 js 中 import 图片资源
- 处理 es6 语法
- 处理 html 文件
- 处理在 html 文件中通过
<img />标签引用的图片 - 使用开发服务器(热更新)
- 生成调试文件
- 清理打包文件
- 打包多页面应用
工程化
定义工程化不是工具,而是一套解决问题的思想
主要解决软件开发环节中存在的效率和质量问题
典型的效率和质量问题:
- 为兼容老平台而不得不使用老的开发技术
- 存在许多重复手工操作(比如编译打包、刷新界面看效果、运行测试、上传部署等)
- 团队开发人员的代码风格五花八门
- 前端等后端给接口
- …….等等
诸如以上问题都会造成开发效率、及产品质量的低下,最后导致开发成本的上升
做法
凡是能提高效率、降低成本、保证质量的手段、都属于工程化范畴,比如:
- 通过制定规章制度、操作流程
- 目前最流行的工程化思路:将一切具有重复性的工作都自动化
前端工程化
一切能提升前端的开发、测试、部署效率的方法都属于前端工程化的范畴
| 实施前 | 实施后 |
|---|---|
| 花费较多时间纯手工搭建新项目 | 一条命令即可创建新项目 |
| 为浏览器兼容性考虑而使用老版本 JS 语法 | 尽情享受最新版 JS 的语法特性 |
在 HTML 中使用 <link>、 <script>引用 css、js 文件;没有模块机制、或只有简陋的模块机制,一不小心就会出现冲突 |
使用 js、css 模块化机制,不用担心代码冲突问题 |
| 每次改完代码都要手动刷新页面来查看最新效果 | 改完代码即刻能在多种浏览器上看到最新效果 |
| 不同开发人员编写的代码风格迥异 | 通过代码检查工具来统一代码格式,降低潜在问题、提高代码质量 |
| 代码发布时不做文件合并和压缩等优化 | 根据不同环境进行打包、压缩优化、添加文件版本、并生成协助调试的 SourceMaps |
工具
Webpack
- 自身定位:资源打包器、聚焦于对“资源”的转换处理(将一种文件变成另一种文件,浏览器只认识 html、js,不认识 vue、jsx 等,所以这类文件需要转换成 html、js 文件)
- 使用风格:声明式(以编写 js 对象的形式来描述对某种资源的处理方法)
- 具备优点:使用简洁方便、社区庞大、插件众多
Webpack的介绍
官方对 webpack 的定位是: 模块打包器(module bundler),它将项目中依赖的各种文件视作 “模块” 来进行打包处理。
该打包处理工作主要涉及:
- 创建、删除文件
- 复制、移动文件
- 修改、转换文件(是 webpack 的核心功能)
当然,webpack 也可用于非文件处理性质的任务
webpack 进行文件转换是怎么样的一个过程?

- 读取原文件的内容
- 将读取的内容组织成 js 形式的 “webpack 模块”,并记录模块间的依赖关系
- 对 “webpack 模块” 的内容做进一步加工处理
- 对加工后的内容封装定型,封装后就不再允许修改
- 输出这些内容到目标文件(chunk)
webpack 依靠什么完成这些文件处理工作?
要点:
webpack 默认只认识 js、json 文件,其他格式的文件,一律都需要资源加载器来进行识别和处理


每个文件都可经由一个或多个 loader 和 plugin 的处理
每个 loader 和 plugin 的功能都保持单一,保证它们的可复用性,当要完成复杂处理时可组合使用。
Webpack基础
安装
npm i -g webpack webpack-cli webpack-dev-server
查看
简单示例
核心内容:
1、创建一个空目录、编写一个传统的页面
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>Document</title></head><body><div id="app"></div><script src="./src/util.js"></script><script src="./src/index.js"></script></body></html>
src/util.js
function toUpperCase(str) {
return str ? str.toUpperCase() : ''
}
src/index.js
const el = document.getElementById('app')
el.innerHTML = toUpperCase('hello,world')
2、改造:对 js 代码使用模块化
src/util.js
export function toUpperCase(str) {
return str ? str.toUpperCase() : ''
}
src/index.js
import { toUpperCase } from './util'
const el = document.getElementById('app')
el.innerHTML = toUpperCase('hello,world')
3、改造:让 html 文件引用之后通过 webpack 打包产生的 js 文件
<script src="./dist/bundle.js"></script>
4、编写 webpack 配置文件
webpack.config.js
const path = require('path')
module.exports = {
// 环境模式:开发环境 (webpack 针对不同环境会做相应的优化)
mode: 'development',
// 入口配置
entry: './src/index.js',
// 输出配置
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
}
5、在根目录下执行命令,生成打包结果目录 dist
# 打包命令
webpack build
# 快捷形式
webpack
# 监听模式:可实时监听文件修改,重新打包
webpack --watch
处理CSS样式
核心内容:
- 为页面添加 css 样式
- 安装并配置
css-loader和style-loader,处理 css 文件
具体步骤
1、在之前代码的基础上,向页面中添加一个设置了样式类的元素
src/index.js
import { toUpperCase } from './util'
const el = document.getElementById('app')
el.innerHTML = `
<h1 class="title">
${toUpperCase('hello,world!!!')}
</h1>
`
2、创建样式文件
src/assets/main.css
.title {
color: red;
}
3、将样式文件通过 import 模块语法导入 src/index.js
说明:有别于传统方式,使用 webpack 后一般不直接通过 <link> 引入样式文件,而是将样式文件当成 ”模块“ 导入 js:
import './assets/main.css'
此时打包会出错,因为 webpack 当前不认识 css 文件:

4、安装并配置 css-loader``style-loader
npm i css-loader style-loader -D
webpack.config.js
// ...
module.exports = {
// ...
// 模块的处理配置
module: {
// 模块的匹配规则
rules: [
// 后缀名 为 .css 的文件都通过 css-loader 处理
{
// 文件名匹配规则
test: /\.css$/,
// 使用多个 loader 时使用数组形式(注意 loader 从右向左顺序执行)
//css-loader 读取.css文件的内容
//style-loader 将读取的内容以 style 表签插到 html 标签内
use: ['style-loader','css-loader'],
}
]
}
}
上面这样使用style-loader,当有多个css文件时,无法汇合到一起
将样式文件提取到独立文件
核心内容:
安装并配置 mini-css-extract-plugin 中提供的 loader 和 plugin,将 css 从 js 中分离
具体步骤
1、安装并配置 mini-css-extract-plugin
npm i mini-css-extract-plugin -D
webpack.config.js
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// 模式
mode: 'development',
// 代码入口
entry: './src/index.js',
// 打包结果目录
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
// 针对不同类型模块文件的处理
module: {
rules: [{
// 匹配所有以.css结尾的文件
test: /\.css$/i,
// 多个loader的执行顺序是从后往前的
// css-loader 是读取样式文件内容,把它放到打包后的js文件中
// style-loader 是将读取后的内容以 style 标签的格式插入到 html 标签中
// use: ['style-loader', 'css-loader']
use: [MiniCssExtractPlugin.loader, 'css-loader']
}]
},
plugins: [
// MiniCssExtractPlugin 可以接受参数使css文件名变成hash值,并且css可以自动拆分文件
new MiniCssExtractPlugin({
//文件名字
filename:'[name].[contenthash].css',
//自动拆分的文件名字//自动拆分的过程会生成 id
chunkFilename:'[id].[contenthash].css'
})
]
}
打包后可在 dist下看到独立的 css 文件
2、通过 <link>将 css 文件引入 index.html
<link rel="stylesheet" href="dist/main.css">
处理 less/sass 样式
核心内容:
- 编写 less/sass 文件( 以 less 为例 )
- 安装并配置
less-loader及相关依赖
具体步骤
1、基于之前的代码,为页面添加一些元素
<ul class="my-list">
<li class="list-item">项目1</li>
<li class="list-item">项目2</li>
<li class="list-item">项目3</li>
<li class="list-item">项目4</li>
</ul>
2、编写less文件 src/assets/main2.less
.my-list {
list-style: none;
padding: 0;
.list-item {
border-bottom: 1px solid #ccc;
margin-top: 10px;
}
}
3、在 src/index.js 中导入 less 样式
import './assets/main2.less'
4、安装 less-loader 和 less ,并进行配置
npm i less less-loader -D
在 webpack.config.js 中添加针对 *.less 文件的规则:
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
}
打包后观察到 less 文件中的样式都已合并到 dist/main.css 。
处理图片和字体
核心内容:
- 通过
css-loader处理:css 代码中url()引入的图片或字体 - 通过
webpack内置的资源模块处理:js 代码中import引入的图片或字体
具体步骤:
示例1:在样式中引用图片
1、基于之前配置过 css-loader 的项目,找一些图片放入 src/assets/images/目录
2、为页面添加一个元素,并通过样式设置背景图
<div class="my-img"></div>
.my-img {
width: 200px;
height: 200px;
background: url('./images/pic1.png') center center no-repeat;
background-size: contain;
}
3、执行打包,能看到引用的图片已被放入 dist ,并自动生成了随机文件名

以上功能都是由 css-loader 完成的,它可以处理通过 url()引用的资源。
示例2:使用字体图标资源
1、下载若干字体图标,并将下载内容中的 iconfont.css 放入项目的 src/assets/fonts/目录
2、在 src/assets/main.css 中引用 iconfont.css
/* 在样式文件中引入样式文件可以使用 @import url() 指令 */
@import url('./fonts/iconfont.css');
3、在 index.html中测试字体图标
<!-- 使用字体图标 -->
<span class="iconfont icon-Apple"></span>
4、打包后访问页面,查看是否能成功看到字体图标
由于字体文件也是通过 url()来引用,因此也会经由 css-loader处理。
注意:资源文件被打包时,可能会以不同的形式被处理:
- 文件尺寸较大时:以独立文件形式放在
dist目录中- 文件尺寸较小时:以 base64 字符串形式内嵌到样式文件中
示例3:在 js 中 import 图片资源
1、在 index.html 中添加元素
<img id="img2" src="" width="200" height="200" />
2、在 src/index.js 中导入图片资源,并设置到 img 元素上
// 导入一张图片
import pic2 from './assets/images/pic2.png'
// 将导入的图片设置到 img 标签上
const img2 = document.getElementById('img2')
img2.src = pic2
3、处理 import 导入的资源文件,需使用 webpack 内置的特殊 loader:[Asset Modules](https://webpack.js.org/guides/asset-modules/)
在 webpack.config.js 中添加规则:
// 通过 Asset Modules 处理以下后缀名的图片或字体文件
{
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
type: "asset"
}
打包后能在 dist 中看到处理后的图片。
【Asset Moduls 的说明】
它其实是 webpack 内置的一种专用于处理静态资源的 loader。
type属性可设置为下列值:
asset/resource将资源处理成单独文件,并得到它的 URL 路径(文件较大时候会被处理成单独文件)asset/inline将资源处理成 data URI 字符串(base64位字符串,文件较小的时候会被处理成base64位)asset/source将资源处理成它的原始内容asset根据文件大小,在处理成 data URI 字符串和单独文件之间自动选择(自动选择是base64还是单独文件)
处理 es6 语法
通过 webpack 调用 Babel,实现将使用了 ES6 及更高版本语法的 JS 代码进行降级处理
需要说明的是:webpack 本身并不会对 JS 代码做语法层面的转换处理,即编写时采用什么语法,打包后仍保持该语法。如果想在打包后将高版本语法降级,可在 webpack 中使用 Babel。
核心内容:
- 编写一些使用了 ES6+ 语法的 JS 代码
- 安装并配置
babel-loader及 Babel 相关的依赖包
具体步骤
1、在 src/index.js 中编写 ES6+ 语法的代码
// 一个能返回 Promise 的函数
function asyncTask(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const result = `hello,${name}`
resolve(result)
}, 2000)
})
}
// 通过 async/await 语法执行以上函数
async function start() {
const result = await asyncTask('张三')
console.log(result)
}
// 执行
start()
此时执行打包,不会产生任何错误,只不过打包后的代码还保留着 ES6+ 语法。
2、安装和配置 babel-loader及 babel 依赖包
需要安装的包和说明:
@babel/core- Babel 核心功能包@babel/runtime- 支持async/await的运行时(generator-runtime)@babel/preset-env- 标准预设配置@babel/plugin-transform-runtime- 运行时转换插件
npm i babel-loader @babel/core @babel/preset-env @babel/runtime @babel/plugin-transform-runtime -D
3、然后在 webpack.config.js 中添加规则:
{
// 处理 js、mjs 文件
test: /\.m?js$/i,
// 配置 babel-loader
use: {
// loader 的名称
loader: 'babel-loader',
// loader 的配置
options: {
// babel 的预设配置
presets: ['@babel/preset-env'],
// babel 的插件
plugins: ['@babel/plugin-transform-runtime']
}
},
// 忽略处理那些通过包管理器安装的代码目录,否则会非常缓慢
exclude: /(node_modules|bower_components)/
}
打包后观察目标代码的语法是否已降级,且是否能成功执行该异步任务。
处理 html 文件
让 html 文件也进行打包处理,放到 dist 目录下
目前为止,项目中的 js、css、图片等文件都已通过 webpack 打包,唯独 index.html 还未经处理:
dist目录下并没有将index.html包含进去index.html中还通过写死的<script>标签来引用代码文件
核心内容:
通过 html-webpack-plugin 插件,实现以指定 html 文件为模板,生成一份新的 html 并自动注入对打包后的 js、css 的引用
具体步骤
1、删除 html 文件中对 dist/main.css 和 dist/bundle.js 的引用
2、安装并配置插件: html-webpack-plugin
npm i html-webpack-plugin -D
3、在 webpack.config.js 中添加 plugins 插件配置:
const HtmlWebpackPlugin = require('html-webpack-plugin')
// ...
module.exports = {
// ...
plugins: [
// ...
//,面试题:webpack如何做版本锁定:使用script标签,版本固定
new HtmlWebpackPlugin({
// 页面模板
template: 'index.html'
})
]
}
4、打包后看到 dist 下生成了 index.html ,且已自动注入经过处理的 js 和 css:

处理在 html 文件中通过<img />标签引用的图片
虽然 webpack 处理资源的功能非常强大,但仍存在一个问题:在 HTML 中直接用<img src="..."> 来加载图片,该图片默认将不会被 webpack 处理。
但我们可以借助插件来完成该场景下的图片处理工作。
核心内容:
使用 html-withimg-loader 来解决以上问题
具体步骤
1、在页面中通过 <img> 标签引入一张图片
<img src="./src/assets/images/avatar.jpeg" alt="" width="100" height="100" />
尝试打包后,能观察到在 dist 目录下并不会包含该图片。
2、安装 html-withimg-loader 并配置
npm i html-withimg-loader -D
3、在 webpack.config.js 中添加规则:
{
test: /\.(htm|html)$/i,
loader: 'html-withimg-loader'
}
打包后能看到引用图片已被打包到 dist 中。
使用开发服务器(热更新)
借助 webpack 提供的本地开发服务器,实现修改代码后自动实时预览最新效果的功能
核心内容:
开发服务器模块 webpack-dev-server 的安装和配置
具体步骤
1、安装
如果你的 webpack 是全局安装的,那最好 webpack-dev-server 也全局安装
npm i -g webpack-dev-server
2、配置
// ...
module.exports = {
// ...
// 开发服务器
devServer: {
// 资源目录
static: {
directory: path.join(__dirname, 'dist')
},
// 服务器端口
port: 9000,
}
}
3、启动
webpack serve
或者
webapck serve --open
成功启动后,将自动打开浏览器访问首页。
当修改了项目代码后,页面也会自动更新。(注意:修改 index.html 后并不会自动更新,需手动刷新)
生成调试文件
在打包时生成用于辅助调试代码的 sourcemap 文件
在开发过程中,难免遇到需要调试代码的时候。
浏览器支持读取 Source Map 文件来解析打包后的代码,还原出原始代码的样子,这对调试非常有帮助。
核心内容:
通过 webpack 配置项[devtool](https://webpack.js.org/configuration/devtool/#devtool) 生成 Source Map文件
具体步骤
1、在 webpack.config.js 中添加 devtool 配置
module.exports = {
devtool: 'source-map',
// ...
}
打包后即可在 dist 中找到以 .map 为后缀的 Source Map 文件。
清理打包文件
每当开始新一次打包时,先删除上一次打包所生成的所有文件,防止无用文件残留
目前问题:
默认情况下,每次执行打包时不会删除 dist 中的文件,只会对同名文件进行覆盖。这导致 dist 中可能残留无用的文件,让打包后的代码变得越来越臃肿。
解决方案:
每次打包前先自动删除 dist目录下的所有文件。
核心内容:
安装并配置 clean-webpack-plugin 插件,实现在打包前的清理 dist 目录
具体步骤
1、安装clean-webpack-plugin
npm i clean-webpack-plugin -D
2、配置webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// ...
module.exports = {
// ...
// 插件
plugins: [
// 生成 clean-webpack-plugin 实例
new CleanWebpackPlugin()
]
}
现在执行打包,就会先删除 dist 下的所有文件后再进行打包。
(提示:可在 dist 放一些额外的文件来辅助测试观察)
打包多页面应用
使用 webpack 打包拥有多个 html 入口页面的前端项目
核心内容:
- 编写一个简单的多页应用
- 通过配置多个
entry及HtmlWebpackPlugin实例,完成多 html 项目的打包
具体步骤
1、新建项目并添加三个 html 文件
home.htmlproduct.htmlabout.html
2、新建 src目录并添加三个 js 文件
src/home.jssrc/product.jssrc/about.js
3、新建 webpack 配置文件
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
// 多个入口,即打成三个独立的 js 包
index: './src/home.js',
product: './src/product.js',
introduce: './src/about.js'
},
output: {
path: path.join(__dirname, 'dist'),
// 为最终生成的入口 js 设置不同文件名
filename: '[name].bundle.js'
},
plugins: [
// 生成首页 html
new HtmlWebpackPlugin({
template: 'home.html',
filename: 'home.html',
// 在页面中注入指定 entry 名的 js 文件
chunks: ['home']
}),
// 生成产品页 html
new HtmlWebpackPlugin({
template: 'product.html',
filename: 'product.html',
chunks: ['product']
}),
// 生成介绍页 html
new HtmlWebpackPlugin({
template: 'about.html',
filename: 'about.html',
chunks: ['about']
})
]
}
打包后看到 dist 中生成了三个 html 文件,且都已注入对应的 js 文件。
