Vue组件基础内容:注册、prop、自定义事件(一)

时间:2021-6-12 作者:qvyue

前言

Vue 组件是可复用的 Vue 实例,其拥有一个名字,可直接当作 Dom 元素使用,使用次数也没有限制。与函数封装等思想一样,本质都是对重复出现的功能进行复用。但 Vue 组件是对结构、样式、功能的整体封装。利用 Vue 组件,可以实现代码复用与高效开发。以下是一个 Vue 组件的基本定义:

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: ''
})

template 里的字符串模板可以理解为要复用的结构部分。可以看到,组件里也有 data,但这里的 data 不同于Vue实例身上的 data,这里的 data 是一个函数。这是因为函数拥有作用域,可以对 data里的数据进行私有限制,防止其数据影响全局实例的数据。因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

当使用 PascalCase 形式命名一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 和 都是可接受的。但直接在 DOM 中使用时只有 kebab-case 是有效的。

全局组件与局部组件

全局组件就是组件的注册位置与 Vue 的根实例在同一作用域下,这些全局组件在任何地方都可以使用,在彼此的内部也可以使用彼此。

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })

局部组件就是组件的注册作用域在 Vue 的根实例内,可以在根实例外部声明一个组件对象,然后在 Vue 根实例内的components属性内注册该组件:

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

因为作用域的限制,局部组件不能在彼此内部使用,但因为 Vue 组件本身就是 Vue 实例,其也有一个 components 属性,想要在其内部使用其他局部组件,可以在局部组件内部注册:

// 一般使用
var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
    'component-a': ComponentA
  },
  // ...
}

// 模块系统使用
import ComponentA from './ComponentA.vue'

export default {
  components: {
    ComponentA
  },
  // ...
}

Prop

prop 是组件的属性,与 data、methods 等属性一样。其名字可以是驼峰命名或 kebab-case 形式,在非字符串模板中使用时,如果在 Dom 中使用组件 prop 则其必须是 kebab-case 形式。prop 可以声明其类型:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

prop 可以是静态,也可以是动态,所谓动态就是变量,Vue 通过 v-bind 指令绑定 prop 变量,使得prop 变得灵活、强大起来:


单项数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

也就是说,prop 的作用就是父组件向子组件传递数据。这个数据传递的过程是不可逆的,子组件向父组件传递数据是不允许通过 prop 的。同时,这个 prop 也不能随意为子组件所用,不然会造成各种难题、例如数据混乱、数据污染等。且组件本身已经有了维护数据的 data ,再有一个 prop 做同样的事就显得多余。如果确实需要某个 prop,可以将其在 computed 属性里进行转换储存:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

prop 验证

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

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

自定义事件

自定义组件的 v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    
  `
})

现在在这个组件上使用 v-model 的时候:

这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的 property 将会被更新,且仍然需要在组件的 props 选项里声明 checked 这个 prop。

关于事件的两个重要 API

父子组件的通信可以通过 props(父传子)以及 $emit (子传父)来实现,但如果涉及到父传孙时,使用 vm.$attrsvm.$listeners会有用得多。

vm.$attrs 主要是在孙组件上使用,通过 v-bind 命令实现绑定,这个 API 主要用于收集父级组件作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件(包括子组件与孙组件)——在创建高级别的组件时非常有用。

vm.$listeners 主要是在孙组件上使用,通过 v-on 命令实现父组件的事件监听与收集,包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。以下案例是子组件(A组件是父,B组件是子,C组件是孙):

in child1

props: {{ pchild1 }}

$attrs: {{ $attrs }}


import Child2 from "./Child2.vue"; export default { data() { return { child1:'child1' }; }, components: { Child2 }, props: { pchild1:{ type:String } }, inheritAttrs: false, mounted() { this.$emit("method1",this.child1); }, };

在上面代码中出现了一个组件属性:inheritAttrs: false。Vue 官方的解释是:

“ 默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false。”

也就是说,父组件的非 props 属性默认会被当做 Dom 元素的 attribute 那样,直接让子组件的根元素继承。

// 父组件


 
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。