Skip to content
On this page

Vue.js 生命周期

Vue.js 组件生命周期是指组件从创建到销毁的整个过程。理解生命周期钩子函数可以帮助我们在特定的时间点执行相应的逻辑。

选项式 API 生命周期

挂载阶段

vue
<template>
  <div>
    <h2>生命周期演示</h2>
    <p>计数器: {{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
export default {
  name: 'LifecycleDemo',
  data() {
    return {
      count: 0
    }
  },
  
  beforeCreate() {
    console.log('1. beforeCreate: 实例初始化之后,数据观测和事件配置之前')
    // 此时无法访问 data、methods、computed 等
    console.log('Data is not available yet:', this.count) // undefined
  },
  
  created() {
    console.log('2. created: 实例创建完成,数据观测、属性和方法的运算已完成')
    console.log('Data is now available:', this.count) // 0
    // 通常在这里进行数据获取
    this.fetchData()
  },
  
  beforeMount() {
    console.log('3. beforeMount: 挂载开始之前被调用')
    console.log('Mounting element:', this.$el) // 还未挂载,所以是 undefined
  },
  
  mounted() {
    console.log('4. mounted: 实例被挂载后调用')
    console.log('Mounted element:', this.$el) // 现在可以访问DOM元素
    // 通常在这里进行DOM操作、启动定时器、订阅事件等
    this.startTimer()
  },
  
  methods: {
    increment() {
      this.count++
    },
    
    fetchData() {
      console.log('在 created 钩子中获取数据')
    },
    
    startTimer() {
      console.log('在 mounted 钩子中启动定时器')
    }
  }
}
</script>

更新阶段

vue
<template>
  <div>
    <h2>更新生命周期</h2>
    <p>计数器: {{ count }}</p>
    <p>时间: {{ time }}</p>
    <button @click="increment">增加</button>
    <button @click="updateTime">更新时间</button>
  </div>
</template>

<script>
export default {
  name: 'UpdateLifecycle',
  data() {
    return {
      count: 0,
      time: new Date().toLocaleTimeString()
    }
  },
  
  beforeUpdate() {
    console.log('beforeUpdate: 数据更新时调用,发生在虚拟DOM打补丁之前')
    console.log('Count before update:', this.count)
    // 此时DOM还未更新
  },
  
  updated() {
    console.log('updated: 数据更新后,虚拟DOM重新渲染和打补丁之后调用')
    console.log('Count after update:', this.count)
    // 此时DOM已经更新,可以访问更新后的DOM
    console.log('DOM updated, current count:', this.count)
  },
  
  methods: {
    increment() {
      this.count++
    },
    
    updateTime() {
      this.time = new Date().toLocaleTimeString()
    }
  }
}
</script>

卸载阶段

vue
<template>
  <div>
    <h2>卸载生命周期</h2>
    <p>计数器: {{ count }}</p>
    <button @click="increment">增加</button>
    <button @click="removeComponent">移除组件</button>
  </div>
</template>

<script>
export default {
  name: 'UnmountLifecycle',
  data() {
    return {
      count: 0,
      timer: null
    }
  },
  
  mounted() {
    // 设置定时器
    this.timer = setInterval(() => {
      this.count++
    }, 1000)
  },
  
  beforeUnmount() {
    console.log('beforeUnmount: 卸载组件实例之前调用')
    // 清理工作:清除定时器、取消订阅、移除事件监听器等
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
      console.log('定时器已清除')
    }
  },
  
  unmounted() {
    console.log('unmounted: 卸载组件实例后调用')
    // 组件已完全销毁
  },
  
  methods: {
    increment() {
      this.count++
    },
    
    removeComponent() {
      // 通过 v-if 控制组件显示/隐藏来演示卸载
      this.$emit('remove')
    }
  }
}
</script>

组合式 API 生命周期

使用组合式 API

vue
<template>
  <div>
    <h2>组合式 API 生命周期</h2>
    <p>计数器: {{ count }}</p>
    <p>状态: {{ status }}</p>
    <button @click="increment">增加</button>
    <button @click="removeComponent">移除组件</button>
  </div>
</template>

<script>
import {
  ref,
  onMounted,
  onUpdated,
  onUnmounted,
  onBeforeMount,
  onBeforeUpdate,
  onBeforeUnmount,
  getCurrentInstance
} from 'vue'

export default {
  name: 'ComposableLifecycle',
  emits: ['remove'],
  
  setup(props, { emit }) {
    const count = ref(0)
    const status = ref('active')
    let timer = null
    
    // beforeCreate 和 created 在 setup 函数执行时发生
    console.log('setup: 组件实例创建前')
    
    // 挂载前
    onBeforeMount(() => {
      console.log('onBeforeMount: 组件挂载前')
    })
    
    // 挂载后
    onMounted(() => {
      console.log('onMounted: 组件已挂载')
      // 启动定时器
      timer = setInterval(() => {
        count.value++
      }, 1000)
    })
    
    // 更新前
    onBeforeUpdate(() => {
      console.log('onBeforeUpdate: 组件更新前')
    })
    
    // 更新后
    onUpdated(() => {
      console.log('onUpdated: 组件更新后')
    })
    
    // 卸载前
    onBeforeUnmount(() => {
      console.log('onBeforeUnmount: 组件卸载前')
      // 清理定时器
      if (timer) {
        clearInterval(timer)
        timer = null
        console.log('定时器已清除')
      }
    })
    
    // 卸载后
    onUnmounted(() => {
      console.log('onUnmounted: 组件已卸载')
    })
    
    const increment = () => {
      count.value++
    }
    
    const removeComponent = () => {
      emit('remove')
    }
    
    return {
      count,
      status,
      increment,
      removeComponent
    }
  }
}
</script>

完整生命周期示例

vue
<template>
  <div class="lifecycle-container">
    <h2>完整生命周期示例</h2>
    <p>状态: {{ status }}</p>
    <p>数据: {{ data }}</p>
    <p>计数: {{ count }}</p>
    
    <div class="controls">
      <button @click="updateData">更新数据</button>
      <button @click="toggleComponent" v-if="!isDestroyed">
        {{ isVisible ? '隐藏组件' : '显示组件' }}
      </button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'FullLifecycleDemo',
  props: {
    initialData: {
      type: String,
      default: '初始数据'
    }
  },
  
  data() {
    return {
      status: 'created',
      data: this.initialData,
      count: 0,
      isVisible: true,
      isDestroyed: false,
      timer: null,
      observer: null
    }
  },
  
  // 1. 实例创建前
  beforeCreate() {
    console.log('→ beforeCreate: 实例创建前')
    console.log('  - props:', this.$options.propsData)
    console.log('  - data:', this.data) // undefined
  },
  
  // 2. 实例创建后
  created() {
    console.log('→ created: 实例创建后')
    console.log('  - data is available:', this.data)
    console.log('  - status:', this.status)
    
    // 进行初始数据获取
    this.fetchInitialData()
    
    // 设置响应式更新
    this.startReactiveUpdates()
  },
  
  // 3. 挂载前
  beforeMount() {
    console.log('→ beforeMount: 挂载前')
    console.log('  - $el is available:', !!this.$el) // false
  },
  
  // 4. 挂载后
  mounted() {
    console.log('→ mounted: 挂载后')
    console.log('  - $el is available:', !!this.$el) // true
    console.log('  - actual DOM:', this.$el)
    
    this.status = 'mounted'
    
    // 启动定时器
    this.startTimer()
    
    // 设置DOM观察器
    this.setupDOMObserver()
  },
  
  // 5. 更新前
  beforeUpdate() {
    console.log('→ beforeUpdate: 更新前')
    console.log('  - current count:', this.count)
    console.log('  - current data:', this.data)
  },
  
  // 6. 更新后
  updated() {
    console.log('→ updated: 更新后')
    console.log('  - updated count:', this.count)
    console.log('  - updated data:', this.data)
  },
  
  // 7. 卸载前
  beforeUnmount() {
    console.log('→ beforeUnmount: 卸载前')
    
    // 清理工作
    this.cleanup()
    this.status = 'unmounting'
  },
  
  // 8. 卸载后
  unmounted() {
    console.log('→ unmounted: 卸载后')
    this.status = 'destroyed'
    this.isDestroyed = true
  },
  
  methods: {
    fetchInitialData() {
      console.log('  - 获取初始数据...')
      // 模拟API调用
      setTimeout(() => {
        this.data = '已获取的数据'
      }, 1000)
    },
    
    startReactiveUpdates() {
      // 设置响应式数据更新
      this.count = 0
    },
    
    startTimer() {
      this.timer = setInterval(() => {
        this.count++
      }, 1000)
    },
    
    setupDOMObserver() {
      // 观察DOM变化
      if (window.MutationObserver && this.$el) {
        this.observer = new MutationObserver((mutations) => {
          mutations.forEach((mutation) => {
            console.log('DOM变化:', mutation.type)
          })
        })
        
        this.observer.observe(this.$el, {
          childList: true,
          subtree: true
        })
      }
    },
    
    updateData() {
      this.data = `更新的数据 ${Date.now()}`
    },
    
    toggleComponent() {
      this.isVisible = !this.isVisible
      if (!this.isVisible) {
        // 触发组件卸载
        this.$emit('toggle', false)
      } else {
        this.$emit('toggle', true)
      }
    },
    
    cleanup() {
      // 清除定时器
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
        console.log('  - 定时器已清除')
      }
      
      // 断开DOM观察器
      if (this.observer) {
        this.observer.disconnect()
        this.observer = null
        console.log('  - DOM观察器已断开')
      }
      
      console.log('  - 清理工作完成')
    }
  },
  
  // 错误处理
  errorCaptured(err, instance, info) {
    console.error('捕获到子组件错误:', err)
    console.error('错误信息:', info)
    return false // 阻止错误继续传播
  }
}
</script>

