58

重用Vue组件中的逻辑

 5 years ago
source link: https://www.w3cplus.com/vue/reusing-logic-in-vue-components.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

特别声明,本文根据@Alex Jover Morales的《 Reusing Logic in Vue Components 》一文所整理。

当你开始使用Vue创建应用程序时,你可以开始先创建组件,来构建应用程序的不同部分。你应该可以感受到Vue和Web组件结构体系的良好开发体验。随着项目的进行,你开始以某种方式构造应用程序组件,可能是按页面和组件。

但随着项目的不断发展,你开始要在多个组件之间执行重复的逻辑。我们常常说不要做重复的事情(DRY)和让一切保持它的简单。这两个原则利于我们编写和维护应用程序。

也许你已经知道一些有助于遵循这些原则的模式、库和技术。Vuex将帮助你从组件中提取状态逻辑,Vue路由器将对路由逻辑做同样的工作,但是组件呢?

我们经常遇到这样的情况,需要重用属于组件的一些UI功能。例如,除了被锚定和定位到元素之外,弹出窗(Popover)和提示框(Tooltip)都可以在某个事件触发时共享打开和关闭的功能。

在这篇文章中,我将采用一个 Colorful.vue 组件的示例,该组件监听滚动事件并根据滚动位置更改颜色:如果它高于窗口高度,则为蓝色,否则为红色。这是通过使用 :style 绑定 color 本地状态变量来实现:

<!-- Colorful.vue -->
<template>
    <div :style="{ background: color }"></div>
</template>

<script>
    export default {
        data: () => ({
            color: 'red'
        }),

        methods: {
            handleScrollChange(status) {
                this.color = status === 'in' ? 'red' : 'blue'
            },
            handleScroll() {
                if (window.scrollY > window.innerHeight) {
                    this.handleScrollChange('out')
                } else {
                    this.handleScrollChange('in')
                }
            }
        },

        mounted() {
            window.addEventListener('scroll', this.handleScroll)
        },

        destroyed() {
            window.removeEventListener('scroll', this.handleScroll)
        }
    }
</script>

添加一点样式,方便我们看效果。这个时候你在页面中滚动鼠标时,能看到这样的效果:

NBnAnuQ.gif

你可能没有在组件中看到任何错误。它在 mounted 钩子中注册了 scroll 事件,并在 destroyed 钩子中删除 scroll 事件。它们调用一个 handleScroll 方法来检测滚动条位置,并调用 handleScrollChange 方法来改变颜色,具体是什么颜色取决于 status 参数。

现在的滚动功能是在 Colorful 组件中。但是,我们可以去掉 mounteddestroyed 的钩子和 handleScroll 方法,以便在其他组件中重用它们。

我们来看看不同的方法。

组件继承

首先让我们将与滚动相关的行为移动到它自己的组件 Scroll.js 中:

export default {
    methods: {
        handleScroll() {
            if (window.scrollY > window.innerHeight) {
                this.handleScrollChange('out')
            } else {
                this.handleScrollChange('in')
            }
        }
    },

    mounted() {
        window.addEventListener('scroll', this.handleScroll)
    },

    destroyed() {
        window.removeEventListener('scroll', this.handleScroll)
    }
}

正如你所见,这个滚动组件需要有一个 handleScrollChange ,我们在子组件中实现它,这意味着这个组件必须被扩展并且不能单独工作。

因为它只包含JavaScript,所以我们可以将它写在 .js 文件中,但如果需要,它也可以是 .vue 文件。请记住,只会继承 .vue 文件的JavaScript部分。

然后,在 Colorful 组件中删除我们移出的组件行为,并使用 extends 组件选项导入 Scroll 文件:

<!-- Colorful.vue -->
<template>
    <div :style="{ background: color }"></div>
</template>

<script>
    import Scroll from './Scroll'

    export default {
        extends: Scroll,

        data: () => ({
            color: 'red'
        }),

        methods: {
            handleScrollChange(status) {
                this.color = status === 'in' ? 'red' : 'blue'
            }
        }
    }
</script>

