根据尚硅谷的webpack教程写的学习笔记
webpack
它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源。
webpack 只能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成 浏览器能识别的语法。
1 五个核心
1.1 entry
指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
1.2 output
指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。此处必须用绝对路径。
1.3 loader
Loader让webpack能够去处理非JavaScript文件(webpack自身只理解JavaScript)。loader是从后往前解析。
1.4 plugins
插件可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩, 一直到重新定义环境中的变量等。
1.5 mode
指示 webpack 使用相应模式。development/production。生产环境会自动压缩js环境。
2 开始
2.1 设置打包路径
const { resolve } = require('path'); // node 内置核心模块,用来处理路径问题。
module.exports = {
entry: './src/js/index.js',
output: {
filename: './built.js', // 打包后输出的文件名
path: resolve(__dirname, 'build/js') // 输出文件路径配置
},
mode: 'development'
};
webpack将会从entey指定的文件开始打包,打包后生成的文件路径通过path指定。
__dirname是当前文件的根目录的绝对路径,与build/js拼接后,即是将打包好的js文件放在根目录新创建的build文件夹下的js文件夹下
2.2 打包HTML资源
下载:npm install --save-dev html-webpack-plugin,引入HtmlWebpackPlugin
在配置文件的plugins中添加
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
// 功能:如果需要打包的资源中没有HTML文件,会默认会创建一个空的 HTML,自动引入打包输出的所有资源(JS/CSS)
new HtmlWebpackPlugin({
// 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
template: './src/index.html'
})
],
如果需要打包的资源中存在html文件,通过template引入。最后webpack自动将所有资源引入到该指定的html上。
2.3 加载CSS资源
module: {
rules: [
{
test: /.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
],
},
2.4 加载image资源
{
test: /.css$/i,
use: ['style-loader', { loader: 'css-loader', options: { esModule: false } }],
},// 此处由于webpack5的原因,要解析css中的url,url默认导出es6,此处需要common.js
{
test: /.(png|svg|jpg|jpeg|gif)$/i,
// loader: 'file-loader',
// url-loader依赖于file-loader
loader: 'url-loader',
options: {
esModule: false,]
// 图片大小小于8kb,就会被base64处理
limit: 8*1024
// name: 'img/[name].[hash:6].[ext]',
name: '[name].[hash:6].[ext]',
outputPath: 'img',
},
},
{
// html-loader处理了html后,webpack才能识别其中的img标签,才能处理
test: /.html$/,
loader: 'html-loader',
},
loader如果用file-loader,将会把所有图片单独打包,最后每次都会向服务器发送请求。如果用url-loader(依赖于file-loader),可通过limit来设置哪些图片转为base64的格式。
2.4.1 limit
limit:设置图片处理方式界限。当图片大小小于设置值时,将会被base64的方式进行处理。之后转为base64编码格式,在输出中不再以.jpg/png等形式出现。
优点:减少请求数量(减轻服务器压力),如果是图片格式,则每次都会向服务器发送请求。
缺点:图片体积在变为base64格式后会变大,导致整个文件请求变慢。
2.4.2 esModule
esModule:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs,导致解析时会出问题:[object Module]
解决:关闭url-loader的es6模块化,使用commonjs解析
2.4.3 name
name:给图片进行重命名,并分配打后的地址
[hash:10]取图片的hash的前10位
[ext]取文件原来扩展名
[name]取文件原来名字
打包其他资源
{
// 排除css/js/html资源
test: /.(woff|woff2|eot|ttf|otf)$/i,
loader: 'file-loader',
options: {
esModule: false,
},
},
2.5 加载其他资源
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'media/[hash:6].[ext]',
},
},
2.6 不处理node_modules下的文件
在打包进行某些操作时,可能不会处理node_modules下的各类文件,只检查某些文件夹下的文件,所以在处理时应该排除,例如,在处理js文件时,不处理node_modules下的js文件(没必要,而且费时)。
{
test:/.css$/,
exclude:/node_modules/,
include:resolve(__dirname,'src'),
loader:'eslint-loader'
}
2.7 资源模块
在 webpack 5 之前,通常使用:
- raw-loader将文件导入为字符串
- url-loader将文件作为 data URI 内联到 bundle 中
- file-loader将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource发送一个单独的文件并导出 URL。之前通过使用file-loader实现。asset/inline导出一个资源的 data URI。之前通过使用url-loader实现。asset/source导出资源的源代码。之前通过使用raw-loader实现。asset在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader,并且配置资源体积限制实现。
通过资源模块加载资源的方式为:
{
test: /.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
自定义打包后的名字以及储存位置:
{
test: /.html/,
type: 'asset/resource',
generator: {
filename: 'static/[hash][ext][query]'
}
}
3 devServer
module.exports={
devServer: {
static: './build',
port: 3000, //端口号
compress: true, //启动压缩
},
}
开发服务器 devServer:用来自动化,实现热更新(自动编译,自动打开浏览器,自动刷新浏览器~~)
注意:1.只会在内存中编译打包,不会有任何输出
2.是与mode、entry等同级书写的。
启动devServer指令为:npx webpack server
4 单独提取CSS文件
通过npm i mini-css-extract-plugin -D下载该插件,用于单独提取css文件
const miniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
rules: [
{
test: /.css$/,
use: [
// 创建style标签,将样式放入
// 'style-loader',
// 代替style-loader,提取js中的css成单独文件
miniCssExtractPlugin.loader,
// 将css文件整合到js文件中
'css-loader',
],
},
],
},
plugins: [
new miniCssExtractPlugin({ filename: 'css/build.css' }),
],
1.引入插件
2.用miniCssExtractPlugin.loader代替style-loader
3.添加插件,其中参数中filename为打包后放入的地址以及名字,默认为打包后的根目录下。
注意:通过style-loader最后打包后的样式是以style属性添加到元素上的,而通过miniCssExtractPlugin最后打包的css文件是通过link标签添加的。
5 CSS兼容性处理
1.通过npm i postcss-preset-env postcss-loader postcss -D下载postcss和postcss-loader和postcss-preset-env
2.在css-loader后面添加postcss-loader,并如下配置
// 此处需要设置环境变量,否则postcss默认是走的production模式。
process.env.NODE_ENV = 'development'
module: {
rules: [
{
test: /.css$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
],
],
},
},
},
],
},
],
},
编译后可通过package.json中的browserslist添加前缀,例如
"browserslist":{
"development":[
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production":[
">0.2%",
"not dead",
"not op_mini all"
]
}
6 压缩CSS
1.npm i css-minimizer-webpack-plugin -D下载该插件
2.引入插件并如下配置
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// optimization始于entry、ouput等平级的
optimization: {
minimizer: [
new CssMinimizerPlugin(),
],
// 默认是在生产环境下运行,在开发环境下启用 CSS 优化要如下配置
minimize: true,
},
plugins: [new MiniCssExtractPlugin()],
7 JS兼容性处理(babel)
将一些浏览器不能识别的语法转换为可以识别的语法。
1.下载@babel/core和@babel/preset-env(预设有处理ES6和TS语法的处理方法)
npm install -D babel-loader @babel/core @babel/preset-env webpack
2.添加如下配置
test: /.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
转为代码时依赖于package.json中的browserlist
7 Webpack性能优化
7.1 开发环境下
7.1.1 HMR(热模块替换)
在开发环境下使用devServe时,如果改变某一个模块,此时整个文件都将重新打包。
作用:开启HMR后(需要重启devServe),一个模块发生变化,只会重新打包这一个模块。
devServer: {
static: './build',
port: 3000,
// 开启HMR
hot: true,
},
-
样式文件:可以使用HMR,通过style-loader实现
-
js文件:默认不可以使用HMR。
解决无法使用HMR的方法:修改js,使其支持HMR
-
html文件:默认不能使用HMR。
注意:开启了HMR后还会导致问题,html无法进行热更新了。
解决html无法更新的方法:修改entry入口,将html文件引入(需要重启devServe)。
entry: ['./src/js/index.js', './src/index.html'],
7.1.2 sourceMap(devtool)
提供源代码到构建后代码的映射技术。
作用:如果构建后的代码发生错误,通过映射可追踪到源代码
常用选项:
- source-map:通过外部(生成一个映射文件),告诉错误代码的信息以及源代码的错误位置
- inline-source-map:通过内联(不会生成文件),告诉错误代码的信息以及源代码的错误位置
// devtool与entry、output等同级书写
devtool: 'source-map',
可以使用以下代码将配置项 devtool: inline-source-map 替换为等效的自定义插件配置:
module.exports = {
// ...
devtool: false,
plugins: [new webpack.SourceMapDevToolPlugin({})],
};
7.2 生产环境下
7.2.1 oneof
当规则匹配时,只使用第一个匹配规则。优化打包速度。
module: {
rules: [
{
test: /\.css$/,
oneOf: [
{
resourceQuery: /inline/, // foo.css?inline
use: 'url-loader',
},
{
resourceQuery: /external/, // foo.css?external
use: 'file-loader',
},
],
},
],
},
7.2.2 缓存
1.babel缓存:
当更改项目其中一个js文件时,整个js文件都将重新打包。可采用HMR热模块替换的方法只重新构建改变的文件,但在生产环境下无法使用,此时可用babel缓存来代替HMR的功能。
配置如下:
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
cacheDirectory: true,
},
},
2.资源缓存:
客户端在获取服务端资源时是比较耗费时间的,这就是为什么浏览器使用一种名为缓存技术。可以通过命中缓存,以降低网络流量,使网站加载速度更快,然而,如果我们在部署新版本时不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。由于缓存的存在,当你需要获取新的代码时,就会显得很棘手。
解决方法:[contenthash]将根据资源内容创建出唯一 hash。当资源内容发生变化时,[contenthash] 也会发生变化。将打包的文件命名为:
filename: '[name].[contenthash].js',
当文件改变时,不同文件一定创建不同hash值。
如果用[hash],css和js的hash值相同,因为js中引入了css。
区别:
| [fullhash] ([hash]以弃用) | 完整的 hash 值 |
|---|---|
| [chunkhash] | 此 chunk 的 hash 值,包含该 chunk 的所有元素 |
| [contenthash] | 此 chunk 的 hash 值,只包括该内容类型的元素(受 optimization.realContentHash 影响) |
hash:是跟整个项目的构建相关,构建生成的文件hash值都是一样的,所以hash计算是跟整个项目的构建相关,同一次构建过程中生成的hash都是一样的,只要项目里有文件更改,整个项目构建的hash值都会更改。如果出口是hash,那么一旦针对项目中任何一个文件的修改,都会构建整个项目,重新获取hash值,缓存的目的将失效。 chunkhash:根据不同的入口文件(Entry,即不同的入口js文件生成不同的块) 进行依赖文件解析、构建对应的chunk,生成对应的哈希值。在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响
contenthash:是针对文件内容级别的,模块的内容变了,那么hash值才改变
7.2.3 treeShaking
tree shaking是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码。它依赖于1. ES2015 模块语法的静态特性特性,例如import、export。2.开启生成模式,之后打包会进行treeShaking。
7.2.4 代码分割
方式一:多入口
以上代码都会将所有js文件打包到同一个文件下。通过以下方式可将打包后的js代码分割,单独存放。
entry: {
main: './src/index.js',
text: './src/text.js',
},
output: {
filename: 'js/[chunkhash].js',
path: resolve(__dirname, 'build'),
},
注意:此时输出不能写为一个文件,如js/build.js。通过hash的方式动态生成文件名。
方式二;optimization.splitChunks
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'all',
},
};
作用:1.可以将node_modeules中代码(外部引入的文件,如jquery)单独打包一个chunk输出。
2.自动分析多入口时是否有公共文件。不会重复打包。
方式三:通过js代码
如果不采用多入口的方式,可采用通过修改js代码的方式
在index.js引入其他js代码时,采用以下方式
test.js文件如下:
export function say() {
console.log('test文件被加载了')
}
index.js文件如下;
import(/*webpackChunkName:'test'*/ './text.js') // 此方式返回一个promise对象,此处的注释/*webpackChunkName:'text'*/可为文件命名
.then(({say}) => {
say()
})
.catch(() => {
console.log('文件加载失败')
})
import()是一个函数,返回的是一个Promise对象
7.2.5 预加载和懒加载
当以上述方式打包时,当打开html文件时,所有文件都会被加载,如果想要某些文件只在特定的情况下被加载,就需要用到预加载和懒加载。
预加载:可能用到的文件,提前下载
懒加载:文件在被用到的时候,才会加载
懒加载方式如下:
// index文件如下:
console.log('index文件被加载')
document.getElementById('btn').onclick = function () {
import('./test').then(({ say }) => {
say()
})
}
预加载方式如下:
// index文件如下:
console.log('index文件被加载')
document.getElementById('btn').onclick = function () {
import(/* webpackPrefetch: true */ './test').then(({ say }) => { // /* webpackPrefetch: true */表示文件预加载
say()
})
}
预加载与正常加载都是提前加载,但是存在一定的区别:
普通的正常加载是多个文件并行加载,而预加载是在主要文件加载完成后,才会去加载预加载文件。
7.2.6 PWA(渐进式网络应用程序)
提供离线访问技术。
1.npm install workbox-webpack-plugin --save-dev添加 workbox-webpack-plugin 插件。
2.添加该插件,并配置两个选项。生成Service Worker文件。
plugins: [
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
3.在index.js中注册Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/service-worker.js')
.then((registration) => {
console.log('注册成功,SW registered: ', registration)
})
.catch((registrationError) => {
console.log('注册失败,SW registration failed: ', registrationError)
})
})
}
4.注意Service Worker只能在服务器上运行。此处可开启一个便捷服务器。
npm i serve -g通过serve -s build开启服务器。
以上步骤完成后,当客户端离线时,需要的数据将会从Service Worker中获取。
7.2.7 多进程打包
多进程加载loader
1.npm install --save-dev thread-loader下载包
2.配置如下:
module.exports = {
module: {
rules: [
{
test: /.js$/,
include: path.resolve('src'),
use: [
"thread-loader",
// 耗时的 loader (例如 babel-loader)
],
},
],
},
};
注意:1.使用时,需将此 loader 放置在其他 loader 之前。
2.进程启动需要600ms,所以在耗时的情况下再用此loader。
7.2.8 externals
防止将某些引入的外部包(jQuery、Bootstrap等)打包到 bundle 中,而是在运行时再去从外部获取这些扩展依赖。
// 防止打包外部引入的jquery
module.exports = {
//...
externals: {
jquery: 'jQuery',
},
};
\