vue - 封装组件

组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:

  1. Vue.component('todo-item', {
  2. // todo-item 组件现在接受一个
  3. // "prop",类似于一个自定义 attribute。
  4. // 这个 prop 名为 todo。
  5. props: ['todo'],
  6. template: '<li>{{ todo.text }}</li>'
  7. })

子单元通过 prop 接口与父单元进行了良好的解耦。

Vue 组件提供了纯自定义元素<slot name="s">所不具备的一些重要功能,最突出的是跨组件数据流、自定义事件通信以及构建工具集成。

一、 基础 组件

  1. 组件是可复用的 Vue 实例,且带有一个名字,它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
  2. 因为你每用一次组件,就会有一个它的新实例被创建。
  3. 一个组件的 data 选项必须是一个函数
  4. 组件注册类型:全局注册和局部注册

二、局部组件

局部注册的组件在其子组件中不可用

适用于 业务型组件

三、全局组件

  1. 使用
    1. Vue.component('my-component-name', {
    2. // ... 选项 ...
    3. })
  1. 基础组件的自动化全局注册

  2. 全局注册

    • webpack中使用 require.context全局注册
    • src/main.js 中全局导入
  3. 注意: 全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生

四、组件参数

  1. 无指定类型props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
  1. 每个 prop 都有指定的值类型props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object, callback: Function, contactsPromise: Promise // or any other constructor }
  1. 传入不同类型的props
  • 传入一个布尔值
    1. <!-- 包含该 prop 没有值的情况在内,都意味着 `true`。-->
    2. <blog-post is-published></blog-post>
    3. <!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
    4. <!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
    5. <blog-post v-bind:is-published="false"></blog-post>
  • 传入一个对象的所有 property

    如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:

  1. post: {
  2. id: 1,
  3. title: 'My Journey with Vue'
  4. }
  5. 下面的模板:
  6. <blog-post v-bind="post"></blog-post>

Prop 验证

五、组件之间的传参

  1. 单向数据流

    所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。


这里有两种常见的试图变更一个 prop 的情形:

  • 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
    1. props: ['initialCounter'],
    2. data: function () {
    3. return {
    4. counter: this.initialCounter
    5. }
    6. }
  1. props: ['size'],
  2. computed: {
  3. normalizedSize: function () {
  4. return this.size.trim().toLowerCase()
  5. }
  6. }

PS:注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。

  1. props验证
  1. Vue.component('my-component', {
  2. props: {
  3. // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
  4. propA: Number,
  5. // 多个可能的类型
  6. propB: [String, Number],
  7. // 必填的字符串
  8. propC: {
  9. type: String,
  10. required: true
  11. },
  12. // 带有默认值的数字
  13. propD: {
  14. type: Number,
  15. default: 100
  16. },
  17. // 带有默认值的对象
  18. propE: {
  19. type: Object,
  20. // 对象或数组默认值必须从一个工厂函数获取
  21. default: function () {
  22. return { message: 'hello' }
  23. }
  24. },
  25. // 自定义验证函数
  26. propF: {
  27. validator: function (value) {
  28. // 这个值必须匹配下列字符串中的一个
  29. return ['success', 'warning', 'danger'].indexOf(value) !== -1
  30. }
  31. }
  32. }
  33. })

PS注意:那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的。

  1. 类型检查
    类型检查
    type 可以是下列原生构造函数中的一个:
  1. String
  2. Number
  3. Boolean
  4. Array
  5. Object
  6. Date
  7. Function
  8. Symbol

额外的,type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。例如,给定下列现成的构造函数:

  1. function Person (firstName, lastName) {
  2. this.firstName = firstName
  3. this.lastName = lastName
  4. }

你可以使用:

  1. Vue.component('blog-post', {
  2. props: {
  3. author: Person
  4. }
  5. })

来验证 author prop 的值是否是通过 new Person 创建的。

  1. 非 Prop 的 Attribute

    • 替换/合并已有的 Attribute
      想象一下 的模板是这样的:<input type="date" class="form-control">
      为了给我们的日期选择器插件定制一个主题,我们可能需要像这样添加一个特别的类名:
      1. <bootstrap-date-input
      2. data-date-picker="activated"
      3. class="date-picker-theme-dark"
      4. >
      5. </bootstrap-date-input>


在这种情况下,我们定义了两个不同的 class 的值:

form-control,这是在组件的模板内设置好的
date-picker-theme-dark,这是从组件的父级传入的
对于绝大多数 attribute 来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入 type=”text” 就会替换掉 type=”date” 并把它破坏!庆幸的是,class 和 style attribute 会稍微智能一些,即两边的值会被合并起来,从而得到最终的值:form-control date-picker-theme-dark。

  • 禁用 Attribute 继承

    如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false。例如:

  1. Vue.component('my-component', {
  2. inheritAttrs: false,
  3. // ...
  4. })

这尤其适合配合实例的 $attrs property 使用,该 property 包含了传递给一个组件的 attribute 名和 attribute 值,例如:

  1. {
  2. required: true,
  3. placeholder: 'Enter your username'
  4. }

有了 inheritAttrs: false 和 $attrs,你就可以手动决定这些 attribute 会被赋予哪个元素。在撰写基础组件的时候是常会用到的:

  1. Vue.component('base-input', {
  2. inheritAttrs: false,
  3. props: ['label', 'value'],
  4. template:
  5. ` <label>
  6. {{ label }}
  7. <input
  8. v-bind="$attrs"
  9. v-bind:value="value"
  10. v-on:input="$emit('input', $event.target.value)" >
  11. </label>`
  12. })

注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定。


这个模式允许你在使用基础组件的时候更像是使用原始的 HTML 元素,而不会担心哪个元素是真正的根元素:

  1. <base-input
  2. v-model="username"
  3. required
  4. placeholder="Enter your username"
  5. >
  6. </base-input>