vue组件调用生命周期


《vue基础学习-组件》提到组件传递数据方式:

1. props/$emit

  • 父传子:子组件通过 props 显式声明 自定义 属性,接收父组件的传值。
  • 子传父:子组件通过 $emit() 显式声明 自定义 事件,父组件调用自定义事件接收子组件返回的参数。

2. $ref / $parent

  • 父传子:父组件的方法中通过 this.$refs 访问子组件的引用。
  • 子传父:子组件中通过 this.$parent.$refs 访问父组件的引用。

本节将主要介绍组件调用中涉及的生命周期钩子。

1. <componet :is=“组件名”>

在 Vue.js 中,你可以使用 <component> 元素结合 :is 属性来动态地切换组件。这种方式非常灵活,可以根据数据的变化来渲染不同的组件。

<template>
  <div>
    <button @click="currentComponent = 'ComponentA'">Show Component A</button>
    <button @click="currentComponent = 'ComponentB'">Show Component B</button>
    <component :is="currentComponent"></component>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  components: {
    ComponentA,
    ComponentB
  },
  data() {
    return {
      currentComponent: 'ComponentA'
    };
  }
};
</script>
  • <component :is="currentComponent"></component>:这里使用了 <component> 元素,并通过 :is 绑定一个变量 currentComponentcurrentComponent 的值决定了要渲染哪个组件。
  • currentComponent:这是一个响应式的数据属性,初始值为 'ComponentA'。当点击按钮时,这个值会被更新,从而触发组件的切换。

📊 假设你有两个组件 ComponentA.vueComponentB.vue

ComponentA.vue

<template>
  <div>
    <h1>Component A</h1>
    <p>This is Component A</p>
  </div>
</template>

<script>
export default {
  name: 'ComponentA'
};
</script>

ComponentB.vue

<template>
  <div>
    <h1>Component B</h1>
    <p>This is Component B</p>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  name: 'ComponentB',
  data() {
    return {
      count: 0
    };
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  },
  methods: {
    increment() {
      this.count++;
    }
  }
};
</script>

点击 “Show Component A”,加载组件 ComponentA
在这里插入图片描述

点击 “Show Component B”,加载组件 ComponentB
在这里插入图片描述

点击 “increment”,数值改变:
在这里插入图片描述

但如果点击 “Show Component A” 后,再点击 “Show Component B”,组件 ComponentB 状态没有保存:
在这里插入图片描述

如果你希望在切换组件时保留组件的状态,可以使用 <keep-alive> 包裹 <component>

2. <keep-alive>

Vue.js 中,<keep-alive> 是一个内置组件,用于缓存组件实例,以避免重复渲染和销毁。

<template>
  <div>
    <button @click="currentComponent = 'ComponentA'">Show Component A</button>
    <button @click="currentComponent = 'ComponentB'">Show Component B</button>
    <keep-alive>
      <component :is="currentComponent"></component>
    </keep-alive>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  components: {
    ComponentA,
    ComponentB
  },
  data() {
    return {
      currentComponent: 'ComponentA'
    };
  }
};
</script>

这样,即使组件被切换,它们的状态也会被保留。

点击 “Show Component A” 后,再点击 “Show Component B”,组件 ComponentB 状态不变:
在这里插入图片描述

总结:

  • 使用 <component :is="组件名"></component> 可以动态地切换组件。
  • 结合 <keep-alive> 可以保留组件的状态。
  • 通过改变 :is 绑定的变量值来控制显示哪个组件。

2.1 <keep-alive>包裹的组件怎么去除状态保留

如果你希望在某些情况下不保留组件的状态,可以通过以下几种方法来实现:

2.1.1 使用 include 和 exclude 属性

<keep-alive> 组件提供了 includeexclude 属性,可以用来控制哪些组件会被缓存。你可以根据组件的名称或组件本身来决定是否缓存。

