Appearance
前端项目的编译优化处理
下面介绍前端编译优化的处理,让我们的网站打开速度更快。
使用打包分析插件查看打包状态
使用 vite-bundle-analyzer 插件可以以图形的形式全面了解打包后文件大小,每个包体积都清洗展示。并会显示原包与 gzip 等压缩后的大小对比。
首先安装插件
pnpm add -D vite-bundle-analyzer
然后在 vite.config.ts 配置项里定义插件
import { defineConfig } from 'vite'
import { analyzer } from 'vite-bundle-analyzer'
export default defineConfig({
plugins: [
// ...your plugin
analyzer()
]
})
现在运行打包命令后,会自动打开网页显示模块打包数据
pnpm run build
压缩前文件大小
为了更好对比优化效果,我们来看看使用默认的 vite配置统后,有信息提示我们有文件大于500kb
(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
最终我们看到有文件超过 7MB,这非常大了,网站打开速度会非常慢
下面 vite-bundle-analyzer 插件运行结果,这是默认 vite 打包后的结果,文件会比较大,下面我们一步一步来优化,让网站性能更快。
拆包分包优化加载
使用分包可以将代码拆分成多个文件,比如我们使用的第三方包拆分成独立的文件。
这样当我们使用某个第三方包时,因为这些包一般不会变,所以当我们项目更新了,但是这个模块包的版本没有变,浏览器就会使用缓存文件不再重新加载该模块,提升网页显示性能。
使用 manualChunks 来进行处理,支持对象与函数方式调用。
下面是生成压缩 chunk react-vendor,将'react', 'react-dom'进行打包
manualChunks:{
'react-vendor': ['react', 'react-dom']
}
当选项是函数时,我们可以灵活的自定义生成 chunks。下面是将项目中使用的 node_modules 每个模块单独分包。
...
build: {
rollupOptions: {
output: {
manualChunks(id: string) {
if (id.includes('node_modules')) {
const moduleName = id.split('node_modules/')[2].split('/')[0]
return moduleName
}
}
}
}
}
...
生成的包会在 index.html 中使用 link 标签引入。
使用 gzip 压缩模块包
压缩吗?就是为了减少文件的大小,使用 gzip 压缩是一种常用的手段,使用静态资源下载速度更快。
首先安装扩展包 vite-plugin-compression2
pnpm -D add vite-plugin-compression2
然后在 vite.config.ts 配置文件中定义
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import path from 'path'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import { compression } from 'vite-plugin-compression2'
import cdn from 'vite-plugin-cdn-import'
import createExternal from 'vite-plugin-external';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), TanStackRouterVite(), compression({
algorithm: 'gzip',
//只压缩大于20KB的文件
threshold: 1024 * 20,
})],
...
})
下面是压缩后的文件大小,我们看到文件小了很多,原来的7MB 的文件使用 gzip 后变到了 2MB,效果非常好。
使用 cdn 加速静态资源
有些文件压缩后,比如一个大的富文本编辑器,压缩后文件也不小,像这种文件我们可以使用 cdn来处理,减少我们服务器压缩,优化加载速度。
默认使用 https://cdn.jsdelivr.net/ cdn 网站加载资源,你可以修改模块的 path 为网址,或修改插件的 prodUrl 选项来改变所有模块的 cdn 网站。
vite-plugin-cdn-import
首先我们安装cdn插件 vite-plugin-cdn-import。
pnpm add -D vite-plugin-cdn-import
vite-plugin-externals
因为使用了 cdn 所以我们需要使用 vite-plugin-externals 插件,将使用 cdn 的包排除在外。首先安装插件
pnpm add -D vite-plugin-externals
开发时热更新速度慢
开发时使用 cdn 刷新网页没有使用本地模块愉快,我们可以把viteExternalsPlugin
的 { disableInServe: true }
开启,让开发时不排除模块。然后把 cdn
的enableInDevMode: false
关闭,让开发时不使用 cdn 。
viteExternalsPlugin(
{
......
},
{ disableInServe: true }
),
cdn({
modules: [
......
],
enableInDevMode: false
}),
],
下面是在 vite.config.ts 配置文件中定义的代码
export default defineConfig({
plugins: [
react(),
analyzer(),
TanStackRouterVite(),
compression({
algorithm: 'gzip',
//只压缩大于20KB的文件
threshold: 1024 * 20,
}),
viteExternalsPlugin(
{
react: 'React',
'react-dom': 'ReactDOM',
'cherry-markdown': 'Cherry',
'xgplayer': 'Player',
'lodash': '_',
},
{ disableInServe: true }
),
cdn({
modules: [
{
name: 'react',
var: 'React',
path: `umd/react.production.min.js`,
},
{
name: 'react-dom',
var: 'ReactDOM',
path: `umd/react-dom.production.min.js`,
},
{
name: 'cherry-markdown',
var: 'Cherry',
path: 'dist/cherry-markdown.min.js',
css: ['dist/cherry-markdown.css']
},
{
name: 'xgplayer',
var: 'Player',
path: 'dist/index.min.js',
css: ['dist/index.min.css']
},
{
name: 'lodash',
var: '_',
path: 'lodash.min.js'
},
],
enableInDevMode: false
}),
],
......
})
最终优化后的结果
经过一系列优化,由原来最大的文件是几 MB,现在最大的文件是100KB,优化了几十倍。下面是最终优化后的 vite.config.ts 配置文件
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import path from 'path'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import { compression } from 'vite-plugin-compression2'
import cdn from 'vite-plugin-cdn-import'
import { viteExternalsPlugin } from 'vite-plugin-externals'
import { analyzer } from 'vite-bundle-analyzer'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
analyzer(),
TanStackRouterVite(),
compression({
algorithm: 'gzip',
//只压缩大于20KB的文件
threshold: 1024 * 20,
}),
viteExternalsPlugin(
{
react: 'React',
'react-dom': 'ReactDOM',
'cherry-markdown': 'Cherry',
'xgplayer': 'Player',
'lodash': '_',
},
{ disableInServe: true }
),
cdn({
modules: [
{
name: 'react',
var: 'React',
path: `umd/react.production.min.js`,
},
{
name: 'react-dom',
var: 'ReactDOM',
path: `umd/react-dom.production.min.js`,
},
{
name: 'cherry-markdown',
var: 'Cherry',
path: 'dist/cherry-markdown.min.js',
css: ['dist/cherry-markdown.css']
},
{
name: 'xgplayer',
var: 'Player',
path: 'dist/index.min.js',
css: ['dist/index.min.css']
},
{
name: 'lodash',
var: '_',
path: 'lodash.min.js'
},
],
enableInDevMode: false
}),
],
resolve: {
alias: { '@': path.resolve(__dirname, 'src') },
},
server: {
proxy: {
'/api': {
target: 'http://php.test',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
'/images': {
target: 'http://php.test',
changeOrigin: true,
}
}
},
build: {
rollupOptions: {
output: {
manualChunks(id: string) {
if (id.includes('node_modules')) {
const moduleName = id.split('node_modules/')[2].split('/')[0]
return moduleName
}
}
}
}
}
})
下面是使用 vite-bundle-analyzer 插件查看打包结果,