问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Vue 2 生命周期详解与性能优化指南

创作时间:
作者:
@小白创作中心

Vue 2 生命周期详解与性能优化指南

引用
CSDN
1.
https://blog.csdn.net/qq_74114417/article/details/145628400

Vue 2的生命周期管理是前端开发中的重要知识点。本文详细介绍了Vue 2的生命周期流程、各个阶段的钩子函数及其应用场景,并提供了具体的优化策略,帮助开发者编写更高效、稳定的Vue应用。

Vue 2 生命周期

Vue 2 的生命周期指的是 Vue 实例从创建、挂载到页面、数据更新、销毁的整个过程。在这个过程中,Vue 提供了一系列的钩子函数,允许开发者在特定的时间点插入自己的代码逻辑,从而实现对组件的精确控制。

1. 官方图示

2. 生命周期阶段及钩子函数

⭐️表示常用

2.1 创建阶段

在这个阶段,Vue 实例正在被创建,主要进行数据观测、property 和 method 的计算、watch/event 事件回调的配置等工作。

  • beforeCreate

  • 触发时间:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

  • 使用场景:此时 data 和 methods 还未初始化,通常在此阶段进行一些与实例创建无关的全局配置或初始化操作,如加载外部库等。

new Vue({
    beforeCreate() {
        console.log('实例初始化,data 和 methods 未可用');
    }
});
  • created
    ⭐️
  • 触发时间:实例已经创建完成之后被调用。在这一步,实例已经完成了数据观测 (data observer)、property 和 method 的计算、watch/event 事件回调的配置等。然而,挂载阶段还没有开始,$el 属性目前不可用。
  • 使用场景:可以在这个阶段进行数据的初始化操作,如发起网络请求获取初始数据。
new Vue({
    data() {
        return {
            userData: null
        };
    },
    created() {
        // 模拟异步请求
        fetch('https://api.example.com/user')
          .then(response => response.json())
          .then(data => {
                this.userData = data;
            });
    }
});

2.2 挂载阶段

这个阶段主要涉及到将 Vue 实例挂载到 DOM 上。

  • beforeMount

  • 触发时间:在挂载开始之前被调用,相关的 render 函数首次被调用。此时模板已经编译完成,但还未挂载到页面上。

  • 使用场景:可以在这个阶段对模板进行一些最后的修改或处理。

new Vue({
    template: '<div>{{ message }}</div>',
    data() {
        return {
            message: 'Hello'
        };
    },
    beforeMount() {
        console.log('模板编译完成,即将挂载到页面');
    }
});
  • mounted
    ⭐️
  • 触发时间:el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。此时模板已经成功挂载到页面上,可以访问 DOM 元素。
  • 使用场景:可以在这个阶段对模板进行一些最后的修改或处理。
new Vue({
    template: '<div id="app">{{ message }}</div>',
    data() {
        return {
            message: 'Hello'
        };
    },
    mounted() {
        const appElement = document.getElementById('app');
        console.log('DOM 已挂载:', appElement.textContent);
    }
});

2.3 更新阶段

当响应式数据发生变化时,会触发更新阶段。

  • beforeUpdate

  • 触发时间:在数据更新之前被调用,发生在虚拟 DOM 打补丁之前。此时数据已经发生变化,但 DOM 还未更新。

  • 使用场景:可以在这个阶段获取数据更新前的 DOM 状态,进行一些数据备份或其他操作。

new Vue({
    data() {
        return {
            count: 0
        };
    },
    beforeUpdate() {
        console.log('数据即将更新,当前 count:', this.count);
    },
    methods: {
        increment() {
            this.count++;
        }
    }
});
  • updated
    ⭐️
  • 触发时间:在由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。此时数据和 DOM 都已经更新完成。
  • 使用场景:可以在这个阶段操作更新后的 DOM,如根据新的数据更新第三方插件的状态。
new Vue({
    data() {
        return {
            count: 0
        };
    },
    updated() {
        console.log('数据和 DOM 已更新,当前 count:', this.count);
    },
    methods: {
        increment() {
            this.count++;
        }
    }
});