<template>
  <div>
    <button @click="currentComponent = 'ComponentA'">Show Component A</button>
    <button @click="currentComponent = 'ComponentB'">Show Component B</button>
    <keep-alive :include="['ComponentA']" :exclude="['ComponentB']">
      <component :is="currentComponent"></component>
    </keep-alive>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  components: {
    ComponentA,
    ComponentB
  },
  data() {
    return {
      currentComponent: 'ComponentA'
    };
  }
};
</script>

2.1.2 动态控制 <keep-alive>

可以通过条件渲染来动态控制是否使用 <keep-alive>

<template>
  <div>
    <button @click="toggleComponent">Toggle Component</button>
    <keep-alive v-if="useKeepAlive">
      <component :is="currentComponent"></component>
    </keep-alive>
    <component v-else :is="currentComponent"></component>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  components: {
    ComponentA,
    ComponentB
  },
  data() {
    return {
      currentComponent: 'ComponentA',
      useKeepAlive: false
    };
  },
  methods: {
    toggleComponent() {
      if (this.currentComponent === 'ComponentA') {
        this.currentComponent = 'ComponentB';
        this.useKeepAlive = true;
      } else {
        this.currentComponent = 'ComponentA';
        this.useKeepAlive = false;
      }
    }
  }
};
</script>

但运行后,发现虽然组件 ComponentB 的方法 activateddeactivated 都执行了,但是状态并没有保留。o(╯□╰)o

查阅资料 关于动态使用keepAlive不生效的问题 发现:

通过 v-if 来判断是否生成 <keep-alive>,当列表页面符合条件时,内存缓存了组件状态。当跳转到详情页时,不符合条件,由于 v-if 是挂载到 <keep-alive> 标签上,会把之前 <keep-alive> 的列表页面进行销毁,再次进入到列表会重新创建。

✔️ 正确的使用方法:

<template>
  <div>
    <button @click="toggleComponent">Toggle Component</button>
    <keep-alive>
      <component v-if="useKeepAlive" :is="currentComponent"></component>
    </keep-alive>
    <component v-if="!useKeepAlive" :is="currentComponent"></component>
  </div>
</template>

☝️ ⚠️ ✨注:实际项目中,<keep-alive> 需要配合 vue-router 共同使用,通过增加 router.meta 属性,判定组件是否缓存。

const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../views/Home.vue'),
	meta: {
      keepAlive: true // 需要被缓存
    }
  },
  {
    path: '/',
    name: 'edit',
    component: () => import('../views/edit.vue'),
	meta: {
      keepAlive: false // 不需要被缓存
    }
  }
]
<keep-alive>
    <router-view v-if="$route.meta.keepAlive">
        <!-- 这里是会被缓存的视图组件,比如 Home! -->
    </router-view>
</keep-alive>

<router-view v-if="!$route.meta.keepAlive">
    <!-- 这里是不被缓存的视图组件,比如 Edit! -->
</router-view>

2.1.3 使用 activated 和 deactivated 生命周期钩子在组件内部控制状态

可以在组件内部使用 activateddeactivated 生命周期钩子来手动管理组件的状态。这些钩子在组件被激活和停用时调用。

例如,不想组件 ComponentB 状态被保留:

<template>
  <div>
    <h1>Component B</h1>
    <p>This is Component B</p>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  name: 'ComponentB',
  data() {
    return {
      count: 0
    };
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  },
  methods: {
    increment() {
      this.count++;
    }
  },
  activated() {
    console.log('Component activated');
    // 在这里可以恢复一些状态
  },
  deactivated() {
    console.log('Component deactivated');
    // 在这里可以清理一些状态
  }
};
</script>

3. activated()

在 Vue.js 中,activateddeactivated 是 Vue 组件的 生命周期钩子,主要用于 <keep-alive> 包裹的组件。

  • activated:当组件被 激活 时调用。这通常发生在组件从缓存中恢复并重新显示时。
  • deactivated:当组件被 停用 时调用。这通常发生在组件被隐藏并放入缓存时。

ComponentB.vue

