zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    学习Python的体会 (1)
    李敖的管理经
    《inside the c++ object model》读书笔记 之五 构造,解构,拷贝语意学
    《inside the c++ object model》读书笔记 之四 Function 语意学
    《inside the c++ object model》读书笔记 之三:Data语意学
    《inside the c++ object model》读书笔记 之六 执行期语意学
    排序算法插入排序/冒泡排序
    《inside the c++ object model》读书笔记 之七 站在对象模型的尖端
    《inside the c++ object model》读书笔记 之二:构造函数
    《inside the c++ object model》读书笔记 之一:对象
  • 原文地址:https://www.cnblogs.com/lrgupup/p/12900276.html
Copyright © 2011-2022 走看看