<style scoped>
.lifecycle-container {
  border: 1px solid #ddd;
  padding: 16px;
  margin: 8px 0;
  border-radius: 4px;
}

.controls {
  margin-top: 16px;
}

button {
  margin-right: 8px;
  padding: 6px 12px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}
</style>

特殊生命周期场景

keep-alive 组件生命周期

vue
<template>
  <div>
    <h2>Keep-alive 组件</h2>
    <p>激活次数: {{ activatedCount }}</p>
    <p>停用次数: {{ deactivatedCount }}</p>
    <p>当前时间: {{ currentTime }}</p>
  </div>
</template>

<script>
export default {
  name: 'KeepAliveComponent',
  data() {
    return {
      activatedCount: 0,
      deactivatedCount: 0,
      currentTime: new Date().toLocaleTimeString()
    }
  },
  
  created() {
    console.log('KeepAliveComponent created')
  },
  
  mounted() {
    console.log('KeepAliveComponent mounted')
    this.updateTime()
  },
  
  // keep-alive 激活时调用
  activated() {
    console.log('KeepAliveComponent activated')
    this.activatedCount++
    this.updateTime()
    this.startTimer()
  },
  
  // keep-alive 停用时调用
  deactivated() {
    console.log('KeepAliveComponent deactivated')
    this.deactivatedCount++
    this.stopTimer()
  },
  
  unmounted() {
    console.log('KeepAliveComponent unmounted')
  },
  
  methods: {
    updateTime() {
      this.currentTime = new Date().toLocaleTimeString()
    },
    
    startTimer() {
      this.timer = setInterval(() => {
        this.currentTime = new Date().toLocaleTimeString()
      }, 1000)
    },
    
    stopTimer() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
    }
  }
}
</script>

错误处理生命周期

vue
<template>
  <div>
    <h2>错误处理示例</h2>
    <button @click="triggerError">触发错误</button>
    <div v-if="hasError" class="error">
      发生错误: {{ errorMessage }}
    </div>
  </div>
</template>

<script>
export default {
  name: 'ErrorHandlingDemo',
  data() {
    return {
      hasError: false,
      errorMessage: ''
    }
  },
  
  errorCaptured(err, instance, info) {
    console.error('组件错误捕获:', err)
    console.error('错误信息:', info)
    
    this.hasError = true
    this.errorMessage = err.message
    
    // 返回 false 来阻止错误继续向上传播
    return false
  },
  
  methods: {
    triggerError() {
      // 故意触发一个错误
      throw new Error('演示错误')
    }
  }
}
</script>

<style scoped>
.error {
  color: red;
  padding: 8px;
  background-color: #ffe6e6;
  border: 1px solid red;
  border-radius: 4px;
  margin-top: 8px;
}
</style>

生命周期最佳实践

资源清理

vue
<template>
  <div>
    <h2>资源清理示例</h2>
    <p>WebSocket 状态: {{ wsStatus }}</p>
    <p>定时器计数: {{ timerCount }}</p>
    <button @click="sendMessage">发送消息</button>
  </div>