<template>
  <div>
    <h1>Component B</h1>
    <p>This is Component B</p>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  name: 'ComponentB',
  data() {
    return {
      count: 0
    };
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  },
  methods: {
    increment() {
      this.count++;
    }
  },
  activated() {
    console.log('Component activated');
  },
  deactivated() {
    console.log('Component deactivated');
  }
};
</script>

点击 “Show Component B”,加载组件 ComponentB ,打印 Component activated
在这里插入图片描述

点击 “Show Component A”,加载组件 ComponentA,打印 Component deactivated
在这里插入图片描述

再次点击 “Show Component B”,加载组件 ComponentB ,打印 Component activated
在这里插入图片描述

注意:

  • 生命周期钩子 activateddeactivated 必须和 <keep-alive> 结合使用才生效。

4. 生命周期钩子

Vue.js 中,生命周期钩子(Lifecycle Hooks)是一些特定的函数,你可以在这些函数中执行自定义逻辑。Vue 组件从创建到销毁的过程中会经历一系列的阶段,每个阶段都有相应的钩子函数可以让你插入自定义代码。

Vue 2 和 Vue 3 的生命周期钩子有一些差异。Vue 3 引入了一些新的生命周期钩子,并对一些旧的钩子进行了重命名。

  • Vue 2 生命周期钩子
    1. 创建阶段
      • beforeCreate:在实例初始化之后,数据、事件配置之前被调用。
      • created:在实例创建完成后被调用。此时实例已完成数据、事件配置。但是挂载阶段还没开始,$el 属性目前不可见。
    2. 挂载阶段
      • beforeMount:在挂载开始之前被调用。相关的 render 函数首次被调用。
      • mounted:在实例挂载到 DOM 之后被调用。
    3. 更新阶段
      • beforeUpdate:在更新 data 数据之后,view 视图重新渲染之前触发。所以,beforeUpdateupdated 针对视图层的。
      • updatedview 视图渲染完成后触发。
      • ⚠️注1:只有 view 上面的数据变化才会触发 beforeUpdateupdated,仅属于 data 中的数据改变是并不能触发。
      • ⚠️注2:发起数据请求不能在 beforeUpdateupdated 中操作,会导致无限循环
    4. 销毁阶段
      • beforeDestroy:在实例销毁之前调用。在这一步,实例仍然完全可用。
      • destroyed:在实例销毁后调用。此时所有的事件监听器都被移除,所有的子实例也都被销毁。
    5. 其他钩子
      • activated:在 keep-alive 组件激活时调用。
      • deactivated:在 keep-alive 组件停用时调用。
      • errorCaptured:捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
  • Vue 3 生命周期钩子
    1. 创建阶段
      • setup():在 Vue 3 中,beforeCreatecreated 钩子被 setup 函数取代。
    2. 挂载阶段
      • beforeMount:在挂载开始之前被调用。
      • mounted:在实例挂载到 DOM 之后被调用。
    3. 更新阶段
      • beforeUpdate:在数据更新时调用,发生在虚拟 DOM 打补丁之前。
      • updated:在数据更新后调用,此时 DOM 已经更新。
    4. 销毁阶段
      • beforeUnmount:代替 beforeDestroy
      • unmounted:代替 destroyed
    5. 其他钩子
      • onActivated:代替 activated
      • onDeactivated :代替 deactivated
      • onErrorCaptured:代替 errorCaptured

5. 常用生命周期钩子及使用场景

5.1 created

created 钩子在实例创建完成后被调用。此时实例已完成数据、事件配置。但是挂载阶段还没开始,$el 属性目前不可见。

典型用例:

  • 初始化数据。
  • 发起网络请求获取初始数据
  • 设置事件监听器。
export default {
  created() {
    console.log('Component is created');
    this.fetchData();
  },
  methods: {
    fetchData() {
      // 发起网络请求,获取初始数据。
    }
  }
};

5.2 mounted

mounted 钩子在实例挂载到 DOM 之后被调用。

典型用例:

  • 操作 DOM,一般结合 this.$nextTick()确保子组件 DOM 已经被更新
  • 初始化第三方库(如 jQuery 插件)。
  • 发送网络请求
  • 启动定时器或动画。
