提取CSS
因为CSS的下载和JS可以并行,当一个HTML文件很大的时候,我们可以把CSS单独提取出来加载
npm install mini-css-extract-plugin —save-dev
const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');+const MiniCssExtractPlugin = require('mini-css-extract-plugin');module.exports = {mode: 'development',devtool: false,entry: './src/index.js',output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js',+ publicPath: '/'},module: {rules: [{ test: /\.txt$/, use: 'raw-loader' },+ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },+ { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },+ { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },{test: /\.(jpg|png|gif|bmp|svg)$/,type:'asset/resource',generator:{filename:'images/[hash][ext]'}}]},plugins: [new HtmlWebpackPlugin({ template: './src/index.html' }),+ new MiniCssExtractPlugin({+ filename: '[name].css'+ })]};
可以在调用MiniCssExtractPlugin的时候指定将css放在哪个目录下:
+ new MiniCssExtractPlugin({+ filename: 'style/[name].css'+ })
图片优化
webpack5以后,对于文件的处理,使用type: ‘asset/resource’,而不再使用url-loader、file-loader
module: {rules: [{test: /\.(jpg|png|gif|bmp|svg)$/,type:'asset/resource',generator:{filename:'images/[hash][ext]'}}
module: {rules: [{test: /\.(jpg|png|gif|bmp|svg)$/,type:'asset', //必定会输出一个文件parser: {//根据这个条件做选择,如果小于maxSize的话就变成base64字符串,如果大于的就拷贝文件并返回新的地址dataUrlCondition: {maxSize: 4 * 1024}},generator:{filename:'images/[hash][ext]'}}
压缩HTML、css、js
js压缩:之前有UglifyJsPlugin等插件,但UglifyJsPlugin不支持ES6,因此现在用TerserPlugin更多
css压缩:OptimizeCssAssetsWebpackPlugin,可能会和webpack5不兼容,因此需要改用CssMinimizerPlugin
HTML压缩:HtmlWebpackPlugin里直接配置
图片不推荐用webpack压缩
const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const MiniCssExtractPlugin = require('mini-css-extract-plugin');+const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');+const TerserPlugin = require('terser-webpack-plugin');module.exports = {+ mode: 'none',devtool: false,entry: './src/index.js',+ optimization: {+ minimize: true,+ minimizer: [+ new TerserPlugin(),+ ],+ },output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js',publicPath: '/',},devServer: {contentBase: path.resolve(__dirname, 'dist'),compress: true,port: 8080,open: true,},module: {rules: [{test: /\.jsx?$/,loader: 'eslint-loader',enforce: 'pre',options: { fix: true },exclude: /node_modules/,},// ...{test: /\.(jpg|png|gif|bmp|svg)$/,type:'asset/resource',generator:{filename:'images/[hash][ext]'}},{test: /\.html$/,loader: 'html-loader',},],},plugins: [new HtmlWebpackPlugin({template: './src/index.html',+ minify: {+ collapseWhitespace: true,+ removeComments: true}}),new MiniCssExtractPlugin({filename: 'css/[name].css',}),+ new OptimizeCssAssetsWebpackPlugin(),],};
clean-webpack-plugin可以每次在执行build之前将原来打包的dist目录清除掉
purgecss-webpack-plugin
- 可以去除未使用的 css,一般与 glob、glob-all 配合使用
- 必须和mini-css-extract-plugin配合使用
- paths路径是绝对路径
npm i purgecss-webpack-plugin mini-css-extract-plugin css-loader glob -D
const path = require("path");+const glob = require("glob");+const PurgecssPlugin = require("purgecss-webpack-plugin");+const MiniCssExtractPlugin = require('mini-css-extract-plugin');const PATHS = {src: path.join(__dirname, 'src')}module.exports = {mode: "development",entry: "./src/index.js",module: {rules: [{test: /\.js/,include: path.resolve(__dirname, "src"),use: [{loader: "babel-loader",options: {presets: ["@babel/preset-env", "@babel/preset-react"],},},],},+ {+ test: /\.css$/,+ include: path.resolve(__dirname, "src"),+ exclude: /node_modules/,+ use: [+ {+ loader: MiniCssExtractPlugin.loader,+ },+ "css-loader",+ ],+ },],},plugins: [+ new MiniCssExtractPlugin({+ filename: "[name].css",+ }),+ new PurgecssPlugin({+ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),+ })],};
CDN
HTML放在自己的服务器上,不缓存,关闭服务器缓存,每次访问服务器都可以获取最新的资源
里面的静态文件js css image都指向CDN的地址
js css image都放在CDN上,并且文件名带上hash值,hash一般是结合CDN缓存来使用,通过webpack构建之后,生成对应文件名自动带上对应的MD5值。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存。
为了可以并行加载,需要把不同类型的文件和不同的文件放在不同的CDN服务器上
为了避免同一时间对同一个域名请求数并发的限制,不同的资源放在不同的域名服务器进行并行加载,但
多个域名后会增加域名解析时间dns-prefetch DNS预解析,可以通过在 HTML HEAD 标签中 加入去预解析域名,以降低域名解析带来的延迟
三种hash值
- hash:代表整个项目,每次webpack构建时生成一个唯一的hash值,即使css没动过,只动了js,打包后这个hash依然会变,同样的,公共代码vendor虽然没有改变,但它的hash也会变
vendor.4305adaee27b2a3244e5.js
main.4305adaee27b2a3244e5.js
style/main.4305adaee27b2a3244e5.css
- chunkhash
每个入口都有自己的chunkhash
如果本入口对应的文件发生改变,chunkhash会改变,如果没有改变,chunkhash会保持不变
但是对于css来说,我们通常都是在js文件中通过import ‘./index.css’这种方式引入的,如果我们改了js文件,css文件的chunkhash也会跟着改,这时就要用到contenthash:
new MiniCssExtractPlugin({filename: 'style/[name].[contenthash].css'}),
treeshaking
webpack4的tree-shaking原理是找import进来的变量在模块中是否出现过
webpack5可以进行各作用域之间的关系进行优化
tree-shaking 必须是esm模块规范才会生效
ModuleConcatenationPlugin
hello.js:
export default 'hello';
entry.js:
import hello from './hello';console.log(hello);
hello模块只导出了一个常量,然后在entry中直接被引入,经过ModuleConcatenationPlugin处理后,hello.js并不会单独作为一个模块放到module中,而是直接将’hello’,放到entry模块里:
(() => {"use strict";var exports = {};/*****./src/entry1.js + 1 modules *****/;// CONCATATION_MODULE: ./src/hello.jsconst hello = ("hello");;// CONCATATION_MODULE: ./src/index.jsconsole.log(hello);})()
babel-loader增加缓存
{test: /\.js$/,exclude: /node_modules/,use: [{loader: 'thread-loader',options: {workers: 3}},{loader: 'babel-loader',options: {//babel编译 后把结果缓存起来,下次编译 的时候可以复用上次的结果cacheDirectory: true}}]},
babel-loader自带缓存,所以可以通过配置参数来实现,但有些loader不带缓存,就需要配置额外的cache-loader来实现
假设babel-loader没有缓存,我们就可以进行如下配置:
npm install cache-loader -D
{test: /\.js$/,exclude: /node_modules/,use: [{loader: 'cache-loader'},{loader: 'babel-loader',options: {//babel编译 后把结果缓存起来,下次编译 的时候可以复用上次的结果cacheDirectory: true}}]},
但在webpack5中,已经不需要这个loader了,直接写在webpack配置里即可:
module.exports = {mode: 'development',devtool: false,entry: './src/index.js',cache: {type: 'filesystem', // memory|filesystemcacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack')}
可以看到我们配置的cache.type是filesystem,即在磁盘持久缓存
这个缓存的配置会缓存生成的webpack模块和chunk,来改善构建速度
webpack5中默认开启cache.type为’memory’的缓存
moduleId和chunkId的优化
module:每一个文件都可以看成是一个module
chunk:webpack打包最终生成的代码块,代码块会生成文件,一个文件对应一个chunk
在webpack5以前,没有从entry打包的chunk文件,都会以1 2 3的文件命名方式输出:
例如,下面这些以import动态加载进来的模块:
index.js:
import('./one')
import('./two')
import('./three')
这样打包后会生成chunk.0.js chunk.1.js chunk.2.js 3个包
如果在某次更改代码时,我们去掉了import(‘./two’)
那会生成chunk.0.js chunk.1.js两个包,而这一次生成的chunk.1.js和上一次的chunk.1.js是不一样的,因此,我们删除掉的import(‘./two’),可能会导致缓存失败
在webpack5中,做了一些优化,这些模块名默认会变成以路径区分:
例如:src_chunks_one_js.main
我们也可以手动配置这个名字的生成规则
这个名字生成的规则是可以在optimize里面配置的:
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
optimization: {
moduleIds: 'named', //
chunkIds: 'named',

其中deterministic在生产环境中用的较多
sideEffect副作用
package.json中可以配置sideEffect的值:
{
"name": "9.optimize",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"sideEffects": [
"*.css"
],
配置值为[“*.css”],代表不干掉css文件
