11.1 Vue SSR生态系统概览

11.1.1 核心工具链

// Vue SSR生态系统工具链
const vueSSREcosystem = {
  // 核心框架
  core: {
    vue: '^2.6.14',
    'vue-server-renderer': '^2.6.14',
    'vue-router': '^3.5.4',
    vuex: '^3.6.2'
  },
  
  // 构建工具
  build: {
    webpack: '^4.46.0',
    'vue-loader': '^15.9.8',
    'vue-template-compiler': '^2.6.14',
    'webpack-merge': '^5.8.0',
    'webpack-node-externals': '^3.0.0'
  },
  
  // 开发工具
  development: {
    'webpack-dev-server': '^4.7.4',
    'webpack-hot-middleware': '^2.25.1',
    'friendly-errors-webpack-plugin': '^1.7.0',
    'vue-devtools': 'latest'
  },
  
  // UI框架
  ui: {
    'element-ui': '^2.15.6',
    vuetify: '^2.6.1',
    'ant-design-vue': '^1.7.8',
    'bootstrap-vue': '^2.21.2'
  },
  
  // 工具库
  utilities: {
    axios: '^0.26.1',
    lodash: '^4.17.21',
    moment: '^2.29.1',
    'vue-meta': '^2.4.0',
    'vue-i18n': '^8.27.0'
  },
  
  // 测试工具
  testing: {
    jest: '^27.5.1',
    '@vue/test-utils': '^1.3.0',
    'vue-jest': '^3.0.7',
    supertest: '^6.2.2'
  },
  
  // 部署工具
  deployment: {
    pm2: '^5.1.2',
    docker: 'latest',
    nginx: 'latest',
    'serve-static': '^1.14.2'
  }
}

11.1.2 框架对比分析

// SSR框架对比
const ssrFrameworks = {
  nuxt: {
    description: 'Vue.js的通用应用框架',
    pros: [
      '零配置开始',
      '自动路由生成',
      '内置模块系统',
      '强大的插件生态',
      '优秀的开发体验'
    ],
    cons: [
      '学习曲线',
      '框架约束较多',
      '定制化程度有限'
    ],
    useCase: '快速开发、标准化项目'
  },
  
  vuePress: {
    description: 'Vue驱动的静态网站生成器',
    pros: [
      '专为文档优化',
      '优秀的SEO',
      '插件丰富',
      '主题系统'
    ],
    cons: [
      '主要用于静态内容',
      '动态功能有限'
    ],
    useCase: '文档网站、博客'
  },
  
  quasar: {
    description: '基于Vue的跨平台框架',
    pros: [
      '一套代码多平台',
      '丰富的组件库',
      '性能优秀',
      'CLI工具强大'
    ],
    cons: [
      '学习成本高',
      '生态相对较小'
    ],
    useCase: '跨平台应用开发'
  },
  
  customSSR: {
    description: '自定义Vue SSR解决方案',
    pros: [
      '完全控制',
      '高度定制',
      '性能优化空间大',
      '技术栈灵活'
    ],
    cons: [
      '开发成本高',
      '维护复杂',
      '需要深度技术知识'
    ],
    useCase: '复杂业务需求、性能要求极高'
  }
}

11.2 Nuxt.js深入解析

11.2.1 Nuxt.js项目结构

