本章将介绍Vue.js开发中的最佳实践、进阶技巧和团队协作规范,帮助你构建高质量的Vue.js应用。

11.1 代码规范与团队协作

ESLint配置

// .eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true,
    browser: true,
    es2022: true
  },
  extends: [
    'plugin:vue/vue3-essential',
    'plugin:vue/vue3-strongly-recommended',
    'plugin:vue/vue3-recommended',
    '@vue/eslint-config-typescript',
    '@vue/eslint-config-prettier'
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  rules: {
    // Vue相关规则
    'vue/multi-word-component-names': 'error',
    'vue/component-definition-name-casing': ['error', 'PascalCase'],
    'vue/component-name-in-template-casing': ['error', 'PascalCase'],
    'vue/custom-event-name-casing': ['error', 'camelCase'],
    'vue/define-emits-declaration': 'error',
    'vue/define-props-declaration': 'error',
    'vue/no-unused-vars': 'error',
    'vue/no-unused-components': 'error',
    'vue/no-unused-refs': 'error',
    'vue/prefer-import-from-vue': 'error',
    'vue/prefer-separate-static-class': 'error',
    'vue/require-macro-variable-name': 'error',
    'vue/block-order': ['error', {
      order: ['template', 'script', 'style']
    }],
    'vue/component-tags-order': ['error', {
      order: ['template', 'script', 'style']
    }],
    'vue/padding-line-between-blocks': 'error',
    
    // JavaScript/TypeScript规则
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-unused-vars': 'error',
    'prefer-const': 'error',
    'no-var': 'error',
    'object-shorthand': 'error',
    'prefer-template': 'error',
    'template-curly-spacing': 'error',
    'arrow-spacing': 'error',
    'comma-dangle': ['error', 'never'],
    'quotes': ['error', 'single'],
    'semi': ['error', 'never'],
    'indent': ['error', 2],
    'max-len': ['error', { code: 120 }],
    'no-multiple-empty-lines': ['error', { max: 1 }],
    'eol-last': 'error'
  },
  overrides: [
    {
      files: ['*.vue'],
      rules: {
        'max-len': 'off' // Vue模板中可能有长行
      }
    },
    {
      files: ['**/__tests__/**/*', '**/*.{test,spec}.*'],
      env: {
        jest: true
      },
      rules: {
        'no-unused-expressions': 'off'
      }
    }
  ]
}

Prettier配置

// .prettierrc
{
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "none",
  "printWidth": 120,
  "endOfLine": "lf",
  "arrowParens": "avoid",
  "bracketSpacing": true,
  "bracketSameLine": false,
  "vueIndentScriptAndStyle": false
}

Git Hooks配置

// package.json
{
  "scripts": {
    "prepare": "husky install",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
    "format": "prettier --write .",
    "type-check": "vue-tsc --noEmit",
    "test": "vitest",
    "test:run": "vitest run",
    "build": "vue-tsc --noEmit && vite build"
  },
  "lint-staged": {
    "*.{vue,js,ts}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{css,scss,less}": [
      "prettier --write"
    ],
    "*.{json,md}": [
      "prettier --write"
    ]
  },
  "devDependencies": {
    "husky": "^8.0.3",
    "lint-staged": "^13.2.3"
  }
}
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
# .husky/commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx commitlint --edit $1

提交信息规范

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',     // 新功能
        'fix',      // 修复bug
        'docs',     // 文档更新
        'style',    // 代码格式化
        'refactor', // 重构
        'perf',     // 性能优化
        'test',     // 测试
        'chore',    // 构建过程或辅助工具的变动
        'revert',   // 回滚
        'build',    // 构建系统或外部依赖项的更改
        'ci'        // CI配置文件和脚本的更改
      ]
    ],
    'type-case': [2, 'always', 'lower-case'],
    'type-empty': [2, 'never'],
    'scope-case': [2, 'always', 'lower-case'],
    'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
    'subject-empty': [2, 'never'],
    'subject-full-stop': [2, 'never', '.'],
    'header-max-length': [2, 'always', 72],
    'body-leading-blank': [1, 'always'],
    'body-max-line-length': [2, 'always', 100],
    'footer-leading-blank': [1, 'always'],
    'footer-max-line-length': [2, 'always', 100]
  }
}