2.4 销毁阶段

当调用 vm.$destroy() 方法时,会触发销毁阶段。

  • beforeDestroy
    ⭐️
  • 触发时间:在实例销毁之前调用。此时实例仍然完全可用。
  • 使用场景:可以在这个阶段进行一些清理工作,如解绑自定义事件、清除定时器等。
new Vue({
    data() {
        return {
            intervalId: null
        };
    },
    created() {
        this.intervalId = setInterval(() => {
            console.log('定时器执行');
        }, 1000);
    },
    beforeDestroy() {
        clearInterval(this.intervalId);
        console.log('定时器已清除');
    }
});
  • destroyed

  • 触发时间:在实例销毁之后调用。所有的事件监听器和子实例都已经被销毁。

  • 使用场景:通常用于确认实例已经完全销毁,可进行一些最后的日志记录等操作。

new Vue({
    destroyed() {
        console.log('实例已销毁');
    }
});

3. 特殊阶段

3.1 适用于包裹的组件

  • activated

  • 触发时间:被缓存的组件激活时调用

  • 使用场景:可以在这个阶段恢复组件的状态,如重新发起网络请求获取最新数据。

// 这是被<keep-alive>包裹的组件
<template>
  <div>
    <!-- 组件内容 -->
  </div>
</template>
<script>
export default {
  activated() {
    console.log('组件被激活');
    // 可以在这里重新发起请求获取最新数据
  }
};
</script>
  • deactivated

  • 触发时间:被缓存的组件停用时调用。

  • 使用场景:可以在这个阶段暂停一些不必要的操作,如停止定时器。

<template>
  <div>
    <!-- 组件内容 -->
  </div>
</template>
<script>
export default {
  data() {
    return {
      timer: null
    };
  },
  activated() {
    this.timer = setInterval(() => {
      console.log('定时器运行');
    }, 1000);
  },
  deactivated() {
    clearInterval(this.timer);
    console.log('定时器停止');
  }
};
</script>

3.2 错误捕获阶段

  • errorCaptured

  • 触发时机:当捕获一个来自子孙组件的错误时被调用。

  • 理解要点:可以在这个阶段记录错误信息,显示错误提示等,big可以通过返回false阻止错误继续向上传播

  • 示例代码:

new Vue({
  errorCaptured(err, vm, info) {
    console.error('捕获到错误:'err, '来自组件:',vm ,'建议信息:' info)
    return false //阻止错误继续向上传播
  }
})

4. 如何优化Vue组件的生命周期性能?

优化 Vue 组件的生命周期性能可以从多个方面入手,下面将结合不同生命周期钩子详细介绍具体的优化策略。

4.1 创建阶段(beforeCreate 和 created)

(1) 减少不必要的初始化操作

在 created 钩子中,避免进行大量耗时的初始化操作,尤其是那些可能阻塞主线程的操作。如果有异步操作,尽量使用异步方式处理,避免同步阻塞。

<template>
  <div>{{ userData }}</div>
</template>
<script>
export default {
  data() {
    return {
      userData: null
    };
  },
  created() {
    // 使用异步请求获取数据
    this.fetchUserData();
  },
  methods: {
    async fetchUserData() {
      try {
        const response = await fetch('https://api.example.com/user');
        const data = await response.json();
        this.userData = data;
      } catch (error) {
        console.error('获取用户数据出错:', error);
      }
    }
  }
};
</script>

(2) 避免重复的初始化代码

确保在 created 钩子中不会重复执行相同的初始化代码。如果有多个组件需要进行相同的初始化操作,可以将这些操作封装成一个函数或混入(mixin)。

// 封装初始化函数
function initUserData(vm) {
  vm.fetchUserData();
}
export default {
  created() {
    initUserData(this);
  },
  methods: {
    async fetchUserData() {
      // ...
    }
  }
};

4.2 挂载阶段(beforeMount 和 mounted)

(1) 减少DOM 操作

在 mounted 钩子中,尽量减少对 DOM 的频繁操作,因为 DOM 操作是比较耗时的。如果需要对 DOM 进行多次操作,可以考虑先在内存中进行计算,最后一次性更新到 DOM 上。