// nuxt.config.js
export default {
  // 全局页面头部
  head: {
    title: 'Vue SSR电商平台',
    htmlAttrs: {
      lang: 'zh-CN'
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'Vue SSR电商平台演示' },
      { name: 'format-detection', content: 'telephone=no' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },

  // 全局CSS
  css: [
    'element-ui/lib/theme-chalk/index.css',
    '@/assets/css/main.scss'
  ],

  // 插件
  plugins: [
    '@/plugins/element-ui.js',
    '@/plugins/axios.js',
    { src: '@/plugins/localStorage.js', mode: 'client' }
  ],

  // 自动导入组件
  components: true,

  // 开发工具模块
  buildModules: [
    '@nuxtjs/eslint-module',
    '@nuxtjs/stylelint-module'
  ],

  // 模块
  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/pwa',
    '@nuxtjs/auth-next',
    '@nuxtjs/i18n'
  ],

  // Axios模块配置
  axios: {
    baseURL: process.env.API_BASE_URL || 'http://localhost:3001/api'
  },

  // PWA模块配置
  pwa: {
    manifest: {
      lang: 'zh-CN',
      name: 'Vue SSR电商平台',
      short_name: 'SSR商城',
      description: 'Vue SSR电商平台演示应用'
    },
    workbox: {
      runtimeCaching: [
        {
          urlPattern: '/api/.*',
          handler: 'NetworkFirst',
          method: 'GET'
        },
        {
          urlPattern: '/images/.*',
          handler: 'CacheFirst'
        }
      ]
    }
  },

  // 认证模块配置
  auth: {
    strategies: {
      local: {
        token: {
          property: 'token',
          global: true,
          required: true,
          type: 'Bearer'
        },
        user: {
          property: 'user',
          autoFetch: true
        },
        endpoints: {
          login: { url: '/auth/login', method: 'post' },
          logout: { url: '/auth/logout', method: 'post' },
          user: { url: '/auth/user', method: 'get' }
        }
      }
    },
    redirect: {
      login: '/login',
      logout: '/',
      callback: '/login',
      home: '/'
    }
  },

  // 国际化配置
  i18n: {
    locales: [
      { code: 'en', name: 'English', file: 'en.js' },
      { code: 'zh', name: '中文', file: 'zh.js' }
    ],
    defaultLocale: 'zh',
    langDir: 'locales/',
    strategy: 'prefix_except_default'
  },

  // 构建配置
  build: {
    transpile: [/^element-ui/],
    
    // 优化配置
    optimization: {
      splitChunks: {
        chunks: 'all',
        cacheGroups: {
          vendor: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendors',
            chunks: 'all'
          },
          common: {
            name: 'common',
            minChunks: 2,
            chunks: 'all',
            enforce: true
          }
        }
      }
    },
    
    // Webpack扩展
    extend(config, { isDev, isClient }) {
      if (isDev && isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  },

  // 服务器配置
  server: {
    port: process.env.PORT || 3000,
    host: process.env.HOST || 'localhost'
  },

  // 环境变量
  env: {
    API_BASE_URL: process.env.API_BASE_URL || 'http://localhost:3001/api'
  },

  // 渲染配置
  render: {
    // 启用HTTP/2推送
    http2: {
      push: true
    },
    
    // 静态资源配置
    static: {
      maxAge: 1000 * 60 * 60 * 24 * 7 // 7天
    },
    
    // 压缩配置
    compressor: {
      threshold: 0
    }
  }
}

11.2.2 Nuxt.js插件开发

// plugins/api.js
export default function ({ $axios, redirect, store }) {
  // 请求拦截器
  $axios.onRequest(config => {
    console.log('Making request to ' + config.url)
    
    // 添加认证头
    const token = store.getters['auth/token']
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    
    return config
  })

  // 响应拦截器
  $axios.onResponse(response => {
    return response.data
  })

  // 错误处理
  $axios.onError(error => {
    const code = parseInt(error.response && error.response.status)
    
    if (code === 401) {
      // 未授权,重定向到登录页
      store.dispatch('auth/logout')
      redirect('/login')
    } else if (code === 403) {
      // 禁止访问
      redirect('/403')
    } else if (code === 404) {
      // 页面不存在
      redirect('/404')
    } else if (code >= 500) {
      // 服务器错误
      redirect('/500')
    }
    
    return Promise.reject(error)
  })
}
// plugins/global-components.js
import Vue from 'vue'
import LoadingSpinner from '@/components/common/LoadingSpinner.vue'
import ErrorMessage from '@/components/common/ErrorMessage.vue'
import ConfirmDialog from '@/components/common/ConfirmDialog.vue'

// 注册全局组件
Vue.component('LoadingSpinner', LoadingSpinner)
Vue.component('ErrorMessage', ErrorMessage)
Vue.component('ConfirmDialog', ConfirmDialog)

// 全局混入
Vue.mixin({
  methods: {
    // 格式化价格
    formatPrice(price, currency = 'USD') {
      return new Intl.NumberFormat('en-US', {
        style: 'currency',
        currency
      }).format(price)
    },
    
    // 格式化日期
    formatDate(date, options = {}) {
      const defaultOptions = {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      }
      
      return new Intl.DateTimeFormat('zh-CN', {
        ...defaultOptions,
        ...options
      }).format(new Date(date))
    },
    
    // 截断文本
    truncateText(text, length = 100) {
      if (!text) return ''
      return text.length > length ? text.substring(0, length) + '...' : text
    },
    
    // 防抖函数
    debounce(func, wait = 300) {
      let timeout
      return function executedFunction(...args) {
        const later = () => {
          clearTimeout(timeout)
          func(...args)
        }
        clearTimeout(timeout)
        timeout = setTimeout(later, wait)
      }
    }
  }
})

11.2.3 Nuxt.js中间件

// middleware/auth.js
export default function ({ store, redirect, route }) {
  // 检查用户是否已认证
  if (!store.getters['auth/isAuthenticated']) {
    // 保存当前路由,登录后重定向
    const redirectPath = route.fullPath !== '/login' ? route.fullPath : '/'
    return redirect(`/login?redirect=${encodeURIComponent(redirectPath)}`)
  }
}
// middleware/admin.js
export default function ({ store, redirect, error }) {
  // 检查用户是否为管理员
  const user = store.getters['auth/user']
  
  if (!user || user.role !== 'admin') {
    error({
      statusCode: 403,
      message: '访问被拒绝:需要管理员权限'
    })
  }
}
// middleware/guest.js
export default function ({ store, redirect }) {
  // 如果用户已登录,重定向到首页
  if (store.getters['auth/isAuthenticated']) {
    return redirect('/')
  }
}

11.3 开发工具与调试

11.3.1 Vue DevTools集成

// plugins/vue-devtools.js
import Vue from 'vue'

if (process.client && process.env.NODE_ENV === 'development') {
  // 启用Vue DevTools
  Vue.config.devtools = true
  
  // 性能追踪
  Vue.config.performance = true
  
  // 自定义DevTools钩子
  if (window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
    window.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue = Vue
  }
  
  // SSR调试信息
  Vue.mixin({
    beforeCreate() {
      if (this.$ssrContext) {
        console.log('SSR Context:', this.$ssrContext.url)
      }
    }
  })
}

11.3.2 性能监控工具

// utils/performance.js
class PerformanceMonitor {
  constructor() {
    this.metrics = new Map()
    this.observers = []
    this.init()
  }

  init() {
    if (typeof window === 'undefined') return
    
    // 监听页面加载性能
    this.observePageLoad()
    
    // 监听资源加载
    this.observeResources()
    
    // 监听用户交互
    this.observeInteractions()
    
    // 监听Vue组件性能
    this.observeVueComponents()
  }

  // 页面加载性能
  observePageLoad() {
    window.addEventListener('load', () => {
      setTimeout(() => {
        const navigation = performance.getEntriesByType('navigation')[0]
        
        this.recordMetric('page_load', {
          dns: navigation.domainLookupEnd - navigation.domainLookupStart,
          tcp: navigation.connectEnd - navigation.connectStart,
          request: navigation.responseStart - navigation.requestStart,
          response: navigation.responseEnd - navigation.responseStart,
          dom: navigation.domContentLoadedEventEnd - navigation.responseEnd,
          load: navigation.loadEventEnd - navigation.loadEventStart,
          total: navigation.loadEventEnd - navigation.navigationStart
        })
      }, 0)
    })
  }

  // 资源加载性能
  observeResources() {
    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        if (entry.entryType === 'resource') {
          this.recordMetric('resource_load', {
            name: entry.name,
            type: this.getResourceType(entry.name),
            duration: entry.duration,
            size: entry.transferSize,
            cached: entry.transferSize === 0
          })
        }
      })
    })
    
    observer.observe({ entryTypes: ['resource'] })
    this.observers.push(observer)
  }

  // 用户交互性能
  observeInteractions() {
    // First Input Delay (FID)
    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        this.recordMetric('first_input_delay', {
          delay: entry.processingStart - entry.startTime,
          duration: entry.duration,
          target: entry.target?.tagName
        })
      })
    })
    
    observer.observe({ entryTypes: ['first-input'] })
    this.observers.push(observer)
  }

  // Vue组件性能
  observeVueComponents() {
    if (typeof Vue === 'undefined') return
    
    Vue.mixin({
      beforeCreate() {
        this._performanceStart = performance.now()
      },
      
      mounted() {
        const duration = performance.now() - this._performanceStart
        
        if (duration > 16) { // 超过一帧的时间
          this.$performanceMonitor?.recordMetric('component_mount', {
            component: this.$options.name || 'Anonymous',
            duration,
            route: this.$route?.path
          })
        }
      }
    })
  }

  // 记录指标
  recordMetric(type, data) {
    const timestamp = Date.now()
    
    if (!this.metrics.has(type)) {
      this.metrics.set(type, [])
    }
    
    this.metrics.get(type).push({
      ...data,
      timestamp,
      url: window.location.href,
      userAgent: navigator.userAgent
    })
    
    // 发送到分析服务
    this.sendToAnalytics(type, data)
  }

  // 发送分析数据
  sendToAnalytics(type, data) {
    // 批量发送,避免频繁请求
    if (!this._analyticsQueue) {
      this._analyticsQueue = []
    }
    
    this._analyticsQueue.push({ type, data, timestamp: Date.now() })
    
    // 延迟发送
    if (!this._analyticsTimer) {
      this._analyticsTimer = setTimeout(() => {
        this.flushAnalytics()
      }, 5000)
    }
  }

  // 批量发送分析数据
  async flushAnalytics() {
    if (!this._analyticsQueue || this._analyticsQueue.length === 0) return
    
    try {
      await fetch('/api/analytics/performance', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          metrics: this._analyticsQueue,
          session: this.getSessionId()
        })
      })
    } catch (error) {
      console.error('Failed to send analytics:', error)
    } finally {
      this._analyticsQueue = []
      this._analyticsTimer = null
    }
  }

  // 获取资源类型
  getResourceType(url) {
    if (url.match(/\.(js|mjs)$/)) return 'script'
    if (url.match(/\.(css)$/)) return 'stylesheet'
    if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/)) return 'image'
    if (url.match(/\.(woff|woff2|ttf|eot)$/)) return 'font'
    return 'other'
  }

  // 获取会话ID
  getSessionId() {
    let sessionId = sessionStorage.getItem('performance_session_id')
    if (!sessionId) {
      sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
      sessionStorage.setItem('performance_session_id', sessionId)
    }
    return sessionId
  }

  // 获取性能报告
  getReport() {
    const report = {
      timestamp: Date.now(),
      url: window.location.href,
      metrics: {}
    }
    
    for (const [type, entries] of this.metrics.entries()) {
      report.metrics[type] = {
        count: entries.length,
        latest: entries[entries.length - 1],
        average: this.calculateAverage(entries)
      }
    }
    
    return report
  }

  // 计算平均值
  calculateAverage(entries) {
    if (entries.length === 0) return 0
    
    const numericFields = ['duration', 'delay', 'total', 'size']
    const averages = {}
    
    numericFields.forEach(field => {
      const values = entries
        .map(entry => entry[field])
        .filter(value => typeof value === 'number')
      
      if (values.length > 0) {
        averages[field] = values.reduce((sum, value) => sum + value, 0) / values.length
      }
    })
    
    return averages
  }

  // 清理资源
  destroy() {
    this.observers.forEach(observer => observer.disconnect())
    this.observers = []
    this.metrics.clear()
    
    if (this._analyticsTimer) {
      clearTimeout(this._analyticsTimer)
      this.flushAnalytics()
    }
  }
}