export default {
  mounted() {
    console.log('Component is mounted');
    this.initThirdPartyLibrary();
  },
  methods: {
    initThirdPartyLibrary() {
      // 初始化第三方库
    }
  }
};

⚠️注1created 钩子在组件实例被创建后调用,此时组件的 datamethods 已经初始化,但 DOM 还未挂载。如果你的网络请求不需要操作 DOM,优先放在 created 中,这样可以尽早获取数据,减少用户等待时间。如果请求的结果需要操作 DOM(例如初始化图表、地图等),则放在 mounted 中。

⚠️注2:如果请求依赖于某些 动态数据(如路由参数),可以使用 watch 监听数据变化并重新发起请求。
示例:根据路由参数动态请求数据

export default {
  data() {
    return {
      post: {}
    };
  },
  created() {
    // 在 created 中发起请求
    this.fetchPost(this.$route.params.id);
  },
  watch: {
    // 监听路由参数变化,重新请求数据
    '$route.params.id': function(newId) {
      this.fetchPost(newId);
    }
  },
  methods: {
    async fetchPost(id) {
      try {
        const response = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
        this.post = response.data;
      } catch (error) {
        console.error('请求失败:', error);
      }
    }
  }
};

5.3 beforeUpdate

典型用例:

  • 在数据更新前保存当前状态。
  • 清理无效的数据或状态。
  • 如果一个组件的数据更新会影响到其他组件的状态,可以在 beforeUpdate 中发送事件通知其他组件进行相应的处理‌。

5.4 updated

典型用例:

  • 操作更新后的 DOM。
  • 基于更新后的 DOM 的操作,比如调整布局、更新样式等。
  • 结合 beforeUpdate 记录时间戳,计算视图渲染时间,进行性能测试,以便后续优化。
  • 如果组件之间需要在数据更新后进行交互,可以在 updated 钩子中发送事件或调用其他组件的方法。

5.5 beforeDestroy、beforeUnmount

典型用例:

  • 清理定时器。
  • 取消网络请求。
  • 移除事件监听器。

5.6 destroyed、unmounted

典型用例:

  • 最终清理工作。
  • 记录日志。

完整示例 Lifecycle.vue:

<template>
  <div>
    <h1 ref="count">{{ count }}</h1>
    <button @click="changeMessage">ChangeMessage</button>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'vue message', // 该数据不在视图中展示
      count: 0
    }
  },
  methods: {
    changeMessage() {
      // message 没有在视图中展示,所以,修改 message 并不会触发 beforeUpdate、updated
      this.message = 'change message';
      console.log(this.message);
    },
    increment() {
      this.count++;
    }
  },
  created() {
    console.log('+++ Component is created +++');
    console.log(this.count);
  },
  mounted() {
    console.log('+++ Component is mounted +++');
    console.log(this.count);
  },
  beforeUpdate() {
    // 视图更新前保存当前状态
    // console.log('Before update, DOM:', document.getElementsByTagName("h1")[0].textContent);
    console.log('Before update, DOM:', this.$refs.count.innerText);
    console.log('Before update, count:', this.count);
    console.log('+++ beforeUpdate called +++');
  },
  updated() {
    // 视图更新后的操作
    console.log('After update, DOM:', this.$refs.count.innerText);
    console.log('After update, count:', this.count);
    console.log('+++ updated called +++');
  },
  beforeDestroy() {
    // 清理资源
    console.log('+++ beforeDestroy called +++');
  },
  destroyed() {
    // 最终清理
    console.log('+++ destroyed called +++');
  }
};
</script>

<style scoped>
/* 添加一些样式 */
h1 {
  color: blue;
}
</style>

组件加载,触发 createdmounted
在这里插入图片描述

点击 “changeMessage”,因为message 没有在视图中展示,不会触发 beforeUpdateupdated
在这里插入图片描述

