提取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.js
const hello = ("hello");
;// CONCATATION_MODULE: ./src/index.js
console.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|filesystem
cacheDirectory: 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文件