// 创建全局实例
const performanceMonitor = new PerformanceMonitor()

// Vue插件
export default {
  install(Vue) {
    Vue.prototype.$performanceMonitor = performanceMonitor
    
    // 全局混入
    Vue.mixin({
      beforeDestroy() {
        // 组件销毁时记录生命周期
        if (this._performanceStart) {
          const lifetime = performance.now() - this._performanceStart
          if (lifetime > 1000) { // 超过1秒的组件
            performanceMonitor.recordMetric('component_lifetime', {
              component: this.$options.name || 'Anonymous',
              lifetime,
              route: this.$route?.path
            })
          }
        }
      }
    })
  }
}

export { performanceMonitor }

11.3.3 错误监控与日志

// utils/error-tracker.js
class ErrorTracker {
  constructor(options = {}) {
    this.options = {
      apiEndpoint: '/api/errors',
      maxErrors: 50,
      enableConsoleLog: true,
      enableRemoteLog: true,
      ...options
    }
    
    this.errors = []
    this.init()
  }

  init() {
    // 全局错误处理
    if (typeof window !== 'undefined') {
      window.addEventListener('error', this.handleError.bind(this))
      window.addEventListener('unhandledrejection', this.handlePromiseRejection.bind(this))
    }
    
    // Vue错误处理
    if (typeof Vue !== 'undefined') {
      Vue.config.errorHandler = this.handleVueError.bind(this)
    }
  }

