Skip to content

前端项目的编译优化处理

下面介绍前端编译优化的处理,让我们的网站打开速度更快。

使用打包分析插件查看打包状态

使用 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 } 开启,让开发时不排除模块。然后把 cdnenableInDevMode: 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 插件查看打包结果,