点击“increment”,触发 beforeUpdateupdated
beforeUpdate 视图渲染前,获取 DOM 视图中 count 为更新前数据,数据 data 中的 count 已经被修改,但未渲染更新到 DOM 视图中。(可以 debugger 断点调试)
updated 视图渲染完成,DOM 视图中 count 和 数据 data 中的 count 均被修改。
在这里插入图片描述

再次点击“increment”,,触发 beforeUpdateupdated
在这里插入图片描述

6. Vue事件循环机制

本来准备介绍 this.$nextTick(),在这之前先介绍下Vue 事件循环机制Vue 事件循环机制是基于 JavaScript 事件循环(Event Loop) 和 异步更新队列 实现的。Vue 利用这些机制来高效地管理 DOM 更新和响应式数据的变化。

JavaScript 事件循环可以参考之前的文章 📖vue进阶-es6语法:10.JavaScript 执行机制,这里大体介绍下:

6.1 JavaScript 事件循环

  • 事件循环的核心
    • 调用栈(Call Stack):用于执行同步代码。
    • 宏任务队列(MacroTask Queue):用于存放异步任务(如 setTimeout、Promise 等)。
    • 微任务队列(MicroTask Queue):用于存放微任务(如 Promise.then、MutationObserver等)。
  • 事件循环的执行顺序
    1. 执行同步代码(调用栈)。
    2. 执行微任务队列中的所有任务。
    3. 依次执行宏任务队列中的任务。
    4. 重复上述过程。

6.2 Vue 的异步更新队列

Vue 利用 JavaScript 的事件循环机制来实现高效的 DOM 更新。具体来说,Vue 会将数据变化引起的 DOM 更新推迟到下一个事件循环中执行,而不是立即更新。

  • 异步更新的优势
    • 批量更新:将多次数据变化合并为一次 DOM 更新,减少不必要的渲染。
    • 性能优化:避免频繁的 DOM 操作,提高性能。
  • 更新流程
    1. 当数据发生变化时,Vue 会触发依赖该数据的组件的重新渲染。
    2. Vue 不会立即更新 DOM,而是将更新任务推入一个 异步更新队列
    3. 在当前事件循环的同步代码执行完成后,Vue 会执行微任务队列中的更新任务,批量更新 DOM。

6.3 this.$nextTick()

this.$nextTick()Vue 提供的一个方法,用于在 DOM 更新完成后执行回调函数

  • 使用场景
    • 当你修改了数据后,需要操作更新后的 DOM 元素。
    • 确保某些逻辑在 DOM 更新后执行。
  • 实现原理
    • Vue 会将 this.$nextTick() 的回调函数推入微任务队列
    • 在当前事件循环的同步代码执行完成后,Vue 会先执行 DOM 更新任务,然后执行 this.$nextTick() 的回调。

示例:

this.message = '更新后的消息';
this.$nextTick(() => {
  console.log('DOM 已更新:', this.$refs.message.textContent);
});

6.4 Vue 事件循环的具体流程

以下是 Vue 事件循环的具体流程:

  1. 数据变化
    • 当你修改了 Vue 实例的响应式数据时,Vue 会标记依赖该数据的组件为“需要更新”。
  2. 推入更新队列
    • Vue 会将需要更新的组件推入一个异步更新队列。
  3. 同步代码执行
    • 继续执行当前的同步代码。
  4. 微任务阶段
    • 在当前事件循环的同步代码执行完成后,Vue 会执行微任务队列中的任务。
    • Vue 会遍历更新队列,批量更新 DOM。
  5. this.$nextTick() 回调
    • 在 DOM 更新完成后,执行 this.$nextTick() 中的回调函数。
  6. 宏任务阶段
    • 如果有宏任务(如 setTimeout),会在下一个事件循环中执行。

示例代码:

<template>
  <div>
    <p ref="message">{{ message }}</p>
    <button @click="updateMessage">更新消息</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue!'
    };
  },
  methods: {
    updateMessage() {
      // 修改数据
      this.message = '消息已更新!';

      // 同步代码
      console.log('同步代码执行');

      // 使用 $nextTick
      this.$nextTick(() => {
        console.log('DOM 已更新:', this.$refs.message.textContent);
      });

      // 微任务
      Promise.resolve().then(() => {
        console.log('微任务执行');
      });

      // 宏任务
      setTimeout(() => {
        console.log('宏任务执行');
      }, 0);
    }
  }
};
</script>

输出顺序:

同步代码执行
微任务执行
DOM 已更新: 消息已更新!
宏任务执行

6.5 小结

  • Vue 的事件循环机制基于 JavaScript 的事件循环,利用异步更新队列(微任务队列)来批量更新 DOM。
  • this.$nextTick() 是 Vue 提供的方法,用于在 DOM 更新完成后执行回调

7. this.$nextTick()

this.$nextTick() 是 Vue.js 提供的一个方法,用于在 DOM 更新完成后 执行回调函数。

Vue 是异步更新 DOM 的,当你修改了数据后,DOM 不会立即更新,而是会等到下一个“tick”(即下一次事件循环)才会更新。this.$nextTick() 可以确保你在 DOM 更新完成后执行某些操作,确保操作到最新的 DOM。

Children.vue:
<template>
  <div> {{ msg }} </div>
</template>

<script>
export default {
  props: ['msg']
};
</script>

NextTick.vue:
<template>
  <div>
    <Children ref="children" :msg="message"></Children>
    <button @click="updateMessage">updateMessage</button>
  </div>
</template>

<script>
import Children from "./Children.vue";

export default {
  components: {
    Children
  },
  data() {
    return {
      message: "Hello",
    };
  },
  methods: {
    updateMessage() {
      this.message = "Hello, World";
      console.log("111");
      console.log(this.$refs.children.$el.textContent); // Hello
      this.$nextTick(() => {
        console.log("222");
        console.log(this.$refs.children.$el.textContent); // Hello, World
      });
      console.log("333");
    },
  },
};
</script>

在这里插入图片描述

结果可见,this.$nextTick() 内回调函数是异步执行的,所以先打印 “333”, 再打印 “222”。而且,等子组件视图更新结束之后,才执行 this.$nextTick() 内回调函数,保证操作到最新的 DOM

⚠️ 注意:虽然 this.$nextTick() 方法可以解决异步更新导致的问题,但如果过度使用该方法会导致性能问题。因此,在实际开发中,只有在必要的情况下才应该使用 $nextTick() 方法。

在 Vue 2.2.0+ 中,this.$nextTick() 返回一个 Promise,因此你可以用 async/await 语法:

async updateMessage() {
  this.message = '消息已更新!';
  await this.$nextTick();
  console.log('DOM 已更新:', this.$refs.message.textContent);
}

7.1 常见应用场景

7.1.1 获取更新后的 DOM 内容

当你修改了数据后,Vue 会异步更新 DOM。如果你需要操作更新后的 DOM 元素,可以使用 this.$nextTick()

<template>
  <div>
    <p ref="message">{{ message }}</p>
    <button @click="updateMessage">更新消息</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue!'
    };
  },
  methods: {
    updateMessage() {
      this.message = '消息已更新!';
      this.$nextTick(() => {
        // 确保 DOM 已更新
        console.log('更新后的内容:', this.$refs.message.textContent); // 输出: 消息已更新!
      });
    }
  }
};
</script>

7.1.2 初始化第三方库

某些第三方库(如图表库、地图库等)需要在 DOM 渲染完成后初始化。使用 this.$nextTick() 可以确保 DOM 已经准备好。

<template>
  <div ref="chart" style="width: 100%; height: 400px;"></div>
</template>

<script>
import * as echarts from 'echarts';

export default {
  mounted() {
    this.$nextTick(() => {
      // 确保 DOM 已渲染
      const chart = echarts.init(this.$refs.chart);
      chart.setOption({
        title: {
          text: '示例图表'
        },
        xAxis: {
          type: 'category',
          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        },
        yAxis: {
          type: 'value'
        },
        series: [
          {
            data: [120, 200, 150, 80, 70, 110, 130],
            type: 'bar'
          }
        ]
      });
    });
  }
};
</script>

