组件

       组件可以扩展HTML元素,封装可重用的代码,在较高的层面上,组件是自定义元素,vue.js的编译器为它添加特殊功能,在有些情况下,组件也可以是原生HTML元素的形式,以is特性扩展。
       Vue.js的组件可以理解为 预先定义好了行为的ViewModel类。一个组件可以预定义很多选项,但最核心的是以下几个:
  • 模板(template):模板声明了数据和最终展现给用户的DOM之间的映射关系。
  • 初始数据(data):一个组件的初始数据状态。对于可复用的组件来说,这通常是私有的状态。
  • 接受的外部参数(props):组件之间通过参数来进行数据的传递和共享。参数默认是单向绑定(由上至下),但也可以显式地声明为双向绑定。
  • 方法(methods):对数据的改动操作一般都在组件的方法内进行。可以通过v-on指令将用户输入事件和组件方法进行绑定。
  • 生命周期钩子函数(lifecycle hooks):一个组件会触发多个生命周期钩子函数,比如created,attached,destroyed等等。在这些钩子函数中,我们可以封装一些自定义的逻辑。和传统的MVC相比,可以理解为 Controller的逻辑被分散到了这些钩子函数中。
  • 私有资源(assets):Vue.js当中将用户自定义的指令、过滤器、组件等统称为资源。由于全局注册资源容易导致命名冲突,一个组件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。
       除此之外,同一颗组件树之内的组件之间还可以通过内建的事件API来进行通信。Vue.js提供了完善的定义、复用和嵌套组件的API,让开发者可以像搭积木一样用组件拼出整个应用的界面。
       组件大大提高了代码的效率和维护性以及复用率。

使用组件

注册
1.创建一个组件构造器:
   
   
  1. var MyComponent = Vue.extend({
  2.     //选项
  3. })
2.将构造器用做组件,用Vue.component(tag,constructor)注册:
   
   
  1. Vue.component('my-component',MyComponent)
3.在父实例的模块中以自定义元素<my-component>的形式使用:
   
   
  1. <div id = "example">
  2.     <my-component></my-component>
  3. </div>
       例子:
   
   
  1. <div id="example">
  2.   <my-component></my-component>
  3. </div>
  4. // 定义
  5. var MyComponent = Vue.extend({
  6.   template: '<div>A custom component!</div>'
  7. })
  8. // 注册
  9. Vue.component('my-component', MyComponent)
  10. // 创建根实例
  11. new Vue({
  12.   el: '#example'
  13. })
       渲染为:
   
   
  1. <div id = "example">
  2.     <div>A custom component!</div>
  3. </div>
         组件的模板替换了自定义元素,自定义元素的作用只是作为一个挂载点。可以用实例选项 replace 决定是否替换。
局部注册
        用实例选项components注册,不需要全局注册每个组件,可以让组件只能用在其他组件内:
   
   
  1. var Child = Vue.extend({ /* ... */ })
  2. var Parent = Vue.extend({
  3.   template: '...',
  4.   components: {
  5.     // <my-component> 只能用在父组件模板内
  6.     'my-component': Child
  7.   }
  8. })
       这种封装也适用于其它资源,如指令、过滤器和过渡。
注册语法糖
   
   
  1. // 在一个步骤中扩展与注册
  2. Vue.component('my-component', {
  3.   template: '<div>A custom component!</div>'
  4. })
  5.  
  6. // 局部注册也可以这么做
  7. var Parent = Vue.extend({
  8.   components: {
  9.     'my-component': {
  10.       template: '<div>A custom component!</div>'
  11.     }
  12.   }
  13. })
组件选项问题
        传入Vue构造器的多数选项也可以用在Vue.extend()中,除了data和el,如果简单的把一个对象作为data选项传给Vue.extend(),则所有的实例将共享同一个data对象,因此 我们应当使用一个函数作为data选项,让这个函数返回一个新对象
   
   
  1. var MyComponent = Vue.extend({
  2.   data: function () {
  3.     return { a: 1 }
  4.   }
  5. })
