Vue组件化

组件化

组件注册

在注册一个组件我们,需要给它一个名字。比如在全局注册的时候

Vue.component('my-component-name',{})

该组件名就是Vue.component的第一个参数,推荐遵循W3C规范中的自定义组件名。这回帮助你避免个当前以及未来的HTML元素冲突。

局部注册的组件在其子组件中不可用。如果想要使用需要这样写

var ComponentA ={}
var COmponentB ={
    components:{
        'component-a':ComponentA
    }
}

或者通过Babel和webpack使用ES2015模块

import ComponentA from './ComponentA.vue'

export default{
    components:{
        ComponentA
    }
}

注意在ES2015+中,在对象中放一个类似ComponentA的变量名其实是:ComponentA:ComPonentA的缩写,即这个变量名同时是:

  • 用在模板中的自定义元素的名称
  • 包含了这个组件选项的变量名

Prop

HTML中attribue名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用DOM中的模板时,驼峰命名法的prop名需要使用其等价的短横线分割命名命名:

Vue.component('blog-post',{
    props:['postTitle'],
    template:'<h3>{{postTitle}}</h3>'
})
<blog-post post-title="hello!"></blog-post>

Prop类型

字符串数组形式

props:['title','likes','isPublished','commentIds','author']

通常每个prop都有自己制定的值类型

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

单向数据流

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

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

  1. 在prop用来传递一个初始值:这个子组件接下来希望将其作为一个本地的prop数据来使用

    在这种情况下,最好定义一个本地的data属性并将这个prop用作其初始值:

    props:['initialCounter'],
    data: function(){
        return {
            counter:this.initialCounter
        }
    }    
    
  2. 这个prop以一个原始的值传入且需要进行转换。在这种情况下,最好使用这个prop的值来定义一个计算属性:

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

Prop验证

props中的值提供一个带有验证需求的对象

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,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。

this.$emit('myEvent')

监听这个名字的短横线分割命名版本是不会有任何效果的

<my-component v-on:myEvent="doSometing"></my-component>

将原生事件绑定到组件

监听一个原生事件,你可以使用v-on.native修饰符

<base-input v-on:focus.native="onFocus"></base-input>

有的时候这是很有用的,但是尝试监听类似<input>的非常特定的元素时,会导致监听失败

<base-input>组件可能作为以下重构

<label>
    {{label}}
</label>
<input
  v-bind:"$attrs"
  v-bind:value:"value"
  v-on:input="$emit('input',$event.target.value)"
>

此时,父级的.native监听器将失败,不会产生任何报错,但是oFocus处理函数不会如逾期地被调用

