Chủ Nhật, 27/08/2017 | 00:00 GMT+7

Viết các thành phần trừu tượng với Vue.js


Các thành phần Vue là tuyệt vời, phải không? Chúng gói gọn chế độ xem và hành vi của ứng dụng của bạn thành những phần nhỏ có thể ghép lại được. Nếu bạn cần thêm một chút chức năng trên chúng, chỉ cần đính kèm các chỉ thị! Điều đó là, các chỉ thị khá linh hoạt và không thể thực hiện tất cả mọi thứ. Ví dụ, các lệnh không thể (dễ dàng) phát ra các sự kiện. Vâng, đây là Vue, tất nhiên có một giải pháp. Các thành phần trừu tượng!

Các thành phần trừu tượng giống như các thành phần bình thường, ngoại trừ chúng không hiển thị bất kỳ thứ gì cho DOM. Họ chỉ thêm hành vi bổ sung cho những hành vi hiện có. Bạn có thể quen thuộc với các thành phần trừu tượng từ các thành phần tích hợp sẵn của Vue, chẳng hạn như <transition> , <component><slot> .

Một trường hợp sử dụng tuyệt vời cho các thành phần trừu tượng là theo dõi khi một phần tử đi vào khung nhìn với IntersectionObserver . Hãy xem cách triển khai một thành phần trừu tượng đơn giản để xử lý điều đó ở đây.

Nếu bạn muốn triển khai điều này ở chế độ sẵn sàng production phù hợp, hãy xem vue-interect , hướng dẫn này dựa trên.

Bắt đầu

Đầu tiên, ta sẽ tạo một thành phần trừu tượng nhanh chóng mà chỉ đơn giản là hiển thị nội dung của nó. Để thực hiện điều này, ta sẽ xem xét nhanh các hàm kết xuất .

IntersectionObserver.vue
export default {
   // Enables an abstract component in Vue.
   // This property is undocumented and may change at any time,
   // but your component should work without it.
  abstract: true,
  // Yay, render functions!
  render() {
    // Without using a wrapper component, we can only render one child component.
    try {
      return this.$slots.default[0];
    } catch (e) {
      throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
    }

    return null;
  }
}

Xin chúc mừng! Đến đây bạn có một thành phần trừu tượng, tốt, không làm gì cả! Nó chỉ ám ảnh con cái của nó.

Thêm IntersectionObserver

Được rồi, bây giờ hãy đi sâu vào logic của IntersectionObserver .

IntersectionObserver nguyên bản không được hỗ trợ trong IE hoặc Safari, vì vậy bạn có thể cần lấy một polyfill cho nó.

IntersectionObserver.vue
export default {
   // Enables an abstract component in Vue.
   // This property is undocumented and may change at any time,
   // but your component should work without it.
  abstract: true,
  // Yay, render functions!
  render() {
    // Without using a wrapper component, we can only render one child component.
    try {
      return this.$slots.default[0];
    } catch (e) {
      throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
    }

    return null;
  },

  mounted () {
    // There's no real need to declare observer as a data property,
    // since it doesn't need to be reactive.

    this.observer = new IntersectionObserver((entries) => {
      this.$emit(entries[0].isIntersecting ? 'intersect-enter' : 'intersect-leave', [entries[0]]);
    });

    // You have to wait for the next tick so that the child element has been rendered.
    this.$nextTick(() => {
      this.observer.observe(this.$slots.default[0].elm);
    });
  }
}

Được rồi, bây giờ ta có một thành phần trừu tượng mà ta có thể sử dụng như sau:

<intersection-observer @intersect-enter="handleEnter" @intersect-leave="handleLeave">
  <my-honest-to-goodness-component></my-honest-to-goodness-component>
</intersection-observer>

Tuy nhiên, ta vẫn chưa hoàn thành…

Kết thúc

Ta cần đảm bảo không để lại bất kỳ IntersectionObservers nào đang lủng lẳng khi thành phần bị xóa khỏi DOM, vì vậy hãy khắc phục điều đó thật nhanh ngay bây giờ.

IntersectionObserver.vue
export default {
   // Enables an abstract component in Vue.
   // This property is undocumented and may change at any time,
   // but your component should work without it.
  abstract: true,
  // Yay, render functions!
  render() {
    // Without using a wrapper component, we can only render one child component.
    try {
      return this.$slots.default[0];
    } catch (e) {
      throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
    }

    return null;
  },

  mounted() {
    // There's no real need to declare observer as a data property,
    // since it doesn't need to be reactive.

    this.observer = new IntersectionObserver((entries) => {
      this.$emit(entries[0].isIntersecting ? 'intersect-enter' : 'intersect-leave', [entries[0]]);
    });

    // You have to wait for the next tick so that the child element has been rendered.
    this.$nextTick(() => {
      this.observer.observe(this.$slots.default[0].elm);
    });
  },

  destroyed() {
    // Why did the W3C choose "disconnect" as the method anyway?
    this.observer.disconnect();
  }
}

Và chỉ đối với điểm thưởng, hãy làm cho ngưỡng quan sát có thể cấu hình bằng các đạo cụ.

IntersectionObserver.vue
export default {
   // Enables an abstract component in Vue.
   // This property is undocumented and may change at any time,
   // but your component should work without it.
  abstract: true,

  // Props work just fine in abstract components!
  props: {
    threshold: {
      type: Array
    }
  },

  // Yay, render functions!
  render() {
    // Without using a wrapper component, we can only render one child component.
    try {
      return this.$slots.default[0];
    } catch (e) {
      throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
    }

    return null;
  },

  mounted() {
    // There's no real need to declare observer as a data property,
    // since it doesn't need to be reactive.

    this.observer = new IntersectionObserver((entries) => {
      this.$emit(entries[0].isIntersecting ? 'intersect-enter' : 'intersect-leave', [entries[0]]);
    }, {
      threshold: this.threshold || 0
    });

    // You have to wait for the next tick so that the child element has been rendered.
    this.$nextTick(() => {
      this.observer.observe(this.$slots.default[0].elm);
    });
  },

  destroyed() {
    // Why did the W3C choose "disconnect" as the method anyway?
    this.observer.disconnect();
  }
}

Cách sử dụng cuối cùng trông như thế này:

<intersection-observer @intersect-enter="handleEnter" @intersect-leave="handleLeave" :threshold="[0, 0.5, 1]">
  <my-honest-to-goodness-component></my-honest-to-goodness-component>
</intersection-observer>

Của bạn đây! Thành phần trừu tượng đầu tiên của bạn.

Xin chân thành cảm ơn Thomas Kjærgaard / Heavyy về việc thực hiện và ý tưởng ban đầu!


Tags:

Các tin liên quan