11.2 组件设计模式

高阶组件(HOC)

// src/hoc/withLoading.js
import { defineComponent, h } from 'vue'

export function withLoading(WrappedComponent, loadingComponent) {
  return defineComponent({
    name: `WithLoading(${WrappedComponent.name})`,
    
    props: {
      loading: {
        type: Boolean,
        default: false
      },
      ...WrappedComponent.props
    },
    
    setup(props, { slots, attrs }) {
      return () => {
        if (props.loading) {
          return h(loadingComponent || 'div', { class: 'loading' }, 'Loading...')
        }
        
        return h(WrappedComponent, {
          ...attrs,
          ...props
        }, slots)
      }
    }
  })
}

// 使用示例
import UserList from './UserList.vue'
import LoadingSpinner from './LoadingSpinner.vue'

const UserListWithLoading = withLoading(UserList, LoadingSpinner)

渲染函数组件

// src/components/DynamicTable.js
import { defineComponent, h } from 'vue'

export default defineComponent({
  name: 'DynamicTable',
  
  props: {
    columns: {
      type: Array,
      required: true
    },
    data: {
      type: Array,
      required: true
    },
    rowKey: {
      type: String,
      default: 'id'
    }
  },
  
  emits: ['row-click', 'cell-click'],
  
  setup(props, { emit }) {
    function renderCell(row, column, rowIndex, columnIndex) {
      const value = row[column.key]
      
      // 自定义渲染函数
      if (column.render) {
        return column.render(value, row, rowIndex)
      }
      
      // 插槽渲染
      if (column.slot) {
        return h('slot', {
          name: column.slot,
          value,
          row,
          rowIndex,
          column,
          columnIndex
        })
      }
      
      // 默认渲染
      return value
    }
    
    function renderRow(row, rowIndex) {
      return h('tr', {
        key: row[props.rowKey],
        class: {
          'table-row': true,
          'table-row-even': rowIndex % 2 === 0,
          'table-row-odd': rowIndex % 2 === 1
        },
        onClick: () => emit('row-click', row, rowIndex)
      }, props.columns.map((column, columnIndex) => 
        h('td', {
          key: column.key,
          class: {
            'table-cell': true,
            [`table-cell-${column.align || 'left'}`]: true
          },
          onClick: (event) => {
            event.stopPropagation()
            emit('cell-click', row[column.key], row, rowIndex, column, columnIndex)
          }
        }, renderCell(row, column, rowIndex, columnIndex))
      ))
    }
    
    function renderHeader() {
      return h('thead', {
        class: 'table-header'
      }, [
        h('tr', props.columns.map(column => 
          h('th', {
            key: column.key,
            class: {
              'table-header-cell': true,
              [`table-header-cell-${column.align || 'left'}`]: true,
              'sortable': column.sortable
            },
            style: {
              width: column.width
            }
          }, column.title)
        ))
      ])
    }
    
    function renderBody() {
      return h('tbody', {
        class: 'table-body'
      }, props.data.map((row, index) => renderRow(row, index)))
    }
    
    return () => {
      return h('table', {
        class: 'dynamic-table'
      }, [
        renderHeader(),
        renderBody()
      ])
    }
  }
})

组合式组件

<!-- src/components/ComposableForm.vue -->
<template>
  <form @submit.prevent="handleSubmit" class="composable-form">
    <div
      v-for="field in fields"
      :key="field.name"
      class="form-field"
    >
      <label :for="field.name" class="form-label">
        {{ field.label }}
        <span v-if="field.required" class="required">*</span>
      </label>
      
      <component
        :is="getFieldComponent(field.type)"
        :id="field.name"
        v-model="formData[field.name]"
        v-bind="field.props"
        :class="{
          'form-control': true,
          'error': errors[field.name]
        }"
        @blur="validateField(field.name)"
      />
      
      <div v-if="errors[field.name]" class="error-message">
        {{ errors[field.name] }}
      </div>
    </div>
    
    <div class="form-actions">
      <button type="submit" :disabled="!isValid" class="btn btn-primary">
        {{ submitText }}
      </button>
      <button type="button" @click="reset" class="btn btn-secondary">
        重置
      </button>
    </div>
  </form>
