Skip to content

code-blocks 低代码项目性能优化

起因:在部署到线上的时候,项目访问速度比较慢(不考虑服务器带宽的问题)

打包构建优化前

需要

  • 路由懒加载
  • 组件库按需导入

体积分析

执行 pnpm build 进行打包

项目打包优化前

提示的警告是:打包后的文件超过大小,可以采用代码切割来解决(单个文件过大,会导致首屏加载速度过慢)

TIP

可以使用 rollup-plugin-visualizer 查看打包后的文件大小

js
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    visualizer({
      emitFile: true, // 生成文件
      open: true,
      filename: 'stats.html', //分析图生成的文件名
      gzipSize: true, // 收集 gzip 大小并将其显示
      brotliSize: true // 收集 brotli 大小并将其显示
    })
  ]
})

代码切割

对于分析的结果,可以采用 代码压缩代码切割 的方式

ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver()]
    }),
    Components({
      resolvers: [ElementPlusResolver()]
    })
  ],
  build: {
    minify: 'terser', // 压缩代码
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return extractPackageName(id) 
          } 
        } 
      } 
    } 
  } 
})

// 从路径中提取包名
function extractPackageName(id: string): string {
  // 检查是否包含 .pnpm
  if (id.includes('.pnpm')) {
    const pnpmMatch = id.match(/\.pnpm\/([^/@]+)/) 
    if (pnpmMatch && pnpmMatch[1]) {
      return pnpmMatch[1] 
    } 
  } 
  // 对于其他包管理工具(如 npm 或 yarn)
  const nodeModulesMatch = id.match(/node_modules\/([^/@]+)/) 
  if (nodeModulesMatch && nodeModulesMatch[1]) {
    return nodeModulesMatch[1] 
  } 
  return 'vendor'
} 

切割后的大小:

代码切割

可以从图中看到,有个highlight.js库还是很大,所以可以将这个库采用 cdn 引入来解决

注意

两次打包时间对比可以看到:通过代码切割,构建时间反而变长了,是因为生成的文件数和是否压缩,都会影响构建时间。我们首先考虑首屏加载优化,其次再考虑构建速度(可以降低优先级)

  • 如何减少构建速度
    • 减少不必要的分割
    • 使用缓存
    • 并行构建

使用 cdn

  1. 使用 rollupOptions.externalhighlight.js 排除构建

这里需要用到 rollup-plugin-external-globals

作用:告诉 Rollup 将 第三方包作为全局变量 xxx 暴露到全局作用域中

ts
import externalGlobals from 'rollup-plugin-external-globals'

export default defineConfig({
  build: {
    minify: 'terser',
    rollupOptions: {
      external: ['highlight.js'], // 告诉 Rollup 不要将 highlight.js 包打包到输出中
      plugins: [ 
        externalGlobals({ 
          // 告诉 Rollup 将 highlight.js 包作为全局变量 hljs 暴露到全局作用域中
          'highlight.js': 'hljs'
        }) 
      ], 
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return extractPackageName(id)
          }
        }
      }
    }
  }
})
  1. cdn引入

使用 vite-plugin-cdn-import 插件

ts
import externalGlobals from 'rollup-plugin-external-globals'
import importCDNPlugin from 'vite-plugin-cdn-import'

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver()]
    }),
    Components({
      resolvers: [ElementPlusResolver()]
    }),
    importCDNPlugin({ 
      modules: [ 
        { 
          name: 'highlight.js', 
          var: 'hljs', 
          path: 'https://cdn.bootcdn.net/ajax/libs/highlight.js/11.10.0/highlight.min.js'
        } 
      ], 
      generateScriptTag(name, url) { // 给script 标签添加 async 属性
        return { 
          attrs: { 
            src: url, 
            async: true
          } 
        } 
      } 
    }) 
  ],
  build: {
    minify: 'terser',
    rollupOptions: {
      external: ['highlight.js'], // 告诉 Rollup 不要将 highlight.js 包打包到输出中
      plugins: [
        externalGlobals({
          // 告诉 Rollup 将 highlight.js 包作为全局变量 hljs 暴露到全局作用域中
          'highlight.js': 'hljs'
        })
      ],
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return extractPackageName(id)
          }
        }
      }
    }
  }
})

虽然打包时间变长了,但是首屏加载变快了,从 4.26s -> 1.98s 速度提升 2.15

优化前

优化后