
3.2 组件化开发
Vue.js有两个最重要的核心概念,一是双向数据绑定,二是组件化开发,因此,本章所阐述的内容极为重要。
3.2.1 组件化开发的必要性
当一个带有数据、样式的HTML模块在一个项目中的多处被使用时,如果每个地方都重复写一遍,则会造成代码冗余。此时,可以考虑使用组件来解决这个问题。组件化开发在现在的网站开发中随处可见。
京东商城界面如图3-3所示,可以看到其中有很多个产品区块是完全相同的。假如纯粹使用HTML来写,然后遍历,则其他页面再有相同的区块时这些代码还得再重复一次,因此,组件化开发成了Vue.js项目中不可或缺的一个解决方案。
组件化开发中的组件分为全局组件与局部组件。全局组件通常可以在多个实例中共享,而局部组件只能在当前组件中调用。
3.2.2 全局组件
全局组件需要使用Vue.component()来定义,以下封装一个最简单的按钮组件,以此阐述全局组件的封装过程,代码如下:

图3-3 京东商城界面

至此,一个自定义的全局组件就诞生了。这里需要了解一点,template中的代码就是HTML标签,开发者也可以将它抽离出来写,代码如下:

注意 实例中的所有属性与方法组件中都有。实例与组件只有两处不同:一是实例中的el在组件中需要使用template替代;二是实例中data是一个属性,而在组件中,data必须是函数,因为每个组件需要复制并维护一份独立的对象数据,从而避免组件之间数据耦合或冲突。
1.组件中的data
在Vue.js组件中,data必须是函数。函数需要返回一个对象,并在对象中存放必要的数据。实现图3-4所示的效果,同时要求按钮中的文字必须放在组件的data中,代码如下:


图3-4 按钮中的文字动态存放于data中
2.组件中的方法
对以上代码进行修改,给按钮添加一种方法,单击之后可以修改msg的值,从而让按钮的文字显示为“这是一个按钮”,代码如下:

组件中除了template和data的写法与实例的写法不一样,其他写法都是完全一致的,例如methods、computed、filters等,因此初学者可以更快更容易地上手组件。
3.组件无法直接调用实例中的数据
一个组件是无法直接调用实例中的数据的,除非实例愿意共享这部分数据。组件默认只能调用自身data中返回的数据,如果直接调用实例中的数据,则会出现如图3-5所示的错误提示信息。具体错误代码如下:


图3-5 组件无法直接调用实例中的数据
3.2.3 局部组件
全局组件可以在多个实例中共享,而局部组件只能在当前实例(或组件)中使用。
注意 组件除了可以在实例中调用,还可以在另一个组件中调用,也就是说一个组件可以调用另一个组件。
在组件或实例中,可以注册局部组件,代码如下:

使用components属性可以定义(或者称为“注册”)一个组件,其格式与全局组件的格式大致相同,依然拥有template、data等属性与方法。
1.组件通信之“父传子”
实例与组件、组件与组件之间是可以相同通信的,如果一个组件是被调用的,则习惯上将其称为子组件,而调用它的组件(或实例),就被称为父组件。父组件的数据子组件无法直接调用,必须通过“父传子”才能实现,代码如下:

注意 此处有个细节需要留意,传递的属性作为子组件的值来使用,此时子组件data中不能重复定义相同名称的属性。
2.props的两种格式
props有两种格式,一种是简写的数组格式,另一种是完整的对象格式。若要修改为对象格式,则代码如下:

当父组件并未给子组件传值时,建议添加上默认值,避免出现渲染缺失等错误。
3.单项数据流
正如Vue.js官网所书:“所有的prop都使其父子prop之间形成了一个单向下行绑定:父级prop的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致应用的数据流向难以理解。”
因此可以得出结论:Vue.js的父传子为单向传递,为了避免数据错乱,开发中应减少甚至不允许子组件直接修改props接收的数据,但有时在实际开发需求中,确实需要在子组件修改父组件传递过来的数据,此种情况应该怎么办呢?
4.组件通信之“子传父”
笔者以最简单的方式对上文中抛出的问题给出答案,即子组件修改父组件数据的唯一途径是触发父组件去修改自身的数据,代码如下:

5.同级组件通信
同级组件相互通信,目前有两种方式,一种是中央事件总线,另一种是状态管理Vuex。由于Vuex的用法相对复杂,后文再行阐述,本章先介绍中央事件总线。
中央事件总线被称为EventBus(也称为事件巴士),代码如下:

单击了按钮1,按钮2的组件会得到按钮1被单击后传递过来的值,效果如图3-6所示。

图3-6 EventBus实现同级组件通信
Vue.js的核心思想之一便是本章所讲解的组件化开发,其中包括全局组件、局部组件、组件通信与组件调用等重要内容。