  // 处理JavaScript错误
  handleError(event) {
    const error = {
      type: 'javascript',
      message: event.message,
      filename: event.filename,
      lineno: event.lineno,
      colno: event.colno,
      stack: event.error?.stack,
      timestamp: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    }
    
    this.recordError(error)
  }

  // 处理Promise拒绝
  handlePromiseRejection(event) {
    const error = {
      type: 'promise_rejection',
      message: event.reason?.message || 'Unhandled Promise Rejection',
      stack: event.reason?.stack,
      timestamp: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    }
    
    this.recordError(error)
  }

  // 处理Vue错误
  handleVueError(err, vm, info) {
    const error = {
      type: 'vue',
      message: err.message,
      stack: err.stack,
      component: vm?.$options.name || 'Anonymous',
      lifecycle: info,
      props: vm?.$props,
      route: vm?.$route?.path,
      timestamp: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    }
    
    this.recordError(error)
  }

  // 记录错误
  recordError(error) {
    // 添加到本地错误列表
    this.errors.push(error)
    
    // 限制错误数量
    if (this.errors.length > this.options.maxErrors) {
      this.errors.shift()
    }
    
    // 控制台输出
    if (this.options.enableConsoleLog) {
      console.error('Error tracked:', error)
    }
    
    // 发送到远程服务
    if (this.options.enableRemoteLog) {
      this.sendToRemote(error)
    }
  }

