视图组件很棒,对吗?他们将您的应用程序的视图和行为嵌入到可复制的小块上。如果您需要一些额外的功能,只需添加指令! 事实是,指令相当不灵活,不能做到一切。指令不能(轻松)发出事件,例如。
抽象组件就像正常组件,除非它们对 DOM 没有任何回报,它们只会为现有组件添加额外的行为,您可能熟悉 Vue 内置的抽象组件,例如 <transition>
, <component>
和 <slot>
。
抽象组件的一个很好的用例是跟踪当一个元素进入视图端口时,使用IntersectionObserver
。
<$>[注] 如果你想要一个正确的生产准备的实现,看看 vue-intersect,这本教程是基于。
开始的
首先,我们将创建一个快速抽象的组件,简单地渲染其内容. 为了实现这一点,我们将快速潜入 渲染函数。
1[label IntersectionObserver.vue]
2export default {
3 // Enables an abstract component in Vue.
4 // This property is undocumented and may change at any time,
5 // but your component should work without it.
6 abstract: true,
7 // Yay, render functions!
8 render() {
9 // Without using a wrapper component, we can only render one child component.
10 try {
11 return this.$slots.default[0];
12 } catch (e) {
13 throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
14 }
15
16 return null;
17 }
18}
恭喜你!你现在有一个抽象的组成部分,好吧,什么都没有!它只是让孩子们。
添加跨界观察员
好了,现在让我们坚持IntersectionObserver
的逻辑。
<$>[警告]在IE或Safari中,IntersectionObserver不是本地支持的,所以你可能想为它抓取 polyfill。
1[label IntersectionObserver.vue]
2export default {
3 // Enables an abstract component in Vue.
4 // This property is undocumented and may change at any time,
5 // but your component should work without it.
6 abstract: true,
7 // Yay, render functions!
8 render() {
9 // Without using a wrapper component, we can only render one child component.
10 try {
11 return this.$slots.default[0];
12 } catch (e) {
13 throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
14 }
15
16 return null;
17 },
18
19 mounted () {
20 // There's no real need to declare observer as a data property,
21 // since it doesn't need to be reactive.
22
23 this.observer = new IntersectionObserver((entries) => {
24 this.$emit(entries[0].isIntersecting ? 'intersect-enter' : 'intersect-leave', [entries[0]]);
25 });
26
27 // You have to wait for the next tick so that the child element has been rendered.
28 this.$nextTick(() => {
29 this.observer.observe(this.$slots.default[0].elm);
30 });
31 }
32}
好吧,所以现在我们有一个抽象的组件,我们可以这样使用:
1<intersection-observer @intersect-enter="handleEnter" @intersect-leave="handleLeave">
2 <my-honest-to-goodness-component></my-honest-to-goodness-component>
3</intersection-observer>
虽然我们还没有完成......
完结
我们需要确保在从DOM中删除组件时不留下任何悬浮的IntersectionObservers
,所以现在让我们快速解决这个问题。
1[label IntersectionObserver.vue]
2export default {
3 // Enables an abstract component in Vue.
4 // This property is undocumented and may change at any time,
5 // but your component should work without it.
6 abstract: true,
7 // Yay, render functions!
8 render() {
9 // Without using a wrapper component, we can only render one child component.
10 try {
11 return this.$slots.default[0];
12 } catch (e) {
13 throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
14 }
15
16 return null;
17 },
18
19 mounted() {
20 // There's no real need to declare observer as a data property,
21 // since it doesn't need to be reactive.
22
23 this.observer = new IntersectionObserver((entries) => {
24 this.$emit(entries[0].isIntersecting ? 'intersect-enter' : 'intersect-leave', [entries[0]]);
25 });
26
27 // You have to wait for the next tick so that the child element has been rendered.
28 this.$nextTick(() => {
29 this.observer.observe(this.$slots.default[0].elm);
30 });
31 },
32
33 destroyed() {
34 // Why did the W3C choose "disconnect" as the method anyway?
35 this.observer.disconnect();
36 }
37}
只是为了奖金点,让我们让观察者门槛可用奖励来配置。
1[label IntersectionObserver.vue]
2export default {
3 // Enables an abstract component in Vue.
4 // This property is undocumented and may change at any time,
5 // but your component should work without it.
6 abstract: true,
7
8 // Props work just fine in abstract components!
9 props: {
10 threshold: {
11 type: Array
12 }
13 },
14
15 // Yay, render functions!
16 render() {
17 // Without using a wrapper component, we can only render one child component.
18 try {
19 return this.$slots.default[0];
20 } catch (e) {
21 throw new Error('IntersectionObserver.vue can only render one, and exactly one child component.');
22 }
23
24 return null;
25 },
26
27 mounted() {
28 // There's no real need to declare observer as a data property,
29 // since it doesn't need to be reactive.
30
31 this.observer = new IntersectionObserver((entries) => {
32 this.$emit(entries[0].isIntersecting ? 'intersect-enter' : 'intersect-leave', [entries[0]]);
33 }, {
34 threshold: this.threshold || 0
35 });
36
37 // You have to wait for the next tick so that the child element has been rendered.
38 this.$nextTick(() => {
39 this.observer.observe(this.$slots.default[0].elm);
40 });
41 },
42
43 destroyed() {
44 // Why did the W3C choose "disconnect" as the method anyway?
45 this.observer.disconnect();
46 }
47}
最终用途看起来是这样的:
1<intersection-observer @intersect-enter="handleEnter" @intersect-leave="handleLeave" :threshold="[0, 0.5, 1]">
2 <my-honest-to-goodness-component></my-honest-to-goodness-component>
3</intersection-observer>
你的第一个抽象组成部分
谢谢你,谢谢你,谢谢你,谢谢你,谢谢你,谢谢你,谢谢你,谢谢你,谢谢你。