vue提供了$listeners属性,可以配合v-on=$listener将所有的事件监听器指向这个组件的某个特定的子元素。为这些监听器创建一个类似inputListeners的计算属性是非常有用的

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  computed: {
    inputListeners: function () {
      var vm = this
      // `Object.assign` 将所有的对象合并为一个新对象
      return Object.assign({},
        // 我们从父级添加所有的监听器
        this.$listeners,
        // 然后我们添加自定义监听器,
        // 或覆写一些监听器的行为
        {
          // 这里确保组件配合 `v-model` 的工作
          input: function (event) {
            vm.$emit('input', event.target.value)
          }
        }
      )
    }
  },
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on="inputListeners"
      >
    </label>
  `
})

组件间的通信

父子组件

父组件通过props的方式向子组件传递数据,而通过$emit子组件可以向父组件通信。

  1. 父组件向子组件传值

在子组件article.vue中如何获取父组件section.vue中的数据articles:['红楼梦','西游记','三国演义']

//section 父组件
<template>
    <div class="section">
        <com-article :articles="articleList"></com-article>	
    </div>
</template>
<script>
    import comArticle from './test/article.vue'
    export default{
        name:'HelloWorld',
        components:{comArticle},
        data(){
            return {
                articleList:['红楼梦', '西游记', '三国演义']
            }
        }
    }
</script>

//子组件 article.vue
<template>
    <div>
        <span v-for="(item,index) in articles" :key="index">{{item}}</span>
    </div>
</template>
<script>
    export default{
        props:['articles']
    }
</script>

prop只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且prop只读,不可被修改,所有修改都会失效并警告

  1. 子组件向父组件传值

$emit绑定一个自定义事件,当这个语句被执行时,就会将参数arg传递给父组件,父组件通过v-on监听并接收参数。在上个例子的基础上,点击页面渲染出来的articleitem,父组件中显示在数组中的下标

//父组件
<template> 
    <div class="section">
        <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
    </div>
</template>
<script>
   import comArticle from './test/article.vue'
    export default{
        name:'HelloWorld',
        components:{comArticle},
        data(){
            return{
                currentIndex:-1,
                articleList:['红楼梦', '西游记', '三国演义']
            }
        },
        methods:{
        onEmitIndex(idx){
            this.currentIndex= idx
        }
    }
    }
</script>

//子组件
<template>
    <div v-for="(item,index) in articles" :key="index" @click="emitIndex(index)">
        {{item}}
    </div>
</template>
<script>
   export default{
    prop:['articles'],
    methods:{
      emitIndex(index){
        this.$emit('onEmitIndex',index)
     }        
    }
   }
</script>

非父子组件通信

eventBus又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念,就像是所有组件公用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以通知其他组件

  1. 初始化

首先需要创建一个事件总线并将其导出,以便其他模块可以使用或者监听它

//event-bus.js
import Vue from 'vue'
export const EventBus =new Vue();

  1. 发送事件

假设你有两个组件:additionNumshowNum,这两个组件可以是兄弟组件也可以是父子组件;这里我们以兄弟组件为例:

<tempalte>
    <div>
        <show-num-com></show-num-com>
        <addition-num-com></addition-num-com>
    </div>
</tempalte>
<script>
 import showNumCom from ‘./showNum.vue’
 import additionNumCom from './additionNum.vue'
 export default{
     components:{showNumCom,additionNumCom}
 }  
</script>

//additionNum 中发送事件
<template>
    <div>
        <button @click="additionHandle">
            +加法器
        </button>
    </div>	
</template>
<script>
   import {EventBus} from './event-bus.js'
   export default{
       data(){
           return{
               num:1
           }
       },
       methods:{
           additionHandle(){
               EventBus.$emit('addition',{
                   num:this.num++
               })
           }
       }
   }
</script>

  1. 接收事件
//showNum.vue中接收事件
<template>
    <div>
        计算和:{{count}}
    </div>
</template>
<script>
    import {EventBus} from './event-bus.js'
    export default{
        data(){
            return {
                count:0
            }
        },
        mounted(){
            EventBus.$on('addition',param=>{
                this.count =this.count +param.num
            })
        }
    }
</script>

  1. 移除事件监听者
import {eventBus} from 'event-bus.js'
EventBus.$off('addition',{})

vuex

组件生命周期

Vue实例从创建到销毁的过程,就是生命周期。也就是从开始创建,初始化数据,编译模板,挂载DOM--->渲染,

更新--->渲染,卸载等一系列过程,我们称这是Vue的生命周期

Vue 实例生命周期

  • beforCreate:在实例化初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用
  • created:实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据检视(data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el属性目前不可见
  • beforeMount: 在挂载开始之前被调用相关的render函数首次被调用
  • mounted: el被新创建的vm.el替换,并挂载到实例上去之后调用此钩子函数
  • beforeUpdate: 数据更新是调用,发生在虚拟DOM重新渲染和打补丁之前
  • updated:由于数据更新导致的虚拟DOM重新渲染和打补丁之前,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于DOM的操作。
  • beforeDestroy: 实例销毁之前被调用。在这一步,实例仍然完全可用
  • destroyed:Vue实例销毁后调用。调用后,Vue实例指示的所有东西都被解绑定,所有的事件监视器会被移除,所有的子实例也会被销毁

生命周期适用场景

beforeCreate:可以在这加loading事件,在加载实例时触发

created:初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用

mounted:挂载元素,获取到DOM节点

updated: 如果对数据统一处理,在这里写上相应函数

beforeDestory:一般在这里通过removeEventListener接触手动绑定的事件

Vue的父组件和子组件生命周期钩子函数执行顺序

  • 加载渲染过程

父beforeCreate ---> 父created ---->父beforeMount ---->子beforeCreate --->子created --->子beforeMount ---->

子mounted ------>父mounted

  • 子组件更新过程

父beforeUpdate ---->子beforeUpdata --->子updated ---->父updated

  • 销毁过程

父beforeDestroy --->子beforeDestroy ---->子destroyed ---->父 destoryed

先创建父组件的实例模型,然后创建子组件模型。渲染页面先渲染子组件页面,当子组件页面渲染完毕之后,渲染父组件页面。在beforeDestory中及时销毁自定义事件,否则可能造成内存泄露

1.png

原文地址:https://www.cnblogs.com/lrgupup/p/12900276.html