实战Vue组件和Mixins

特别声明,本文根据@Saqueib Ansari的《Practical use of Components and Mixins in Vue JS》一文所整理。

这篇文章主要帮助我们深入的学习Vue的组件和mixins,它们帮助你扩展基本的HTML元素,用来封装可重用的代码。在较高的层次上,组件是定制的元素,Vue的编译器附加了行为,而mixins为你提供了一个保持代码可重用性的方式,从而使你的代码保持干净和易于维护。

如果你从未接触过Vue的mixins,建议你花点时间先阅读《Vue中的Mixins》一文。

Vue组件

组件是一段可重用的UI,它封装了其中的所有行为和功能。比如Chrome的日期输入框(<input type="date">)就是浏览器提供的一个组件,它会给你一个日历弹出框来供你选择日期。你只需要在使用的地方添加一个类型(type)为date的输入框(input),即:<input type="date" >,就可以神奇地获得所有关于日期的功能。类似地,Vue为你提供了创建自己定制组件的选项。如果你想构建SPA,你可能需要一个根组件,比如App<App>)组件,它通常充当包装器,并包含所有的根级状态。

创建一个组件

现在让我们在Vue中创建一个组件,我们将构建一个Card组件:

<!-- Card.vue -->
<template>
    <div class="card">
        <div class="card-header">Card title</div>
        <div class="card-body">Card content</div>
    </div>
</template>

<!-- App.vue -->
<template>
    <div id="app" class="container">
        <Card />
    </div>
</template>

<script>
    import Card from "./components/Card";

    export default {
        name: "App",
        components: {
            Card
        }
    };
</script>

这是我们想要的样子:

没有什么特别的,一个简单的组件需要一个名称(name,比如上例中的Card)作为元素的标签和由该标签渲染的模板(<template>)。在上面的示例中,使用的是<Card />。也有人建议在组件名称前添加自己的私有前缀,这样做的好处是不是与第三方引入的组件名发生冲突。

向组件传递数据

就上面示例而言,Card组件到目前为止并没有做任何事,只是显示了文本中位符,并无法达到定制标题和内容的效果(也就是动态加载数据,更改模板中的内容)。这个时候我们可以给Card定制两个属性:titlebody。在Vue中,组件可以通过props接受外部数据,因此在上例的基础上,定义titlebody来动态接受外部数据。

<!-- Card.vue -->
<template>
    <div class="card mb-3">
        <div class="card-header">{{ title }}</div>
        <div class="card-body">{{ body }}</div>
    </div>
</template>

<script>
    export default {
        name: "Card",
        props: ['title', 'body'],
        data() {
            return {};
        }
    };
</script>

<!-- App.vue -->
<template>
    <div id="app" class="container">
        <Card v-for="(item, index) in cardLists" :title="item.title" :body="item.body" :key="index" />
    </div>
</template>

<script>
import Card from "./components/Card";

export default {
    name: "App",
    components: {
        Card
    },
    data() {
        return {
            cardLists: [
                {
                    title: "User Details",
                    body: "Hi, I am Airen from https://www.w3cplus.com"
                },
                {
                    title: "Latest Posts",
                    body: "Components, Directives, Filters and Mixins in Vue JS"
                }
            ]
        };
    }
};
</script>

正如你所见,在<Card>组件上添加了Props,因此在模板中可以使用{{ title }}{{ body }}给HTML元素传递值,从而取替了之前的静态文本。这样就可以在调用<Card>组件的地方,通过title="some literal string"或者像示例中一样通过:title="titleVar"绑定一个变量。

这样一来,我们的组件就可以达到重用的目的。

注,这里使用了Vue中的一些指令,比如v-forv-bind。其中v-for用来对数据做遍历,而v-bind用来做属性的绑定。

创建一个子组件

我们还可以将组件嵌套在另一个组件中。接下来让我们来创建一个Follow组件,它就像一个切换器(关注还是未关注)一样,用来跟踪用户。

