Skip to content

性能优化-构建与监控 ​

构建优化 ​

1. 打包分析 ​

bash
# Vite
vite build --mode analyze

# Webpack
webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json
# Vite
vite build --mode analyze

# Webpack
webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json

2. Tree Shaking ​

js
// ✅ 支持 tree-shaking
import { debounce } from 'lodash-es'
import { createApp } from 'vue'

// ❌ 不支持 tree-shaking
import _ from 'lodash'
// ✅ 支持 tree-shaking
import { debounce } from 'lodash-es'
import { createApp } from 'vue'

// ❌ 不支持 tree-shaking
import _ from 'lodash'

3. 代码压缩 ​

js
// Vite 默认使用 esbuild(构建)和 terser(生产)
// vite.config.js
export default {
  build: {
    minify: 'esbuild',    // 快速
    // minify: 'terser',  // 更小体积
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            if (id.includes('vue')) return 'vendor-vue'
            if (id.includes('lodash')) return 'vendor-lodash'
            return 'vendor'
          }
        },
      },
    },
  },
}
// Vite 默认使用 esbuild(构建)和 terser(生产)
// vite.config.js
export default {
  build: {
    minify: 'esbuild',    // 快速
    // minify: 'terser',  // 更小体积
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            if (id.includes('vue')) return 'vendor-vue'
            if (id.includes('lodash')) return 'vendor-lodash'
            return 'vendor'
          }
        },
      },
    },
  },
}

4. 图片压缩与转换 ​

js
// vite-plugin-imagemin
import viteImagemin from 'vite-plugin-imagemin'

export default {
  plugins: [
    viteImagemin({
      gifsicle: { optimizationLevel: 7 },
      mozjpeg: { quality: 80 },
      optipng: { optimizationLevel: 7 },
      webp: { quality: 80 },
    }),
  ],
}
// vite-plugin-imagemin
import viteImagemin from 'vite-plugin-imagemin'

export default {
  plugins: [
    viteImagemin({
      gifsicle: { optimizationLevel: 7 },
      mozjpeg: { quality: 80 },
      optipng: { optimizationLevel: 7 },
      webp: { quality: 80 },
    }),
  ],
}

运行时监控 ​

1. Performance Observer ​

js
// 监控 long tasks
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.warn('长任务:', entry.duration, 'ms')
    // 上报到监控系统
    reportMetric('long-task', entry.duration)
  }
})
observer.observe({ entryTypes: ['longtask'] })

// 监控资源加载
const resourceObserver = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 1000) {
      reportMetric('slow-resource', {
        name: entry.name,
        duration: entry.duration,
      })
    }
  }
})
resourceObserver.observe({ entryTypes: ['resource'] })
// 监控 long tasks
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.warn('长任务:', entry.duration, 'ms')
    // 上报到监控系统
    reportMetric('long-task', entry.duration)
  }
})
observer.observe({ entryTypes: ['longtask'] })

// 监控资源加载
const resourceObserver = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 1000) {
      reportMetric('slow-resource', {
        name: entry.name,
        duration: entry.duration,
      })
    }
  }
})
resourceObserver.observe({ entryTypes: ['resource'] })

2. 错误监控 ​

js
// 全局 JS 错误
window.onerror = (message, source, line, col, error) => {
  reportError({ message, source, line, col, error })
}

// 未捕获的 Promise 错误
window.addEventListener('unhandledrejection', (event) => {
  reportError({ reason: event.reason })
})

// 资源加载错误
window.addEventListener('error', (event) => {
  if (event.target.tagName === 'IMG' || event.target.tagName === 'SCRIPT') {
    reportError({ type: 'resource-load', src: event.target.src })
  }
}, true)
// 全局 JS 错误
window.onerror = (message, source, line, col, error) => {
  reportError({ message, source, line, col, error })
}

// 未捕获的 Promise 错误
window.addEventListener('unhandledrejection', (event) => {
  reportError({ reason: event.reason })
})

// 资源加载错误
window.addEventListener('error', (event) => {
  if (event.target.tagName === 'IMG' || event.target.tagName === 'SCRIPT') {
    reportError({ type: 'resource-load', src: event.target.src })
  }
}, true)

3. 性能打点 ​

js
// 手动打点
performance.mark('fetch-start')
await fetchData()
performance.mark('fetch-end')
performance.measure('fetch-duration', 'fetch-start', 'fetch-end')

const measure = performance.getEntriesByName('fetch-duration')[0]
console.log('接口耗时:', measure.duration)

// 清理
performance.clearMarks()
performance.clearMeasures()
// 手动打点
performance.mark('fetch-start')
await fetchData()
performance.mark('fetch-end')
performance.measure('fetch-duration', 'fetch-start', 'fetch-end')

const measure = performance.getEntriesByName('fetch-duration')[0]
console.log('接口耗时:', measure.duration)

// 清理
performance.clearMarks()
performance.clearMeasures()

性能预算 ​

json
{
  "performanceBudget": {
    "bundleSize": {
      "total": "200kb",
      "vendor": "100kb",
      "app": "80kb"
    },
    "lighthouse": {
      "performance": 90,
      "accessibility": 90,
      "bestPractices": 85,
      "seo": 90
    },
    "webVitals": {
      "LCP": 2500,
      "FID": 100,
      "CLS": 0.1
    }
  }
}
{
  "performanceBudget": {
    "bundleSize": {
      "total": "200kb",
      "vendor": "100kb",
      "app": "80kb"
    },
    "lighthouse": {
      "performance": 90,
      "accessibility": 90,
      "bestPractices": 85,
      "seo": 90
    },
    "webVitals": {
      "LCP": 2500,
      "FID": 100,
      "CLS": 0.1
    }
  }
}

常用工具 ​

工具用途
Lighthouse CI自动化性能审计
bundlesize包体积预算检查
webpack-bundle-analyzer打包分析
why-did-you-renderReact 不必要渲染检测
Chrome DevTools实战调试分析
Sentry错误与性能监控