Sử dụng this.$nextTick trong Vue.js để đồng bộ hóa thao tác DOM

this.$nextTick() là một phương thức trong Vue.js giúp trì hoãn việc thực thi callback cho đến khi chu kỳ cập nhật DOM tiếp theo hoàn tất. Khi bạn thay đổi trạng thái (state) hoặc dữ liệu phản ứng (reactive data), Vue sẽ lập lịch cập nhật DOM một cách bất đồng bộ — this.$nextTick() cho phép bạn đăng ký một hàm để chạy ngay sau khi DOM đã được render lại, đảm bảo các tham chiếu DOM (ví dụ: ref) phản ánh đúng nội dung mới.

Ví dụ 1: Thứ tự thực thi giữa các hook và DOM

Dưới đây là một thành phần Vue minh họa hành vi của this.$nextTick() trong các giai đoạn vòng đời:

<template>
  <section>
    <div ref="container">
      <p>Vue App</p>
    </div>
    <button @click="triggerUpdate">Cập nhật giá trị</button>
  </section>
</template>

<script>
export default {
  data() {
    return {
      message: 'Ban đầu'
    };
  },
  created() {
    console.log('[created] Bước 1: Ref chưa tồn tại →', this.$refs.container); // null
    this.$nextTick(() => {
      console.log('[created + nextTick] Bước 2: DOM đã sẵn sàng →', this.$refs.container);
    });
  },
  mounted() {
    console.log('[mounted] Bước 3: DOM chắc chắn đã gắn →', this.$refs.container);
    this.$nextTick(() => {
      console.log('[mounted + nextTick] Bước 4: Đảm bảo cập nhật cuối cùng');
    });
  },
  methods: {
    triggerUpdate() {
      this.message = 'Đã thay đổi';
      console.log('[trước nextTick] Nội dung DOM chưa cập nhật:', this.$refs.container?.textContent);
      this.$nextTick(() => {
        console.log('[sau nextTick] Nội dung DOM đã cập nhật:', this.$refs.container?.textContent);
      });
    }
  }
};
</script>

Kết quả in ra console sẽ lần lượt là:

  1. [created] Bước 1: Ref chưa tồn tại → null
  2. [created + nextTick] Bước 2: DOM đã sẵn sàng → [object HTMLDivElement] (chỉ khi component đã được gắn vào DOM)
  3. [mounted] Bước 3: DOM chắc chắn đã gắn → [object HTMLDivElement]
  4. [mounted + nextTick] Bước 4: Đảm bảo cập nhật cuối cùng
  5. Khi nhấn nút: [trước nextTick] Nội dung DOM chưa cập nhật: Vue App
  6. Sau đó: [sau nextTick] Nội dung DOM đã cập nhật: Đã thay đổi

Ví dụ 2: Xử lý dữ liệu động từ API

Một tình huống thực tế thường gặp: tải dữ liệu từ backend, gán vào state, rồi cần đo kích thước hoặc focus vào phần tử mới được render:

<template>
  <div>
    <input ref="searchInput" v-model="query" placeholder="Tìm kiếm..." />
    <ul v-if="results.length">
      <li v-for="item in results" :key="item.id" ref="resultItem">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      query: '',
      results: []
    };
  },
  methods: {
    async fetchResults() {
      // Giả lập gọi API
      const fakeData = [
        { id: 1, name: 'Sản phẩm A' },
        { id: 2, name: 'Sản phẩm B' }
      ];
      this.results = fakeData;

      // Sau khi gán dữ liệu, danh sách <li> được tạo nhưng chưa có trong DOM
      // Để focus vào phần tử đầu tiên hoặc đo chiều cao:
      this.$nextTick(() => {
        const firstItem = this.$refs.resultItem;
        if (firstItem && Array.isArray(firstItem)) {
          firstItem[0]?.scrollIntoView({ behavior: 'smooth' });
        }
      });
    }
  }
};
</script>

Trong ví dụ trên, nếu không dùng this.$nextTick(), việc truy cập this.$refs.resultItem sẽ trả về undefined hoặc mảng rỗng vì DOM chưa kịp render lại sau khi this.results thay đổi.

Lưu ý: Từ Vue 3, this.$nextTick() vẫn hoạt động tương thích với Composition API thông qua hàm nextTick() được import từ vue. Tuy nhiên, trong Options API (như các ví dụ trên), cú pháp này vẫn là tiêu chuẩn và đáng tin cậy.

Thẻ: vuejs nexttick dom-updates reactivity frontend

Đăng vào ngày 16 tháng 7 lúc 19:08