为什么需要打包工具?
- 编译功能:我们在项目使用的
框架、less、sass、es6等浏览器都不能识别,需要打包工具编译成css js等语法才能运行
- 还可以压缩代码、做兼容性处理、提高代码性能
- 主要的打包工具:
webpack vite rollup等
对webpack的理解?
- 是一个静态资源打包工具,它以一个或多个文件作为打包的入口,将整个项目的文件编译成一个或多个文件输出出去,输出文件bundle就是编译好的文件,浏览器可以运行。本身功能有限制,只能编译js的ES Module语法.
- 可以做什么?
① 编译代码,编译高级的语言或语法(TS、ES6+、sass等);
② 模块整合,在项目中有很多模块和文件,合并之后可以,解决浏览器频繁请求文件的问题;
③ 兼容性和错误检查(Polyfill、postcss、eslint);
④ 代码体积更小,加载更快(Tree-shaking、压缩、合并)
webpack怎么使用
- 安装webpack 初始化package.json
npm init -y
- 启用webpack:
npx webpack ./src/main.js --mode=development,指定Webpack从main.js文件开始打包,并且打包 main.js其依赖也一起打包进来。
- npx是通过检查本地缓存或临时下载的包来执行命令,执行后会清理临时文件。首先它会在当前目录下的
node_modules的.bin文件去查找是否有可执行的命令,如果没有就从全局查找是否有安装对应的模块,全局也没有就会自动下载对应的模块,用完就删。
- 总结:webpack本身功能比较少,只能处理js资源,一旦遇到css等其他资源就会报错。所以学习webpack,就是主要学习如何处理其他资源。
常见的Loader?
css-loader:加载css,支持模块化
style-loader:将css添加到页面的style标签里面
postcss-loader:css的兼容性处理
less-loader:处理less文件
file-loader:把文件输出到文件夹,代码可以通过url去引用文件
url-loader:和file-loader类似,但是能在⽂件很⼩的情况下以base64的⽅式把⽂件内容注⼊到代码中去
babel-loader:转换es6为es5
eslint-loader:通过 ESLint 检查 JavaScript 代码
vue-loader:处理vue文件
常见的Plugin?
DefinePlugin: 定义环境变量,内置插件
clean-webpack-plugin: 清理构建目录
html-webpack-plugin: 打包后自动生成一个html文件,并把打包生成的js自动引入到模块
mini-css-extract-plugin:将CSS提取到单独的⽂件中
css-minimizer-webpack-plugin: 压缩css,代替webpack4的optimize-css-assets-webpack-plugin
terser-webpack-plugin:压缩js,代替webpack4的uglifyjs-webpack-plugin
copy-webpack-plugin:复制静态文件到输出目录
image-minimizer-webpack-plugin:自动压缩图片
基础配置
核心概念
- entry(入口):提示文件从哪里打包
- output(输出):提示文件打包完的文件输出到哪里去,如何命名
- loader(加载器):webpack本身只能处理js资源,需要借助loader处理其他资源
- plugins(插件):扩展webpack功能
- mode(模式):分为开发模式与生产模式;
开发模式与生产模式
- 开发模式主要关注:① 编译代码,使浏览器能识别运行。图片、样式、字体、html资源等,webpack默认都不能处理这些资源,所以需要配置来编译这些资源;②检查代码质量、代码规范,统一团队的编码规范、让代码更加优雅美观。
- 生产模式需要代码将来部署上线,主要关注:优化代码运行性能;优化代码打包速度
处理css资源
开发模式
- 安装插件:
less-loader css-loader style-loader
- less-loader: 将less文件编译成css文件
- css-loader:将css文件编译成webpack可以识别的模块
- style-loader:通过动态创造一个style标签,把css添加到html页面上
- 使用,从右到左执行loader:
rules: [{test: /\.less$/, use: ["style-loader", "css-loader", "less-loader"]} ]
生产模式
- mini-css-extract-plugin:提取css为单独文件。在开发模式css文件被打包到js文件中,当js文件加载时,会创建一个style标签来生成样式。但是在生产模式实际打开有可能会出现闪屏现象,用户体验不好,新建单独的css文件,通过link标签加载性能才好。
- 使用:提取css成单独文件,定义输出文件名和目录:
plugins: [new MiniCssExtractPlugin({filename: "static/css/main.css",})]
- postcss-loader:css兼容性处理,下载插件:
npm i postcss-loader postcss postcss-preset-env -D,然后在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度;
- css-minimizer-webpack-plugin:压缩css,使用
plugins: [new CssMinimizerPlugin()]
module: {
rules: [{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env",
],
},
},
},
"less-loader",
],
]}
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
new MiniCssExtractPlugin({
filename: "static/css/main.css",
}),
new CssMinimizerPlugin(),
],
处理图片资源
- 在 Webpack4 时,我们处理图片资源通过
file-loader url-loader进行处理;
- Webpack5已经将两个Loader功能内置到Webpack里了,只需简单配置即可处理图片资源
rules: [{test:/.[png|jpe?g|gif|webp]$/, type: "asset"},]
type: "asset" 相当于url-loader, 将文件转化成Webpack能识别的资源,同时小于某个大小的资源会处理成data URI形式;
- 将小于10kb的图片转为base64格式, 优点是减少请求数量。
rules: [{test:/\.[png|jpe?g|gif|webp]$/, type: "asset"},perser:{dataUrlCondition:maxSize: 10 *1024}]
处理字体图标、其他资源如音视频
- 阿里巴巴矢量图标库添加字体图标需要下面文件:
src/fonts/iconfont.ttf、src/fonts/iconfont.woff、 src/css/iconfont.css;引入后使用字体图标<i class="iconfont icon-arrow-down"></i>
- 需要在webpack处理
ttf、woff2?结尾的文件,不然浏览器识别不出来。[{test: /\.(ttf|woff2?|map4|map3|avi)$/, type: "asset/resource", generator: {filename: "static/media/[hash:8][ext][query]",}}]
type: "asset/resource":相当于file-loader, 将文件转化成 Webpack 能识别的资源,其他不做处理
开发服务器&自动化
- 安装
npm i webpack-dev-server -D
- 开发服务器配置
devServer: { host: "localhost", port: 3000, open: true}
- 含义:启动服务器域名、服务器端口号、是否自动打开浏览器,当使用开发服务器时,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。
- 运行的指令为
npx webpack serve
处理html资源
- 下载
html-webpack-plugin
- 作用: dist 目录输出一个 index.html 文件会自动引入打包生成的js等资源
- 使用:
plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, "public/index.html"),})],以public/index.html为模板创建文件。
- 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
处理js资源
- Webpack对js处理是有限的,只能编译js中ES 模块化语法不能编译其他语法,导致js不能在IE等浏览器运行,所以需要做一些兼容性处理,先完成
Eslint检测代码格式无误后,再由Babel做代码兼容性处理。
- Eslint:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能;需要新建配置文件
.eslintrc,里面有各种rules,运行Eslint时就会以写的规则对代码进行检查。
- 使用:安装
eslint-webpack-plugin,webpack配置:plugins: [new ESLintWebpackPlugin({ context: path.resolve(__dirname, "src")}) //指定检查文件的根目录
- Babel:主要将es6语法转为向后兼容的js语法,可以让它们在旧版浏览器也能用,需要新建
.babelrc.
- Babel使用:下载插件:
babel-loader @babel/core @babel/preset-env,webpack配置 [{test: /.js$/, exclude: /node_modules/, loader: "babel-loader",}]
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module: {
rules: [
{
test: /\.(jsx|js)$/,
include: path.resolve(__dirname, "../src"),
loader: "babel-loader",
options: {
cacheDirectory: true,
cacheCompression: false,
plugins: [
"@babel/plugin-transform-runtime"
],
},
}]
},
plugins: [
new ESLintWebpackPlugin({
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
})
],
提升打包构建速度-HMR/Cache/Thread
Cache
- Cache是什么?每次打包文件都要经过
Eslint Bebal处理,速度比较慢;可以缓存之前的Eslint检查的结果和Babel编译的结果,这样第二次打包速度就比较快。
- babel: 开启babel编译缓存
{test: /.js$/, loader: "babel-loader", option:{cacheDirectory: true, }}
- Eslint: cache开启缓存,cacheLocation缓存目录。
new ESLintWebpackPlugin({ context: path.resolve(__dirname, "../src"), exclude: "node_modules", cache: true, cacheLocation: path.resolve( __dirname,"../node_modules/.cache/.eslintcache")})
Include/Exclude
- 在开发时候使用的第三方的库或插件,所有文件都下载到 node_modules 中了。而这些文件是不需要编译可以直接使用的。所以我们在对 js 文件处理时,要排除 node_modules 下面的文件。
- include(包含):
include: path.resolve(__dirname, "../src")
- exclude(排除):
exclude: "node_modules"
Thread
- 当项目越来越庞大时,打包速度越来越慢,提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少,而对 js 文件处理主要就是
eslint 、babel、Terser三个工具,主要是提升这三个的速度。开启多进程同时处理 js 文件。
- 多进程打包是开启多个进程同时干一件事,速度更快。仅在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右开销。
- 下载
thread-loader, 获取CPU的核数,启动进程的数量就是CPU的核数:const os = require("os"); const threads = os.cpus().length;
- loader开启多进程:
{test: /\.js$/,use: [{loader: "thread-loader",options: {workers: threads}}, {loader: "babel-loader", options: {cacheDirectory: true}}]}
- eslint开启多进程:
new ESLintWebpackPlugin({cache: true, cacheLocation:...,threads})
- Terser开启多进程:
optimization: {minimize: true, minimizer: [new CssMinimizerPlugin(), new TerserPlugin({parallel: threads})]},生产模式会默认开启TerserPlugin,但是要进行其他配置,就要重新写了
module: {
rules: [
{
test: /\.(jsx|js)$/,
include: path.resolve(__dirname, "../src"),
loader: "babel-loader",
options: {
cacheDirectory: true,
cacheCompression: false,
plugins: [
"@babel/plugin-transform-runtime"
],
},
}]
},
plugins: [
new ESLintWebpackPlugin({
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
threads,
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
],
optimization: {
minimizer: [
new CssMinimizerPlugin(),
new TerserWebpackPlugin({parallel: threads}),
]
}
HotModuleReplacement
- HMR是什么?在开发过程中修改某一块代码,会默认把所有的模块重新打包,速度很慢。HMR可以在修改某个模块的代码时候,只打包编译某个模块,这样速度就会很快。
- 热模块替换就是在程序运行中,添加、删除或替换模块,无需重新加载整个页面
- 怎么用:在
devserver里面设置hot:true就是开启了HMR功能,这个时候css经过style-loader处理就可以做到修改后不加载整个页面了。js哪个文件需要热更新需要额外写上module.hot.accept("./js/count.js")
- 实际开发我们会使用其他loader来解决,比如
vue-loader react-hot-loader
减少代码体积-Babel/图片压缩/code split
Image Minimizer
- 项目如果中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢。所以可以对图片进行压缩,减少图片体积
- 下载
npm i image-minimizer-webpack-plugin imagemin -D;以及压缩各种格式图片的插件
- 无损压缩
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
- 有损压缩
npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
- 配置
optimization: {minimize: true, minimizer: [new CssMinimizerPlugin(), new ImageMinimizerPlugin({minimizer: {...配置压缩各种格式图片的插件}}) ]}
Code Split
- 代码分割是什么?打包代码时会将所有 js 文件打包到一个文件中,体积太大了。所以我们需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样每次加载的资源就少,速度就更快。
- 种类:多入口打包;提取插件代码或重复代码到公共模块;使用import按需加载,动态导入
- 多入口打包:在entry配置了几个入口,输出几个 js 文件。入口文件:
entry: {main: "./src/main.js",app: "./src/app.js"} ;出口文件:output: {path: path.resolve(__dirname, "./dist"), filename: "js/[name].js",}
- 提取插件代码或重复代码到公共模块:
optimization: {splitChunks: {chunks: "all", cacheGroups: { default: {minSize: 0, minChunks: 2,priority: -20,}}}},cacheGroups 修改配置组,配置哪些模块要打包到一个组。
- 使用import按需加载,动态导入:
import(/* webpackChunkName: "math" */ "./js/math.js").then(({ count }) => {...})
Core-js
- 使用babel对js进行兼容性处理,使用
@babel/preset-env智能预设处理兼容性问题,它可以对ES6的扩展运算符、箭头函数进行处理,但是版本更高的promise对象、async函数没办法处理。
- core-js是专门来做es6及以上的
api polyfill,叫做垫片/补丁。就是用社区上提供的一段代码,可以在不兼容某些新特性的浏览器上,使用该新特性。
- 如何使用? 在babel.config.js文件,直接
import "@babel-polyfill"会过大,只使用一部分功能可以按需引用。
module.exports = {
presets: [
[
"@babel/preset-env",
{ useBuiltIns: "usage", corejs: 3 },
],
],
};
Tree Shaking
- Webpack开发模式已经默认开启了这个功能,无需其他配置。
- 开发时引用第三方工具函数库或组件库,如果没有特殊处理的话我们打包时会引入整个库,但是实际上可能我们可能只用上极小部分的功能;
Tree Shaking移除 JavaScript 中的没有使用上的代码。它是基于es6模块静态编译思想,在编译时候确定模块的依赖关系、输入输出的变量。在编译阶段利用ES Module判断哪些模块已经加载,哪些未被使用进而删除对应的代码。
链接
尚硅谷Webpack5入门到原理