pre-commit 阶段代码格式化
- 安装必要的 package,并配置 husky。
npm install --save-dev husky lint-staged prettier eslint
# husky init, https://typicode.github.io/husky/zh/get-started.html
npx husky init
# run "npm lint-staged" before commit
echo "npm lint-staged" > .husky/pre-commitpackage.json中配置 lint-staged
"scripts": {
"lint-staged": "lint-staged"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"**/*.{json,css,vue}": [
"prettier --write"
]
}- eslint 配置文件
eslint.config.cjs
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended",
"plugin:prettier/recommended",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
rules: {
自定义规则
},
};
- 配置 prettier 格式化
配置 .prettierignore,执行 npx prettier --write .
# .prettierrc.yml
semi: false # 是否使用分号
tabWidth: 2 # 缩进宽度
singleQuote: true # 使用单引号
printWidth: 80 # 每行最大字符数
trailingComma: 'none' # 末尾逗号优化 chunk 大小
项目里用了一个 element-plus 的分页组件,导致 chunk 很大,build 时报了 warning:Some chunks are larger than 500 kB after minification,于是来解决一下。
方法一:unplugin-auto-import 插件
.vitepress/config.ts 中配置按需引入。这里对 vue、element-plus、icon 进行按需引入。
import AutoImport from 'unplugin-auto-import/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite'
export default defineConfig({
vite: {
plugins: [
Components({
dts: true,
dirs: ['src/components'],
resolvers: [ElementPlusResolver(), IconsResolver()]
}),
AutoImport({
imports: ['vue', 'vue-router'],
resolvers: [ElementPlusResolver()],
dts: true
}),
Icons({
compiler: 'vue3',
autoInstall: true
})
],
})tsconfig.json 中增加 unplugin-icons、auto-imports、components 等类型支持。其中,后两个为 unplugin 插件自动生成的。
{
"compilerOptions": {
"types": ["vue", "unplugin-icons/types/vue"],
},
"include": [
"auto-imports.d.ts",
"components.d.ts"
]
}配置之后,客户端构建大小,从 3.32MB 降低到 1.79MB,服务端大小从 2.77MB 降低到 1.24MB。
但是这种方法仍然会在构建的时候讲 vue、element-plus 等打包到项目中。如果依赖的 package 过多,那么项目 build 时就会比较慢。
方法二:配置 CDN
.vitepress/config.ts 中配置 head 属性,在其中配置 element-plus 的 CDN。具体参考:安装 | Element Plus,其中 jsDelivr 中 element-plus 页面在 element-plus CDN by jsDelivr - A CDN for npm and GitHub。
export default defineConfig({
head: [
['script', { src: 'https://cdn.jsdelivr.net/npm/element-plus@2.9.3/dist/index.full.min.js' }],
['link', { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/npm/element-plus@2.9.3/dist/index.min.css' }]
],
} 配置之后,客户端构建大小,从 3.32MB 降低到 1.45MB,服务端大小从 2.77MB 降低到 908.41KB。
但是,vitepress 总是先渲染 vue,再获得到 Element-Plus,导致一直无法加载组件。
字体子集化
这个仓库提供了 3500 常用字符(GitHub - shinchanZ/-3500-)。这个仓库提供了字符抽取工具(GitHub - wangkuo/fonttools),再加上之前写的 convert.py 将 ttf 转换为 woff2,就齐活了。这里也有一篇关于获取字体中特定字符的文档,后续可以参考下 【性能优化篇】.ttf字体包过大引起的网页加载过慢-CSDN博客。
最终结果如下,
- MaoKenWangXingYuan.woff2 从 1.1M 降低到 566K
- OPPOSans-Bold/Medium/Regular.woff2 从 4.6M 降低到 514K
- Cloudflare 对首页测试显示,LCP(Largest Contentful Paint) 从 5.55s 降低到 1.70s。Cloudflare 内置测试地址:Cloudflare | Web Performance & Security

后续要做的内容:
- 考虑 build 时获得项目所有字符,生成字符子集。但会增加 build 时间。
- 将上面几个工具脚本整合起来。可以提供常用字符、字符抽取、字体转换功能。
字体 preload
这个源于第一次网页加载的时候,前面字体获取太慢,会显示后面的字体。待前面的字体加载好了之后,会闪烁回之前的字体。这就导致了网页看起来闪了一下,如果两个字体差别过大,那么变化会更明显。
所以就需要在网页加载的时候,字体能快一点获取到,上面的字体子集化就是其中一种方式。但是还是存在闪烁的情况,所以还在优化。preload 就是另一种优化方式,预加载在 CSS 和 JavaScript 中定义的资源。
需要注意的是,preload 的资源不可过多,否则会影响正文页面的渲染。自己期间就是一次渲染了 OPPOSans 三个字重的字体,导致加载时间变长。最后只留下了主页字体和 OPPOSans-regular 字体。另外两个字重配置 prefetch 进行异步加载。
最终效果是,LCP 从不加 preload 的 1.7s 降低到当前的 1.49s。中间是配置过多 preload 资源导致的 LCP 上升。现在通过 private 页面测试,现在有时候不会出现闪烁的情况了。

后续更优化的方案就是根据网页实际字符生成子集。比如 MaoKenWangXingYuan.woff2 字体只用在主页上,其实只有很少的字符数目,字体文件估计可以降低到 10K 内,这时候首页应该就不会出现闪烁了。
另外,vitepress 支持在 markdown 的 frontmatter 中配置 head 属性,从而达到不同页面不同 head 的效果。如此以来,可以针对不同网页选择不同的 preload,而其他字体则配置 prefetch。
构建占用分析
在使用 VitePress 或 Vite 构建时,第一次生成的 stats.html 文件通常是客户端的构建结果,而第二次生成的 stats.html 文件是服务端(SSR)的构建结果。

3.32MB

2.77MB

1.79MB

1.24MB
vitepress 中不再引入 vue() 插件,存在兼容问题。