</template>

<script setup>
import { computed } from 'vue'
import { useForm } from '@/composables/useForm'

const props = defineProps({
  fields: {
    type: Array,
    required: true
  },
  initialData: {
    type: Object,
    default: () => ({})
  },
  submitText: {
    type: String,
    default: '提交'
  }
})

const emit = defineEmits(['submit'])

const {
  formData,
  errors,
  isValid,
  validateField,
  validateAll,
  reset
} = useForm(props.fields, props.initialData)

const fieldComponents = {
  text: 'input',
  email: 'input',
  password: 'input',
  number: 'input',
  textarea: 'textarea',
  select: 'select',
  checkbox: 'input',
  radio: 'input',
  date: 'input',
  file: 'input'
}

function getFieldComponent(type) {
  return fieldComponents[type] || 'input'
}

async function handleSubmit() {
  const isFormValid = await validateAll()
  
  if (isFormValid) {
    emit('submit', { ...formData.value })
  }
}
</script>

<style scoped>
.composable-form {
  max-width: 600px;
  margin: 0 auto;
}

.form-field {
  margin-bottom: 1rem;
}

.form-label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 500;
  color: #333;
}

.required {
  color: #e74c3c;
}

.form-control {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 1rem;
  transition: border-color 0.3s;
}

.form-control:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}

.form-control.error {
  border-color: #e74c3c;
}