<template>
  <div id="list"></div>
</template>
<script>
export default {
  mounted() {
    const list = document.getElementById('list');
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 100; i++) {
      const item = document.createElement('div');
      item.textContent = `Item ${i}`;
      fragment.appendChild(item);
    }
    list.appendChild(fragment);
  }
};
</script>

(2) 合理使用第三方插件

如果在 mounted 钩子中初始化第三方插件,确保只在必要时进行初始化,避免不必要的资源消耗。同时,在组件销毁时,及时销毁这些插件以释放资源。

<template>
 <div id="chart"></div>
</template>
<script>
import Chart from 'chart.js';
export default {
 data() {
   return {
     chart: null
   };
 },
 mounted() {
   const ctx = document.getElementById('chart').getContext('2d');
   this.chart = new Chart(ctx, {
     // 配置选项
   });
 },
 beforeDestroy() {
   if (this.chart) {
     this.chart.destroy();
   }
 }
};
</script>

4.3 更新阶段(beforeUpdate 和 updated)

(1) 避免不必要的更新

使用 Vue 的 shouldComponentUpdate 方法(在 Vue 2 中可以通过 Vue.mixin 实现类似功能)来控制组件是否需要更新。只有当数据发生真正有意义的变化时,才触发组件的更新。

Vue.mixin({
 shouldComponentUpdate(nextProps, nextState) {
   // 比较前后数据是否有变化
   return !_.isEqual(this.$data, nextProps.data);
 }
});

(2) 优化计算属性和监听器

对于计算属性和监听器,确保它们的计算逻辑尽可能简单,避免复杂的计算和不必要的副作用。同时,合理使用缓存机制,减少重复计算。

<template>
 <div>{{ fullName }}</div>
</template>
<script>
export default {
 data() {
   return {
     firstName: 'John',
     lastName: 'Doe'
   };
 },
 computed: {
   fullName() {
     return `${this.firstName} ${this.lastName}`;
   }
 }
};
</script>

4.4 销毁阶段(beforeDestroy 和 destroyed)

(1) 清理定时器和事件监听器

在 beforeDestroy 钩子中,确保清理所有在组件中创建的定时器和事件监听器,避免内存泄漏。

<template>
 <div></div>
</template>
<script>
export default {
 data() {
   return {
     intervalId: null
   };
 },
 created() {
   this.intervalId = setInterval(() => {
     console.log('定时器执行');
   }, 1000);
 },
 beforeDestroy() {
   clearInterval(this.intervalId);
 }
};
</script>

(2) 释放第三方资源

如果在组件中使用了第三方资源,如网络连接、数据库连接等,在组件销毁时及时释放这些资源。

<template>
 <div></div>
</template>
<script>
import { connect } from 'some-database-library';
export default {
 data() {
   return {
     dbConnection: null
   };
 },
 created() {
   this.dbConnection = connect('database-url');
 },
 beforeDestroy() {
   if (this.dbConnection) {
     this.dbConnection.close();
   }
 }
};
</script>

4.5 其他优化策略

(1) 使用v-once指令

对于那些不需要动态更新的 DOM 元素,可以使用 v-once 指令,这样 Vue 只会渲染一次这些元素,之后不会再进行更新,从而提高性能。

(2) 使用 v-if 替代 v-show

如果某些元素在大部分时间内是隐藏的,使用 v-if 而不是 v-show。因为 v-if 会根据条件动态地创建和销毁元素,而 v-show 只是通过 CSS 的 display 属性来控制元素的显示和隐藏,即使元素隐藏了,仍然会占用 DOM 资源。

5. 总结

Vue 2 的生命周期钩子函数为开发者提供了丰富的控制能力,通过在不同的生命周期阶段执行相应的代码逻辑,可以更好地管理组件的状态、操作 DOM 元素、处理异步请求和进行资源清理等工作。理解这些生命周期钩子的触发时机和用途,有助于编写更高效、稳定的 Vue 应用。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号