</template>

<script>
export default {
  name: 'ResourceCleanupDemo',
  data() {
    return {
      wsStatus: 'disconnected',
      timerCount: 0,
      websocket: null,
      timer: null
    }
  },
  
  async mounted() {
    try {
      // 建立WebSocket连接
      this.websocket = new WebSocket('ws://localhost:8080')
      
      this.websocket.onopen = () => {
        this.wsStatus = 'connected'
      }
      
      this.websocket.onmessage = (event) => {
        console.log('收到消息:', event.data)
      }
      
      this.websocket.onerror = (error) => {
        console.error('WebSocket错误:', error)
        this.wsStatus = 'error'
      }
      
      this.websocket.onclose = () => {
        this.wsStatus = 'disconnected'
      }
      
      // 启动定时器
      this.timer = setInterval(() => {
        this.timerCount++
      }, 1000)
      
    } catch (error) {
      console.error('初始化失败:', error)
    }
  },
  
  beforeUnmount() {
    // 清理WebSocket连接
    if (this.websocket) {
      this.websocket.close()
      this.websocket = null
    }
    
    // 清理定时器
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
    
    console.log('资源清理完成')
  },
  
  methods: {
    sendMessage() {
      if (this.websocket && this.wsStatus === 'connected') {
        this.websocket.send('Hello WebSocket!')
      }
    }
  }
}
</script>

性能优化

vue
<template>
  <div>
    <h2>性能优化示例</h2>
    <p>渲染次数: {{ renderCount }}</p>
    <p>计算属性执行次数: {{ computedExecutions }}</p>
    <button @click="updateData">更新数据</button>
  </div>
</template>

<script>
import { computed } from 'vue'

export default {
  name: 'PerformanceOptimization',
  data() {
    return {
      renderCount: 0,
      computedExecutions: 0,
      value1: 0,
      value2: 0,
      shouldCalculate: true
    }
  },
  
  created() {
    // 避免在created中进行复杂的计算
    this.initializeData()
  },
  
  mounted() {
    // DOM相关的操作在mounted中进行
    this.setupPerformanceMonitoring()
  },
  
  computed: {
    expensiveCalculation() {
      // 只有在依赖变化时才重新计算
      this.computedExecutions++
      
      if (!this.shouldCalculate) {
        return 0
      }
      
      // 模拟复杂计算
      let result = 0
      for (let i = 0; i < 100000; i++) {
        result += Math.sqrt(i) * this.value1 * this.value2
      }
      return result
    }
  },
  
  updated() {
    this.renderCount++
  },
  
  methods: {
    initializeData() {
      // 初始化数据,避免在created中进行复杂操作
      this.value1 = Math.random()
      this.value2 = Math.random()
    },
    
    setupPerformanceMonitoring() {
      // 性能监控设置
      console.log('性能监控已设置')
    },
    
    updateData() {
      this.value1 = Math.random()
      this.value2 = Math.random()
    }
  }
}
</script>

Vue.js 的生命周期提供了在组件不同阶段执行逻辑的机会,正确使用生命周期钩子可以确保组件的正常运行和资源的有效管理。