.error-message {
  color: #e74c3c;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

.form-actions {
  display: flex;
  gap: 1rem;
  margin-top: 2rem;
}

.btn {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
  transition: background-color 0.3s;
}

.btn-primary {
  background-color: #007bff;
  color: white;
}

.btn-primary:hover:not(:disabled) {
  background-color: #0056b3;
}

.btn-primary:disabled {
  background-color: #6c757d;
  cursor: not-allowed;
}

.btn-secondary {
  background-color: #6c757d;
  color: white;
}

.btn-secondary:hover {
  background-color: #545b62;
}
</style>

11.3 性能优化进阶

组件懒加载策略

// src/utils/lazyLoad.js
import { defineAsyncComponent, h } from 'vue'

// 创建懒加载组件的工厂函数
export function createLazyComponent(loader, options = {}) {
  const {
    delay = 200,
    timeout = 3000,
    errorComponent = null,
    loadingComponent = null,
    retryCount = 3
  } = options
  
  return defineAsyncComponent({
    loader: () => {
      let retries = 0
      
      const load = async () => {
        try {
          const component = await loader()
          return component
        } catch (error) {
          if (retries < retryCount) {
            retries++
            console.warn(`Component load failed, retrying... (${retries}/${retryCount})`)
            await new Promise(resolve => setTimeout(resolve, 1000 * retries))
            return load()
          }
          throw error
        }
      }
      
      return load()
    },
    
    loadingComponent: loadingComponent || {
      template: '<div class="loading-placeholder">Loading...</div>'
    },
    
    errorComponent: errorComponent || {
      template: '<div class="error-placeholder">Failed to load component</div>'
    },
    
    delay,
    timeout,
    suspensible: false
  })
}

// 路由级别的懒加载
export function createLazyRoute(loader, options = {}) {
  return {
    component: createLazyComponent(loader, options),
    meta: {
      ...options.meta,
      lazy: true
    }
  }
}

// 预加载策略
export class ComponentPreloader {
  constructor() {
    this.cache = new Map()
    this.loading = new Set()
  }
  
  async preload(loader, key) {
    if (this.cache.has(key) || this.loading.has(key)) {
      return this.cache.get(key)
    }
    
    this.loading.add(key)
    
    try {
      const component = await loader()
      this.cache.set(key, component)
      return component
    } catch (error) {
      console.error(`Failed to preload component ${key}:`, error)
      throw error
    } finally {
      this.loading.delete(key)
    }
  }
  
  // 预加载路由组件
  preloadRoute(route) {
    if (route.component && typeof route.component === 'function') {
      return this.preload(route.component, route.name || route.path)
    }
  }
  
  // 批量预加载
  async preloadBatch(loaders) {
    const promises = Object.entries(loaders).map(([key, loader]) => 
      this.preload(loader, key).catch(error => ({ key, error }))
    )
    
    const results = await Promise.allSettled(promises)
    
    const errors = results
      .filter(result => result.status === 'rejected' || result.value?.error)
      .map(result => result.reason || result.value.error)
    
    if (errors.length > 0) {
      console.warn('Some components failed to preload:', errors)
    }
    
    return results
  }
}

export const preloader = new ComponentPreloader()

内存泄漏防护

// src/composables/useMemoryLeak.js
import { onUnmounted, ref } from 'vue'

// 防止内存泄漏的工具函数
export function useMemoryLeak() {
  const timers = ref(new Set())
  const intervals = ref(new Set())
  const listeners = ref(new Map())
  const observers = ref(new Set())
  
  // 安全的setTimeout
  function safeSetTimeout(callback, delay) {
    const timer = setTimeout(() => {
      timers.value.delete(timer)
      callback()
    }, delay)
    
    timers.value.add(timer)
    return timer
  }
  
  // 安全的setInterval
  function safeSetInterval(callback, delay) {
    const interval = setInterval(callback, delay)
    intervals.value.add(interval)
    return interval
  }
  
  // 安全的事件监听
  function safeAddEventListener(target, event, handler, options) {
    target.addEventListener(event, handler, options)
    
    const key = `${target.constructor.name}-${event}`
    if (!listeners.value.has(key)) {
      listeners.value.set(key, [])
    }
    listeners.value.get(key).push({ target, event, handler, options })
  }
  
  // 安全的Observer
  function safeObserver(ObserverClass, callback, options) {
    const observer = new ObserverClass(callback, options)
    observers.value.add(observer)
    return observer
  }
  
  // 清理函数
  function cleanup() {
    // 清理定时器
    timers.value.forEach(timer => clearTimeout(timer))
    timers.value.clear()
    
    // 清理间隔器
    intervals.value.forEach(interval => clearInterval(interval))
    intervals.value.clear()
    
    // 清理事件监听器
    listeners.value.forEach(listenerList => {
      listenerList.forEach(({ target, event, handler, options }) => {
        target.removeEventListener(event, handler, options)
      })
    })
    listeners.value.clear()
    
    // 清理观察者
    observers.value.forEach(observer => {
      if (observer.disconnect) {
        observer.disconnect()
      } else if (observer.unobserve) {
        observer.unobserve()
      }
    })
    observers.value.clear()
  }
  
  // 组件卸载时自动清理
  onUnmounted(cleanup)
  
  return {
    safeSetTimeout,
    safeSetInterval,
    safeAddEventListener,
    safeObserver,
    cleanup
  }
}

// 使用示例
export function usePolling(callback, interval = 1000) {
  const { safeSetInterval } = useMemoryLeak()
  const isPolling = ref(false)
  let intervalId = null
  
  function start() {
    if (!isPolling.value) {
      isPolling.value = true
      intervalId = safeSetInterval(callback, interval)
    }
  }
  
  function stop() {
    if (isPolling.value && intervalId) {
      clearInterval(intervalId)
      isPolling.value = false
      intervalId = null
    }
  }
  
  return {
    isPolling,
    start,
    stop
  }
}

大数据处理优化

// src/composables/useBigData.js
import { ref, computed, nextTick } from 'vue'

export function useBigData(data, options = {}) {
  const {
    pageSize = 100,
    searchFields = [],
    sortField = null,
    sortOrder = 'asc'
  } = options
  
  const currentPage = ref(1)
  const searchQuery = ref('')
  const sortBy = ref(sortField)
  const sortDirection = ref(sortOrder)
  const loading = ref(false)
  
  // 搜索过滤
  const filteredData = computed(() => {
    if (!searchQuery.value || searchFields.length === 0) {
      return data.value
    }
    
    const query = searchQuery.value.toLowerCase()
    return data.value.filter(item => 
      searchFields.some(field => {
        const value = getNestedValue(item, field)
        return value && value.toString().toLowerCase().includes(query)
      })
    )
  })
  
  // 排序
  const sortedData = computed(() => {
    if (!sortBy.value) {
      return filteredData.value
    }
    
    return [...filteredData.value].sort((a, b) => {
      const aValue = getNestedValue(a, sortBy.value)
      const bValue = getNestedValue(b, sortBy.value)
      
      let result = 0
      
      if (aValue < bValue) result = -1
      else if (aValue > bValue) result = 1
      
      return sortDirection.value === 'desc' ? -result : result
    })
  })
  
  // 分页数据
  const paginatedData = computed(() => {
    const start = (currentPage.value - 1) * pageSize
    const end = start + pageSize
    return sortedData.value.slice(start, end)
  })
  
  // 总页数
  const totalPages = computed(() => 
    Math.ceil(sortedData.value.length / pageSize)
  )
  
  // 获取嵌套属性值
  function getNestedValue(obj, path) {
    return path.split('.').reduce((current, key) => current?.[key], obj)
  }
  
  // 搜索
  function search(query) {
    searchQuery.value = query
    currentPage.value = 1
  }
  
  // 排序
  function sort(field, direction = 'asc') {
    sortBy.value = field
    sortDirection.value = direction
    currentPage.value = 1
  }
  
  // 切换排序方向
  function toggleSort(field) {
    if (sortBy.value === field) {
      sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'
    } else {
      sortBy.value = field
      sortDirection.value = 'asc'
    }
    currentPage.value = 1
  }
  
  // 跳转页面
  function goToPage(page) {
    if (page >= 1 && page <= totalPages.value) {
      currentPage.value = page
    }
  }
  
  // 重置
  function reset() {
    searchQuery.value = ''
    sortBy.value = sortField
    sortDirection.value = sortOrder
    currentPage.value = 1
  }
  
  // 异步加载数据
  async function loadData(loader) {
    loading.value = true
    
    try {
      const result = await loader({
        page: currentPage.value,
        size: pageSize,
        search: searchQuery.value,
        sortBy: sortBy.value,
        sortDirection: sortDirection.value
      })
      
      return result
    } catch (error) {
      console.error('Failed to load data:', error)
      throw error
    } finally {
      loading.value = false
    }
  }
  
  return {
    // 状态
    currentPage,
    searchQuery,
    sortBy,
    sortDirection,
    loading,
    
    // 计算属性
    filteredData,
    sortedData,
    paginatedData,
    totalPages,
    
    // 方法
    search,
    sort,
    toggleSort,
    goToPage,
    reset,
    loadData
  }
}

11.4 微前端架构

主应用配置

// main-app/src/main.js
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { registerMicroApps, start } from 'qiankun'
import App from './App.vue'

const app = createApp(App)

// 路由配置
const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: () => import('./views/Home.vue')
    },
    {
      path: '/about',
      name: 'About',
      component: () => import('./views/About.vue')
    }
  ]
})