模板解析
       Vue的模板是DOM模板,使用浏览器原生的解析器,所以它必须是有效的HTML片段,一些HTML元素对什么元素可以放在它里面有限制,常见的限制有:
  • a 不能包含其它的交互元素(如按钮,链接)
  • ul 和 ol 只能直接包含 li
  • select 只能包含 option 和 optgroup
  • table 只能直接包含 thead, tbody, tfoot, tr, caption, col, colgroup
  • tr 只能直接包含 th 和 td
       在实际中,这些限制会导致意外的结果。尽管在简单的情况下它可能可以工作,但是你不能依赖自定义组件在浏览器验证之前的展开结果。例如 <my-select><option>...</option></my-select> 不是有效的模板,即使 my-select 组件最终展开为 <select>...</select>。
       另一个结果是,自定义标签(包括自定义元素和特殊标签,如 <component>、<template>、 <partial> )不能用在 ul, select, table 等对内部元素有限制的标签内。放在这些元素内部的自定义标签将被提到元素的外面,因而渲染不正确。
       对于自定义元素,应当使用 is 特性:
    
    
  1. <table>
  2.   <tr is="my-component"></tr>
  3. </table>
  4. //<template> 不能用在 <table> 内,这时应使用 <tbody>,<table> 可以有多个 <tbody>
  5. <table>
  6.   <tbody v-for="item in items">
  7.     <tr>Even row</tr>
  8.     <tr>Odd row</tr>
  9.   </tbody>
  10. </table>

Props

使用props传递数据
组件实例的作用域是孤立的,可以 使用props把数组传给子组件,props是组件数据的一个字段,期望从父组件传下来,子组件需要显式地用props选项声明props:
    
    
  1. Vue.component('child', {
  2.   // 声明 props
  3.   props: ['msg'],
  4.   // prop 可以用在模板内
  5.   // 可以用 `this.msg` 设置
  6.   template: '<span>{{ msg }}</span>'
  7. })
然后向它传入一个普通字符串:
    
    
  1. <child msg="hello!"></child>
动态props
        用v-bind绑定动态props到父组件的数据,每当父组件的数据变化时,也会传导给子组件:
   
   
  1. <div>
  2.   <input v-model="parentMsg">
  3.   <child v-bind:my-message="parentMsg"></child>
  4. //<child :my-message="parentMsg"></child>
  5. </div>
props绑定类型
       prop默认是单向绑定,当父组件的属性变化时,将传导给子组件,但是反过来不会,这是为了防止子组件无意修改了父组件的状态,可以使用.sync或.once绑定修饰符显式地强制双向或单次绑定:
   
   
  1. <!-- 默认为单向绑定 -->
  2. <child :msg="parentMsg"></child>
  3. <!-- 双向绑定 -->
  4. <child :msg.sync="parentMsg"></child>
  5. <!-- 单次绑定 -->
  6. <child :msg.once="parentMsg"></child>
        如果 prop 是一个对象或数组,是按引用传递。在子组件内修改它会影响父组件的状态,不管是使用哪种绑定类型。

父子组件通信

父链
       子组件可以用this.$parent访问它的父组件,根实例的后代可以用this.$root访问它,父组件有一个数组this.$children,包含它所有的子元素
自定义事件
       Vue实例实现了一个自定义事件接口,用于在组件树中通信,这个事件系统独立于原生DOM事件,用法也不同,每一个Vue实例都是一个事件触发器:
  • 使用 $on() 监听事件;
  • 使用 $emit() 在它上面触发事件;
  • 使用 $dispatch() 派发事件,事件沿着父链冒泡;
  • 使用 $broadcast() 广播事件,事件向下传导给所有的后代。
        不同于 DOM 事件,Vue 事件在冒泡过程中第一次触发回调之后自动停止冒泡,除非回调明确返回 true。
   
   
  1. <!-- 子组件模板 -->
  2. <template id="child-template">
  3.   <input v-model="msg">
  4.   <button v-on:click="notify">Dispatch Event</button>
  5. </template>
  6.  
  7. <!-- 父组件模板 -->
  8. <div id="events-example">
  9.   <p>Messages: {{ messages | json }}</p>
  10.   <child></child>
  11. </div>
  12. // 注册子组件
  13. // 将当前消息派发出去
  14. Vue.component('child', {
  15.   template: '#child-template',
  16.   data: function () {
  17.     return { msg: 'hello' }
  18.   },
  19.   methods: {
  20.     notify: function () {
  21.       if (this.msg.trim()) {
  22.         this.$dispatch('child-msg', this.msg)
  23.         this.msg = ''
  24.       }
  25.     }
  26.   }
  27. })
  28.  
  29. // 初始化父组件
  30. // 将收到消息时将事件推入一个数组
  31. var parent = new Vue({
  32.   el: '#events-example',
  33.   data: {
  34.     messages: []
  35.   },
  36.   // 在创建实例时 `events` 选项简单地调用 `$on`
  37.   events: {
  38.     'child-msg': function (msg) {
  39.       // 事件回调内的 `this` 自动绑定到注册它的实例上
  40.       this.messages.push(msg)
  41.     }
  42.   }
  43. })
       效果:
 使用v-on绑定自定义事件
        在模板中子组件用到的地方声明事件处理器,为此子组件可以用v-on监听自定义事件:
   
   
  1. <child v-on:child-msg="handleIt"></child>
       当子组件触发了 "child-msg" 事件,父组件的 handleIt 方法将被调用。所有影响父组件状态的代码放到父组件的 handleIt 方法中;子组件只关注触发事件。