7.1.3 表单验证或焦点管理

在表单验证或焦点管理中,可能需要等待 DOM 更新完成后才能进行操作。

<template>
  <div>
    <input ref="input" v-model="inputValue" />
    <button @click="validateAndFocus">验证并聚焦</button>
    <p v-if="error" style="color: red;">{{ error }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      inputValue: '',
      error: ''
    };
  },
  methods: {
    validateAndFocus() {
      if (!this.inputValue) {
        this.error = '输入不能为空';
        this.$nextTick(() => {
          // 确保错误信息已显示,然后聚焦输入框
          this.$refs.input.focus();
        });
      } else {
        this.error = '';
      }
    }
  }
};
</script>

7.1.4 动态组件或路由切换

在动态组件或路由切换时,可能需要等待新组件渲染完成后再执行某些操作。

<template>
  <div>
    <router-view ref="routerView"></router-view>
  </div>
</template>

<script>
export default {
  watch: {
    '$route'() {
      this.$nextTick(() => {
        // 确保新路由的组件已渲染
        this.$refs.routerView.$el.scrollIntoView();
      });
    }
  }
};
</script>

7.1.5 与 v-if 或 v-show 结合使用

当使用 v-if 或 v-show 控制元素的显示时,可能需要等待元素渲染完成后再执行操作。

<template>
  <div>
    <button @click="toggle">切换显示</button>
    <div v-if="isVisible" ref="content">
      这是动态显示的内容
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isVisible: false
    };
  },
  methods: {
    toggle() {
      this.isVisible = !this.isVisible;
      this.$nextTick(() => {
        // 确保 DOM 已更新
        if (this.isVisible) {
          this.$refs.content.style.color = 'red';
        }
      });
    }
  }
};
</script>

总之,合理使用 this.$nextTick(),可以确保在 DOM 更新完成后执行逻辑,避免因 DOM 未更新而导致的问题

7.2 与 updated 区别

  • this.$nextTick:在当前 DOM 更新周期结束后执行回调函数。适用于需要在数据变化后立即访问更新后的 DOM 的情况。
  • updated 生命周期钩子:在组件因为数据变化而重新渲染后调用。适用于在组件更新后执行某些操作的情况。

选择哪种方式取决于你的具体需求:

  • 如果你需要在特定的数据变化后立即执行某些操作,使用 this.$nextTick
  • 如果你需要在组件每次更新后执行某些操作,使用 updated 生命周期钩子。

7.3 与 process.nextTick 区别

process.nextTickNode.js 中的一个方法,用于将回调函数推迟到 当前事件循环的末尾 执行。

使用场景:

  • 延迟执行:在当前事件循环结束后立即执行某些操作。
  • 确保异步顺序:在某些情况下,确保某些操作在当前事件循环的其他操作之后执行。
console.log('开始');

process.nextTick(() => {
  console.log('nextTick 回调');
});

console.log('结束');

// 输出顺序:
// 开始
// 结束
// nextTick 回调

this.$nextTick() 用于 Vue.js 中,确保在 DOM 更新后执行某些操作。process.nextTick 用于 Node.js 中,将回调函数推迟到当前事件循环的末尾执行。

7.4 应用

ajax 获取了左侧菜单的列表树,在 ajaxsuccess 方法中更新菜单数的数据源 menuTreeListData,更新数据源后,希望能 高亮 id=5 的菜单。
如果这样写:

success: (resp) => {
  this.menuTreeListData = resp.data
  this.hightlightNode(5)
}

这样不一定能达到你要的效果,因为高亮操作的时候,数据源虽然更新了,但是 DOM 有可能还没渲染完成,这时候你操作高亮 DOM 是找不到的。

这时,就需要用 this.nextTick()

success: (resp) => {
  this.menuTreeListData = resp.data;
  let _this = this;
  this.nextTick(function(){
    _this.hightlightNode(5);
  });
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会叫的狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值