app.use(router)

// 注册微应用
registerMicroApps([
  {
    name: 'user-management',
    entry: '//localhost:8081',
    container: '#micro-app-container',
    activeRule: '/user-management'
  },
  {
    name: 'order-system',
    entry: '//localhost:8082',
    container: '#micro-app-container',
    activeRule: '/order-system'
  },
  {
    name: 'analytics-dashboard',
    entry: '//localhost:8083',
    container: '#micro-app-container',
    activeRule: '/analytics'
  }
], {
  beforeLoad: (app) => {
    console.log('Before load:', app.name)
    return Promise.resolve()
  },
  beforeMount: (app) => {
    console.log('Before mount:', app.name)
    return Promise.resolve()
  },
  afterMount: (app) => {
    console.log('After mount:', app.name)
    return Promise.resolve()
  },
  beforeUnmount: (app) => {
    console.log('Before unmount:', app.name)
    return Promise.resolve()
  },
  afterUnmount: (app) => {
    console.log('After unmount:', app.name)
    return Promise.resolve()
  }
})

// 设置默认进入的子应用
setDefaultMountApp('/user-management')

// 启动qiankun
start({
  prefetch: true, // 预加载
  sandbox: {
    strictStyleIsolation: true, // 样式隔离
    experimentalStyleIsolation: true
  },
  singular: false // 是否为单实例场景
})

app.mount('#app')

子应用配置

// micro-app/src/main.js
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import routes from './router'

let instance = null
let router = null