子组件索引
使用v-ref为子组件指定一个索引ID,可以直接访问子组件
   
   
  1. <div id="parent">
  2.   <user-profile v-ref:profile></user-profile>
  3. </div>
  4. var parent = new Vue({ el: '#parent' })
  5. // 访问子组件
  6. var child = parent.$refs.profile

使用Slot分发内容

       内容分发:混合父组件的内容与子组件自己的模板的方式,使用特殊的<slot>元素作为原始内容的插槽。
编译作用域
        父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。
       绑定子组件内的指令到一个组件的根节点:
    
    
  1. Vue.component('child-component', {
  2.   // 有效,因为是在正确的作用域内
  3.   template: '<div v-show="someChildProperty">Child</div>',
  4.   data: function () {
  5.     return {
  6.       someChildProperty: true
  7.     }
  8.   }
  9. })
       类似地, 分发内容是在父组件作用域内编译
单个slot
       父组件的内容将被抛弃,除非子组件模板包含<slot>,如果子组件模板只有一个没有特性的slot,父组件的整个内容将插到slot所在的地方并替换它。
       <slot> 标签的内容视为回退内容。回退内容在子组件的作用域内编译,当宿主元素为空并且没有内容供插入时显示这个回退内容。
       假定 my-component 组件有下面模板:
    
    
  1. <div>
  2.   <h1>This is my component!</h1>
  3.   <slot>
  4.     如果没有分发内容则显示我。
  5.   </slot>
  6. </div>
       父组件模板:
    
    
  1. <my-component>
  2.   <p>This is some original content</p>
  3.   <p>This is some more original content</p>
  4. </my-component>
       渲染结果:
    
    
  1. <div>
  2.   <h1>This is my component!</h1>
  3.   <p>This is some original content</p>
  4.   <p>This is some more original content</p>
  5. </div>
具名slot
       <slot>元素可以用一个特殊特性name配置如何分发内容,多个slot可以有不同的名字,具名slot将匹配内容片段中有对应slot特性的元素。
       仍然可以有一个匿名 slot,它是默认 slot,作为找不到匹配的内容片段的回退插槽。如果没有默认的 slot,这些找不到匹配的内容片段将被抛弃。

动态组件

       多个组件可以使用同一个挂载点,然后动态地在它们之间切换,使用保留的<component>元素,动态地绑定到它的is特性:
   
   
  1. new Vue({
  2.   el: 'body',
  3.   data: {
  4.     currentView: 'home'
  5.   },
  6.   components: {
  7.     home: { /* ... */ },
  8.     posts: { /* ... */ },
  9.     archive: { /* ... */ }
  10.   }
  11. })
  12. <component :is="currentView">
  13.   <!-- 组件在 vm.currentview 变化时改变 -->
  14. </component>
keep-alive
        把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。
   
   
  1. <component :is="currentView" keep-alive>
  2.   <!-- 非活动组件将被缓存 -->
  3. </component>
activate钩子
       控制组件切换时长,activate 钩子只作用于动态组件切换或静态组件初始化渲染的过程中,不作用于使用实例方法手工插入的过程中。
   
   
  1. Vue.component('activate-example', {
  2.   activate: function (done) {
  3.     var self = this
  4.     loadDataAsync(function (data) {
  5.       self.someData = data
  6.       done()
  7.     })
  8.   }
  9. })
transition-mode
       transition-mode 特性用于指定两个动态组件之间如何过渡。
       在默认情况下,进入与离开平滑地过渡。这个特性可以指定另外两种模式:
  • in-out:新组件先过渡进入,等它的过渡完成之后当前组件过渡出去。
  • out-in:当前组件先过渡出去,等它的过渡完成之后新组件过渡进入。
示例:
    
    
  1. <!-- 先淡出再淡入 -->
  2. <component
  3.   :is="view"
  4.   transition="fade"
  5.   transition-mode="out-in">
  6. </component>
  7. .fade-transition {
  8.   transition: opacity .3s ease;
  9. }
  10. .fade-enter, .fade-leave {
  11.   opacity: 0;
  12. }
Vue.js 组件 API 来自三部分——prop,事件和 slot:
  • prop 允许外部环境传递数据给组件;
  • 事件 允许组件触发外部环境的 action;
  • slot 允许外部环境插入内容到组件的视图结构内。
Logo

前往低代码交流专区

更多推荐