主题
静态资源优化
字体
体积优化
字体包往往包含多个字符,但项目中只需要用到部分字体,通过插件取出需要的字体
安装字蛛
shell
npm i -g font-spider-plus
html
<div>
哈哈
</div>
<style>
@font-face {
font-family: 'font';
src: url('./fonts/TencentSans-W7.ttf') format('truetype');
}
div {
font-family:font;
}
</style>
执行生成
js
fsp local index.html
加载时优化
在字体加载时,字是不会被显示出来的,带来糟糕的用户体验
- 在加载前使用默认字体显示
css
@font-face {
font-display: swap;
}
- 使用 base64 内连
js
@font-face {
font-family: 'TencentSansW7';
src: url('data:application/x-font-woff;charset=utf-8;base64,d09G…') format('woff2');
}
图片
SVG
使用 SVGO 压缩 svg
shell
brew install svgo
svgo *.svg
webp
减少图片体积
- 使用智图压缩 png 图片,并生成 webp 文件‘
- 使用
imagemin-webp-webpack-plugin
将图片压缩为 webp
js
const ImageminWebpWebpackPlugin= require("imagemin-webp-webpack-plugin");
module.exports = {
plugins: [new ImageminWebpWebpackPlugin(
{
config: [{
test: /\.(jpe?g|png)/,
options: {
quality: 75
}
}],
overrideExtension: true, // 覆盖原文件
detailedLogs: false, // 日志
silent: false, // 日志
strict: true // 严格匹配
}
)]
};
判断 webp 是否可用
less
.useWepb(@url) {
background-image: url(@url);
.webp & {
background-image: url('@{url}.webp');
}
}
// 使用时
.header{
.useWepb('../image/header.jpg');
}
gif
使用 video 代替 gif,可以减少资源体积
- 使用 ffmpeg 将 gif 压缩成 mp4、webm
shell
ffmpeg -i my-animation.gif -b:v 0 -crf 25 -f mp4 -vcodec libx264 -pix_fmt yuv420p my-animation.mp4
shell
ffmpeg -i my-animation.gif -c vp9 -b:v 0 -crf 41 my-animation.webm
- 使用 video 标签代替,自动播放、循环、静音、内嵌
html
<video autoplay loop muted playsinline>
<source src="my-animation.webm" type="video/webm">
<source src="my-animation.mp4" type="video/mp4">
</video>
CSS
谨慎使用 @import
,会导致 css 串行加载
JS
Tree Shaking
对于无副作用,且导出了但没有使用的,会被去除,不引入打包文件中
副作用:导入时会执行代码,无不只是 export 模块
js
// demo.js
export function a() {
return 'a'
}
export function b() {
return 'b'
}
// main.js
import { a } from "demo.js"
// b 不会被打包
在 生产环境 会自动开启
@Babel/plugin-transform-runtime
以下代码会被编译成
js
class Person{}
js
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left);
}
else {
return left instanceof right;
}
}
function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); }
}
var Person = function Person() {
_classCallCheck(this, Person);
};
可以看到 _instanceof
和 _classCallCheck
都是 Babel 内置的 helpers 函数。如果每个 class 编译结果都在代码中植入这些 helpers 具体内容,对产出代码体积就会有明显恶化影响
启用插件后
json
{
"plugins": [
"@babel/plugin-transform-runtime",
]
}
js
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var Person = function Person() {
(0, _classCallCheck2.default)(this, Person);
};
公用函数会被单独打包到一个模块里
babel 按需引入
json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage", // 按需引入
},
]
],
}
代码切割
http2 普及后,更倾向于多文件,单个文件更小,提高缓存命中率,减少总体积
- 对于多 entry 的应用可以再抽取出公共的方法,通过 minChunks 来设定,比如超过两个引用就抽取到 commons 中。
- initial 的模块按照项目实际情况手动划分为几个部分(组件库,框架依赖React,Vue等),因为考虑到他们更新频率比较低,最大化缓存命中率
- async 的模块设定一个合适的 minSize 即可,使得文件大小和个数最优
js
splitChunks: {
cacheGroups: {
lib: {
// 项目基本框架
chunks: 'initial',
test: /(react|react-dom|react-dom-router)/,
name: 'lib',
},
components: {
// 组件库
chunks: 'initial',
test: /(antd)/,
name: 'components',
reuseExistingChunk: true,
},
commons: {
// 其他同步加载公共包
chunks: 'initial',
minChunks: 2,
name: 'commons',
},
'async-commons': {
// 异步加载公共包、组件等
chunks: 'async',
minSize: 30000,
reuseExistingChunk: true,
},
},
}