// 渲染函数
function render(props = {}) {
  const { container } = props
  
  router = createRouter({
    history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/user-management' : '/'),
    routes
  })
  
  instance = createApp(App)
  instance.use(router)
  
  const containerElement = container 
    ? container.querySelector('#app') 
    : document.querySelector('#app')
    
  instance.mount(containerElement)
}

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}

// 导出qiankun生命周期函数
export async function bootstrap() {
  console.log('User management app bootstraped')
}

export async function mount(props) {
  console.log('User management app mount', props)
  render(props)
}

export async function unmount() {
  console.log('User management app unmount')
  instance?.unmount()
  instance = null
  router = null
}

应用间通信

// shared/eventBus.js
class EventBus {
  constructor() {
    this.events = new Map()
  }
  
  // 订阅事件
  on(event, callback) {
    if (!this.events.has(event)) {
      this.events.set(event, [])
    }
    
    this.events.get(event).push(callback)
    
    // 返回取消订阅函数
    return () => {
      const callbacks = this.events.get(event)
      if (callbacks) {
        const index = callbacks.indexOf(callback)
        if (index > -1) {
          callbacks.splice(index, 1)
        }
      }
    }
  }
  
  // 发布事件
  emit(event, ...args) {
    const callbacks = this.events.get(event)
    if (callbacks) {
      callbacks.forEach(callback => {
        try {
          callback(...args)
        } catch (error) {
          console.error(`Error in event callback for ${event}:`, error)
        }
      })
    }
  }
  
  // 一次性订阅
  once(event, callback) {
    const unsubscribe = this.on(event, (...args) => {
      unsubscribe()
      callback(...args)
    })
    
    return unsubscribe
  }
  
  // 清除所有事件
  clear() {
    this.events.clear()
  }
  
  // 清除特定事件
  off(event) {
    this.events.delete(event)
  }
}

// 全局事件总线
window.__GLOBAL_EVENT_BUS__ = window.__GLOBAL_EVENT_BUS__ || new EventBus()

export default window.__GLOBAL_EVENT_BUS__
// shared/store.js
import { reactive } from 'vue'

// 全局状态管理
class GlobalStore {
  constructor() {
    this.state = reactive({
      user: null,
      theme: 'light',
      language: 'zh-CN',
      permissions: []
    })
    
    this.subscribers = new Set()
  }
  
  // 获取状态
  getState() {
    return this.state
  }
  
  // 更新状态
  setState(updates) {
    Object.assign(this.state, updates)
    this.notifySubscribers()
  }
  
  // 订阅状态变化
  subscribe(callback) {
    this.subscribers.add(callback)
    
    return () => {
      this.subscribers.delete(callback)
    }
  }
  
  // 通知订阅者
  notifySubscribers() {
    this.subscribers.forEach(callback => {
      try {
        callback(this.state)
      } catch (error) {
        console.error('Error in state subscriber:', error)
      }
    })
  }
}

// 全局状态实例
window.__GLOBAL_STORE__ = window.__GLOBAL_STORE__ || new GlobalStore()

export default window.__GLOBAL_STORE__

本章小结

本章我们学习了Vue.js开发的最佳实践和进阶技巧:

  1. 代码规范:ESLint、Prettier、Git Hooks等工具配置
  2. 组件设计模式:高阶组件、渲染函数、组合式组件
  3. 性能优化进阶:懒加载策略、内存泄漏防护、大数据处理
  4. 微前端架构:qiankun框架的使用和应用间通信

下一章预告

下一章我们将学习Vue.js的未来发展和总结,包括: - Vue.js生态系统展望 - 学习路径建议 - 实际项目经验分享 - 持续学习资源

练习题

基础练习

  1. 代码规范

    • 配置完整的ESLint和Prettier规则
    • 设置Git Hooks自动化检查
    • 实现提交信息规范化
  2. 组件设计

    • 创建高阶组件实现权限控制
    • 使用渲染函数构建动态表格
    • 设计可复用的表单组件

进阶练习

  1. 性能优化

    • 实现组件预加载策略
    • 添加内存泄漏检测
    • 优化大数据列表渲染
  2. 微前端实践

    • 搭建微前端主应用
    • 创建独立的子应用
    • 实现应用间数据共享

提示:最佳实践需要在实际项目中不断总结和完善,要根据团队和项目特点选择合适的方案。