UserList组件中,我们从https://reqres.in中获取用户相关的信息,然后将它们添加到users数组件,最后通过v-for将其遍历出来。你也可以看到在UserList中添加了一个子组件Follow,该组件放置在UserList组件的用户名下面。因此,你可以将任何组件嵌套在另一个组件中。你可以点击Follow按钮进行状态切换,那是因为使用v-onclick事件绑定了一个toggle方法,当然,你也可以使用@click="toggle"这样的简写方式。在我们的示例中,还在@click后面添加了一个后缀prevent,用来防止元素默认的事件。在toggle方法中,主要做的事件就是用来切换按钮状态,用来追踪状态已更改。

将数据从子组件传递到父组件

Vue自琮发布子事件系统,可以使用vm.$emit('myEvent', payload)来发送事件,该事件可以被父组件监听。用户在单击Follow组件上的Follow按钮时发出followed事件,我们将在父组件UserList中监听它。

正如你所看到的,当Follow组件的toggle()触发时,会通过vm.$emit('followed', payload)向父组件UserList发送followed事件,这个时候UserList组件的方法handleFollow(payload)就会被被触发。

通过上面的两个示例向大家展示了两种不同的方式进行组件之间的数据通讯。有关于Vue组件之间的数据通讯更多的教程,可以阅读下面的文章。

使用Slot定制模板

Vue中的props对于传递变量和表达式很好,但是如果你想给HTML传递就行不通了。在Vue中可以使用slot来定制模板,这使得组件非常灵活。接来下看看如何通过slotCard组件传递HTML所需要的内容。

在组件中添加slot可能是最简单的,你只需要编辑<template>并添加<slot></slot>,也可以通过nameslot命名,比如<slot name="title"></slot>。调用slot也非常的简单,只需要传递你的内容到slot中,像下面这样:

<Card>
    <!-- default slot -->
    I will be shown in default slot 

    <div slot="title">
        As you guessed I will end up in title slot
    </div>
</Card>

有关于Vue组件中关于slot更深入的介绍,可以阅读《Vue组件内容分发(slot》一文。

Vue组件的生命周期的钩子函数

下面是在Vue组件、实例的生命周期中触发的钩子。所有生命周期钩子都将this上下文自动绑定到实例,以便你可以访问datacomputedmethods

Vue提供的可以注册的钩子都在上图中红色框标注。他们是:

  • beforeCreate:在实例初始化之后,数据观测(Data Observer)和event/watcher事件配置之前被调用
  • created:实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据(Data Observer)、属性和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$el属性目前不可见
  • beforeMount:在挂载开始之前被调用:相关的render函数首次被调用
  • mountedel被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当mounted被调用时vm.$el也在文档内
  • beforeUpdate:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程
  • updated:由于数据更改导致虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于DOM的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用
  • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用
  • destroyed:Vue实例销毁后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用

打开浏览器的console查看对应的生命周期钩子函数相应的输出结果:

有关于Vue生命周期更深入的介绍,可以阅读《Vue实例和生命周期》一文。

Vue的Mixins

mixins是Vue组件分发可重用功能的一种灵活方法。mixins对象可以包含组件的任何选项。当组件使用mixins时,mixins中的所有选项将混入组件自己的选项。

你可能已经看到Bootstrap的很多组件有多种状态,比如successerrorwarninginfo等。如果你想给一个按钮添加loadingwarningerror状态,就可以通过创建一个mixins。除了在按钮上可以使用之外,还可以在AlertCard之类的组件使用这些状态。

在上面的示例中,创建了stateableMixincloseableMixin两个mixinsstateableMixin给元素传递type类型,所以可以使用不同的状态。closeableMixin添加关闭和切换行为。在Vue中可以同时使用多个mixins。比如上例中的CardAlert组件中同时使用了这两个mixins

这就是它的要点,使用mixins,你可以创建一个一致的API,运用在整个运用程序中和你的组件一起工作。而且你可以始终在一个地方维护你的mixins,这样做也会让所有使用它的组件受益。

有关于Vue组件的mixins更多的介绍,可以阅读《Vue中的Mixins》一文。

总结

这篇文章主要介绍了Vue的组件和mixins。可以涵盖了Vue组件的很多方面,比如说:创建组件,组件数据通讯,EventBus(事件总线)和生命周期以及mixins等。涉及的内容比较多,也比较零乱,不过还是希望你能在这篇文章中获得自己想要的一些东西。如果文章中有不对之处,还请在下面的评论中指正,如果你有更多的经验或建议,也欢迎在下面的评论中与我们一起分享。

返回顶部