  // 发送到远程服务
  async sendToRemote(error) {
    try {
      await fetch(this.options.apiEndpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          error,
          context: this.getContext()
        })
      })
    } catch (err) {
      console.error('Failed to send error to remote:', err)
    }
  }

  // 获取上下文信息
  getContext() {
    return {
      timestamp: Date.now(),
      url: window.location.href,
      referrer: document.referrer,
      userAgent: navigator.userAgent,
      viewport: {
        width: window.innerWidth,
        height: window.innerHeight
      },
      screen: {
        width: screen.width,
        height: screen.height
      },
      connection: navigator.connection ? {
        effectiveType: navigator.connection.effectiveType,
        downlink: navigator.connection.downlink
      } : null
    }
  }

  // 手动记录错误
  captureError(error, context = {}) {
    const errorData = {
      type: 'manual',
      message: error.message || error,
      stack: error.stack,
      context,
      timestamp: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    }
    
    this.recordError(errorData)
  }

  // 记录用户操作
  captureUserAction(action, data = {}) {
    const actionData = {
      type: 'user_action',
      action,
      data,
      timestamp: Date.now(),
      url: window.location.href
    }
    
    // 这里可以发送到分析服务
    console.log('User action:', actionData)
  }

  // 获取错误报告
  getErrorReport() {
    return {
      errors: this.errors,
      summary: {
        total: this.errors.length,
        byType: this.groupErrorsByType(),
        recent: this.errors.slice(-10)
      }
    }
  }

  // 按类型分组错误
  groupErrorsByType() {
    const groups = {}
    
    this.errors.forEach(error => {
      const type = error.type
      if (!groups[type]) {
        groups[type] = 0
      }
      groups[type]++
    })
    
    return groups
  }

  // 清除错误
  clearErrors() {
    this.errors = []
  }
}

// 创建全局实例
const errorTracker = new ErrorTracker()

// Vue插件
export default {
  install(Vue, options = {}) {
    const tracker = new ErrorTracker(options)
    
    Vue.prototype.$errorTracker = tracker
    
    // 全局混入
    Vue.mixin({
      methods: {
        $captureError(error, context) {
          tracker.captureError(error, context)
        },
        
        $captureAction(action, data) {
          tracker.captureUserAction(action, data)
        }
      }
    })
  }
}

export { errorTracker }

11.4 构建优化工具

11.4.1 Webpack Bundle分析

// build/analyzer.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const DuplicatePackageCheckerPlugin = require('duplicate-package-checker-webpack-plugin')

class BuildAnalyzer {
  constructor(options = {}) {
    this.options = {
      analyze: process.env.ANALYZE === 'true',
      speed: process.env.SPEED === 'true',
      duplicates: process.env.CHECK_DUPLICATES === 'true',
      ...options
    }
  }

  // 获取分析插件
  getPlugins() {
    const plugins = []

    // Bundle分析
    if (this.options.analyze) {
      plugins.push(
        new BundleAnalyzerPlugin({
          analyzerMode: 'static',
          openAnalyzer: false,
          reportFilename: 'bundle-report.html',
          generateStatsFile: true,
          statsFilename: 'bundle-stats.json'
        })
      )
    }

    // 重复包检查
    if (this.options.duplicates) {
      plugins.push(
        new DuplicatePackageCheckerPlugin({
          verbose: true,
          emitError: false,
          showHelp: true,
          strict: false
        })
      )
    }

    return plugins
  }

  // 包装配置以进行速度测量
  wrapConfig(config) {
    if (this.options.speed) {
      const smp = new SpeedMeasurePlugin({
        outputFormat: 'humanVerbose',
        granularLoaderData: true
      })
      return smp.wrap(config)
    }
    return config
  }

  // 生成优化建议
  generateOptimizationSuggestions(stats) {
    const suggestions = []
    const assets = stats.assets || []
    const chunks = stats.chunks || []

    // 检查大文件
    const largeAssets = assets.filter(asset => asset.size > 250000) // 250KB
    if (largeAssets.length > 0) {
      suggestions.push({
        type: 'large_assets',
        message: `发现${largeAssets.length}个大文件,建议进行代码分割或压缩`,
        assets: largeAssets.map(asset => ({
          name: asset.name,
          size: this.formatSize(asset.size)
        }))
      })
    }

    // 检查重复模块
    const modules = stats.modules || []
    const moduleMap = new Map()
    
    modules.forEach(module => {
      const name = module.name || module.identifier
      if (name) {
        const count = moduleMap.get(name) || 0
        moduleMap.set(name, count + 1)
      }
    })

    const duplicates = Array.from(moduleMap.entries())
      .filter(([name, count]) => count > 1)
      .map(([name, count]) => ({ name, count }))

    if (duplicates.length > 0) {
      suggestions.push({
        type: 'duplicate_modules',
        message: `发现${duplicates.length}个重复模块,建议优化依赖`,
        modules: duplicates
      })
    }

    // 检查未使用的代码
    const unusedExports = this.findUnusedExports(stats)
    if (unusedExports.length > 0) {
      suggestions.push({
        type: 'unused_exports',
        message: `发现${unusedExports.length}个未使用的导出,建议清理`,
        exports: unusedExports
      })
    }

    return suggestions
  }

  // 查找未使用的导出
  findUnusedExports(stats) {
    // 这里需要更复杂的分析逻辑
    // 简化版本,实际应该使用AST分析
    return []
  }

  // 格式化文件大小
  formatSize(bytes) {
    const sizes = ['Bytes', 'KB', 'MB', 'GB']
    if (bytes === 0) return '0 Bytes'
    const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
    return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]
  }

  // 生成性能报告
  generatePerformanceReport(stats) {
    const report = {
      timestamp: new Date().toISOString(),
      buildTime: stats.time,
      assets: {
        total: stats.assets?.length || 0,
        totalSize: stats.assets?.reduce((sum, asset) => sum + asset.size, 0) || 0,
        largest: stats.assets?.reduce((largest, asset) => 
          asset.size > (largest?.size || 0) ? asset : largest, null
        )
      },
      chunks: {
        total: stats.chunks?.length || 0,
        entry: stats.chunks?.filter(chunk => chunk.entry).length || 0,
        async: stats.chunks?.filter(chunk => !chunk.entry).length || 0
      },
      modules: {
        total: stats.modules?.length || 0,
        cached: stats.modules?.filter(module => module.cached).length || 0
      },
      optimizations: this.generateOptimizationSuggestions(stats)
    }

    return report
  }
}

module.exports = BuildAnalyzer

11.4.2 性能优化配置

// build/optimization.js
const TerserPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const CompressionPlugin = require('compression-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

class OptimizationConfig {
  constructor(isProd = false) {
    this.isProd = isProd
  }

  // 获取优化配置
  getOptimization() {
    const config = {
      // 代码分割
      splitChunks: {
        chunks: 'all',
        cacheGroups: {
          // 第三方库
          vendor: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendors',
            chunks: 'all',
            priority: 10,
            reuseExistingChunk: true
          },
          
          // Vue相关
          vue: {
            test: /[\\/]node_modules[\\/](vue|vue-router|vuex)[\\/]/,
            name: 'vue',
            chunks: 'all',
            priority: 20
          },
          
          // UI库
          ui: {
            test: /[\\/]node_modules[\\/](element-ui|ant-design-vue|vuetify)[\\/]/,
            name: 'ui',
            chunks: 'all',
            priority: 15
          },
          
          // 工具库
          utils: {
            test: /[\\/]node_modules[\\/](lodash|moment|axios)[\\/]/,
            name: 'utils',
            chunks: 'all',
            priority: 12
          },
          
          // 公共代码
          common: {
            name: 'common',
            minChunks: 2,
            chunks: 'all',
            priority: 5,
            reuseExistingChunk: true,
            enforce: true
          }
        }
      },
      
      // 运行时chunk
      runtimeChunk: {
        name: 'runtime'
      }
    }

    // 生产环境优化
    if (this.isProd) {
      config.minimizer = [
        // JS压缩
        new TerserPlugin({
          cache: true,
          parallel: true,
          sourceMap: false,
          terserOptions: {
            compress: {
              drop_console: true,
              drop_debugger: true,
              pure_funcs: ['console.log']
            },
            mangle: {
              safari10: true
            },
            output: {
              comments: false,
              ascii_only: true
            }
          }
        }),
        
        // CSS压缩
        new OptimizeCSSAssetsPlugin({
          cssProcessorOptions: {
            safe: true,
            autoprefixer: { disable: true },
            mergeLonghand: false,
            discardComments: {
              removeAll: true
            }
          }
        })
      ]
    }

    return config
  }

  // 获取性能优化插件
  getPlugins() {
    const plugins = []

    if (this.isProd) {
      // 清理输出目录
      plugins.push(new CleanWebpackPlugin())
      
      // Gzip压缩
      plugins.push(
        new CompressionPlugin({
          algorithm: 'gzip',
          test: /\.(js|css|html|svg)$/,
          threshold: 8192,
          minRatio: 0.8
        })
      )
      
      // Brotli压缩
      plugins.push(
        new CompressionPlugin({
          filename: '[path][base].br',
          algorithm: 'brotliCompress',
          test: /\.(js|css|html|svg)$/,
          compressionOptions: {
            level: 11
          },
          threshold: 8192,
          minRatio: 0.8
        })
      )
    }

    return plugins
  }

  // 获取模块解析优化
  getResolve() {
    return {
      // 模块查找优化
      modules: [
        'node_modules',
        path.resolve(__dirname, '../client'),
        path.resolve(__dirname, '../shared')
      ],
      
      // 别名配置
      alias: {
        '@': path.resolve(__dirname, '../client'),
        '~': path.resolve(__dirname, '../server'),
        'shared': path.resolve(__dirname, '../shared'),
        'vue$': 'vue/dist/vue.runtime.esm.js'
      },
      
      // 扩展名优化
      extensions: ['.js', '.vue', '.json'],
      
      // 主字段优化
      mainFields: ['browser', 'module', 'main'],
      
      // 符号链接优化
      symlinks: false
    }
  }

  // 获取缓存配置
  getCacheConfig() {
    return {
      type: 'filesystem',
      cacheDirectory: path.resolve(__dirname, '../node_modules/.cache/webpack'),
      buildDependencies: {
        config: [__filename]
      }
    }
  }
}

module.exports = OptimizationConfig

11.5 本章小结

本章深入介绍了Vue SSR的生态系统与工具链,包括:

核心要点

  1. 生态系统概览

    • 核心工具链组成
    • 框架对比分析
    • 技术选型指导
  2. Nuxt.js深入解析

    • 项目配置与结构
    • 插件开发
    • 中间件使用
  3. 开发工具与调试

    • Vue DevTools集成
    • 性能监控工具
    • 错误追踪系统
  4. 构建优化工具

    • Bundle分析
    • 性能优化配置
    • 自动化优化

最佳实践

  1. 工具选择

    • 根据项目需求选择合适的框架
    • 合理配置开发工具
    • 建立完善的监控体系
  2. 性能优化

    • 持续监控应用性能
    • 定期分析构建产物
    • 优化关键性能指标
  3. 开发效率

    • 自动化开发流程
    • 统一代码规范
    • 完善错误处理

练习作业

  1. 配置完整的Nuxt.js项目
  2. 实现性能监控系统
  3. 搭建错误追踪服务
  4. 优化构建配置

下一章我们将学习Vue SSR的最佳实践与案例分析。