请注意,组件扩展不是类继承。在本例中,Vue合并父组件和子组件选项,从而创建一个新的混合对象。

例如,在前面的示例中,我们最终会得到包含下面API的一个组件:

{
    data: () => ({
        color: "red"
    }),
    methods: {
        handleScrollChange(status),
        handleScroll,
    },
    mounted,
    destroyed  
};

对于 mounteddestroyed 钩子来说,父节点和子节点都会被保留,并且它们将按照从父节点到子节点的继承顺序被调用。

Mixins

与组件继承类似,我们可以使用 mixins 来共享组件逻辑。但是在前面的示例中,我们只能从一个组件继承,而使用 mixins ,我们可以组合它们的多个功能。

事实上,我们不需要从上一个示例中的 Scroll.js 文件中更改任何内容。在 Colorful 组件中只需使用 mixins 选项来替代 extends 选项就足够了。请记住, mixins 需要一个数组:

<!-- Colorful.vue -->
<script>
    import Scroll from './Scroll'

    export default {
        mixins: [Scroll],

        data: () => ({
            color: 'red'
        }),

        methods: {
            handleScrollChange(status) {
                this.color = status === 'in' ? 'red' : 'blue'
            }
        }
    }
</script>

与组件继承的主要区别在于顺序不同:在组件继承中,子组件中的钩子在父组件之前执行,而 mixins 的钩子在组件使用它之前执行它们的钩子。

另外, mixins 不能有任何 templatestyle 标签,它们只是普通的JavaScript。

编写可重用组件

虽然继承和 mixins 看起来很容易实现,但它们在某种程度上是隐式的。在前面的示例中,当你使用 Scroll 功能时,需要知道必要的 handleScrollChange 方法。

在这种情况下并没有那么糟糕,但是当你扩展或使用多个 mixins 时,事情可能会变得混乱,特别是开始追踪许多部分的功能时。

另一种重用功能的方法是创建只接收 propsemit ,这样的方法可能是一种更明确的解决方案,既没有任何合并,也不需要共享上下文。此外,这种解决方案更加通用,因为相同的方法可以应用于任何其他基于组件的技术。

首先,我们必须使用 Scroll.vue 组件:

<!-- Scroll.vue -->
<template>
    <div></div>
</template>

<script>
    export default {
        methods: {
            handleScroll() {
                if (window.scrollY > window.innerHeight) {
                    this.$emit('scrollChange', 'out')
                } else {
                    this.$emit('scrollChange', 'in')
                }
            }
        },

        mounted() {
            window.addEventListener('scroll', this.handleScroll)
        },

        destroyed() {
            window.removeEventListener('scroll', this.handleScroll)
        }
    }
</script>

在本例中,我们不是调用 handleScrollChange 方法,而是发出一个 scrollChange 事件,父组件可以使用这个事件来完成它的工作。

然后,在 Colorful.vue 中,我们必须奖其作为组件导入并处理 scrollChange 事件:

<!-- Colorful.vue -->
<template>
    <scroll @scrollChange="handleScrollChange" :style="{ background:color }"></scroll>
</template>

<script>
    import Scroll from './Scroll'

    export default {
        components: {
            Scroll
        },

        data: () => ({
            color: 'red'
        }),

        methods: {
            handleScrollChange(status) {
                this.color = status === 'in' ? 'red' : 'blue'
            }
        }
    }
</script>

我们已经通过 scroll 替换了 div 标签,但是 Colorful 组件逻辑保持不变。

总结

我们已经看到三种方法来提取滚动功能并重用它。根据你自己的情况,你将选择其中的任何一种方法。

组件继承和 mixins 为我们提供了一种分离组件逻辑的一部分并将它们合在一起的方法,这种方法在某些情况下适用,前提条件是它不会使用你的项目管理变得太混乱。特别是 mixins ,它更强大,因为它允许将多个 mixins 组合在一起。

使用组件组合是一种更清晰,更明确的解决方案。同样的技术也可以用于使用相同技术的其他框架。但在某些情况下,它可能不如 mixins 那么方便。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK