Vue 可復(fù)用性的組件詳解

7.1 使用組件的原因

作用:提高代碼的復(fù)用性

7.2 組件的使用方法

1. 全局注冊

Vue.component('my-component',{
    template:'<div>我是組件的內(nèi)容</div>'
})

var app = new Vue({
    el:'#app',
    data: {
    }
})

優(yōu)點:所有的Vue實例都可以用
缺點:權(quán)限太大,容錯率降低

2. 局部注冊

var app = new Vue({
    el:'#app',
    components:{
        'my-component':{
        template: '<div>我是組件的內(nèi)容</div>'
        }
    }
})

3. vue組件的模板在某些情況下會受到html標(biāo)簽的限制

比如 <table>中只能還有 <tr> , <td> 這些元素,所以直接在table中使用組件是無效的,此時可以使用is屬性來掛載組件

<table>
    <tbody is="my-component"></tbody>
</table>

7.3 組件使用的奇淫技巧

  1. 推薦使用小寫字母加-進行命名(必須) child, my-componnet命名組件
  2. template中的內(nèi)容必須被一個DOM元素包括 ,也可以嵌套
  3. 在組件的定義中,除了template之外的其他選項—data,computed,methods
  4. data必須是一個方法
var app = new Vue({
      el:'#app',
      data: {},
      components: {
        'btn-component': {
          template: '<button @click="count++">{{count}}</button>',
          data: function(){
            return {
              count: 0
            }
          }
        }
      }
    })

7.4 使用props傳遞數(shù)據(jù) 父親向兒子傳遞數(shù)據(jù)

  1. 在組件中使用props來從父親組件接收參數(shù),注意,在props中定義的屬性,都可以在組件中直接使用
  2. props來自父級,而組件中data return的數(shù)據(jù)就是組件自己的數(shù)據(jù),兩種情況作用域就是組件本身,可以在templatecomputedmethods中直接使用
  3. props的值有兩種,一種是字符串?dāng)?shù)組,一種是對象
  4. 可以使用 v-bind動態(tài)綁定父組件來的內(nèi)容

7.5 單向數(shù)據(jù)流

  • 解釋 : 通過 props 傳遞數(shù)據(jù)都是單向的了, 也就是父組件數(shù)據(jù)變化時會傳遞給子組件,但是反過來不行。
  • 目的 :是盡可能將父子組件解稿,避免子組件無意中修改了父組件的狀態(tài)。
  • 應(yīng)用場景: 業(yè)務(wù)中會經(jīng)常遇到兩種需要改變 prop 的情況
  1. 一種是父組件傳遞初始值進來,子組件將它作為初始值保存起來,在自己的作用域下可以隨意使用和修改。這種情況可以在組件 data 內(nèi)再聲明一個數(shù)據(jù),引用父組件的 prop
  • 步驟一:注冊組件
  • 步驟二:將父組件的數(shù)據(jù)傳遞進來,并在子組件中用props接收
  • 步驟三:將傳遞進來的數(shù)據(jù)通過初始值保存起來
<div id="app">
    <my-comp init-count="666"></my-comp>
</div>
<script>
    var app = new Vue({
        el:'#app',
        components:{
            'my-comp':{
                props:['init-count'],
                template:'<div>{{init-count}}</div>',
                data:function () {
                    return{
                        count:this.initCount
                    }
                 }
              }
          }
     })
</script>
  1. 另一種情況就是 prop 作為需要被轉(zhuǎn)變的原始值傳入。這種情況用計算屬性就可以了
  • 步驟一:注冊組件
  • 步驟二:將父組件的數(shù)據(jù)傳遞進來,并在子組件中用props接收
  • 步驟三:將傳遞進來的數(shù)據(jù)通過計算屬性進行重新計算
<div id="app">
    <inputtype="text" v-model="width">
    <my-comp :width="width"></my-comp>
</div>
--------------------------------------------
var app = new Vue({
    el:'#app',
    data:{
          width:''
    },
    components:{
          'my-comp':{
                props:['init-count','width'],
                template:'<div :style="style">{{init-count}}</div>',
                computed:{
                       style:function () {
                               return{
                                    width:this.width+'px',
                                    background:'red'
                                }
                         }
                   }
            }
      }
})

7.6 數(shù)據(jù)驗證

  • @ vue組件中camelCased (駝峰式) 命名與 kebab-case(短橫
    線命名)
  • @ 在html中, myMessage 和 mymessage 是一致的,因此在組件中的html
    中使用必須使用kebab-case(短橫線)命名方式。在html中不允許使用駝
    峰!!!!!!
  • @ 在組件中, 父組件給子組件傳遞數(shù)據(jù)必須用短橫線。在template中,必
    須使用駝峰命名方式,若為短橫線的命名方式。則會直接保錯。
  • @ 在組件的data中,用this.XXX引用時,只能是駝峰命名方式。若為短橫線
    的命名方式,則會報錯。

驗證的 type 類型可以是:

? String
? Number
? Boolean
? Object
? Array
? Function

Vue.component ( ’ my-compopent ’, {
    props : {
        //必須是數(shù)字類型
        propA : Number ,
        //必須是字符串或數(shù)字類型
        propB : [String , Number] ,
        //布爾值,如果沒有定義,默認(rèn)值就是 true
        propC: {
           type : Boolean ,
           default : true
        },
       //數(shù)字,而且是必傳
       propD: {
          type: Number ,
          required : true
        },
       //如果是數(shù)組或?qū)ο螅J(rèn)值必須是一個函數(shù)來返回
       propE: {
           type : Array ,
           default : function () {
                return [] ;
           }
        },
       //自定義一個驗證函數(shù)
       propF: {
          validator : function (value) {
              return value > 10;
          }
      }
   }
});

7.7 組件通信

組件關(guān)系可分為父子組件通信、兄弟組件通信、跨級組件通信

7.7.1 自定義事件—子組件給父組件傳遞數(shù)據(jù)

使用v-on 除了監(jiān)昕 DOM 事件外,還可以用于組件之間的自定義事件。
JavaScript 的設(shè)計模式 一一觀察者模式, dispatchEvent 和 addEventListener這兩個方法。 Vue 組件也有與之類似的一套模式,子組件用emit()來 觸發(fā)事件 ,父組件用on()來 監(jiān)昕子組件的事件 。

直接甩代碼

  • 第一步:自定義事件
  • 第二步: 在子組件中用$emit觸發(fā)事件,第一個參數(shù)是事件名,后邊的參數(shù)是要傳遞的數(shù)據(jù)
  • 第三步:在自定義事件中用一個參數(shù)來接受
<div id="app">
    <p>您好,您現(xiàn)在的銀行余額是{{total}}元</p>
    <btn-compnent@change="handleTotal"></btn-compnent>
</div>
<script src="js/vue.js"></script>
<script>
    //關(guān)于
    var app = new Vue({
        el:'#app',
        data:{
            total:0
        },
        components:{
            'btn-compnent':{
                template:'<div>\
                            <button @click="handleincrease">+1</button> \
                            <button @click="handlereduce">-1</button>\
                          </div>',
                data:function(){
                    return {
                        count:0
                    }
                 },
                 methods:{
                     handleincrease :function () {
                         this.count++;
                         this.$emit('change',this.count);
                     },
                      handlereduce:function () {
                          this.count--;
                          this.$emit('change',this.count);
                      }
                 }
            }
        },
        methods:{
            handleTotal:function (total) {
                this.total = total;
            }
         }
    })
</script>

7.7.2 在組件中使用v-model

$emit的代碼,這行代碼實際上會觸發(fā)一個 input事件, ‘input’后的參數(shù)就是傳遞給v-model綁定的屬性的值
v-model 其實是一個語法糖,這背后其實做了兩個操作

  • v--bind 綁定一個 value 屬性
  • v--on 指令給當(dāng)前元素綁定 input 事件
    要使用v-model,要做到:
    接收一個 value 屬性。
    在有新的 value 時觸發(fā) input 事件
<div id="app">
    <p>您好,您現(xiàn)在的銀行余額是{{total}}元</p>
    <btn-compnent v-model="total"></btn-compnent>
</div>
<script src="js/vue.js"></script>
<script>
    //關(guān)于
    var app = new Vue({
        el:'#app',
        data:{
            total:0
        },
        components:{
            'btn-compnent':{
                template:'<div>\
                             <button @click="handleincrease">+1</button> \
                             <button @click="handlereduce">-1</button>\
                          </div>',
                data:function(){
                    return {
                        count:0
                    }
                 },
                methods:{
                    handleincrease :function () {
                        this.count++;
----------------------注意觀察.這一行,emit的是input事件----------------
-
                        this.$emit('input',this.count);
                    },
                    handlereduce:function () {
                        this.count--;
                        this.$emit('input',this.count);
                    }
                 }
             }
        }
    })
</script>

7.7.3 非父組件之間的通信

<div id="app" >
    <my-acomponent></my-acomponent>
    <my-bcomponent></my-bcomponent>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js">
</script>
<script>
    Vue.component('my-acomponent',{
        template:'<div><button @click="handle">點擊我向B組件傳遞數(shù)據(jù)</button></div>',
        data:function () {
            return{
                aaa:'我是來自A組件的內(nèi)容'
            }
         },
         methods:{
             handle:function () {
                 this.$root.bus.$emit('lala',this.aaa);
             }
          }
    })
    Vue.component('my-bcomponent',{
        template:'<div></div>',
            created:function () {
                //A組件在實例創(chuàng)建的時候就監(jiān)聽事件---lala事件
                this.$root.bus.$on('lala',function (value) {
                    alert(value)
                });
             }
     })
    父鏈:this.$parent
    Vue.component('child-component',{
        template:'<button @click="setFatherData">通過點擊我修改父親的數(shù)據(jù)</button>',
        methods:{
            setFatherData:function () {
                this.$parent.msg = '數(shù)據(jù)已經(jīng)修改了'
            }
          }
    })
    子鏈:this.$refs
    提供了為子組件提供索引的方法,用特殊的屬性ref為其增加一個索引
    var app = new Vue({
        el:'#app',
        data:{
            //bus中介
            bus:new Vue(),
            msg:'數(shù)據(jù)還未修改',
            formchild:'還未拿到'
         },
         methods:{
             getChildData:function () {
                 //用來拿子組件中的內(nèi)容 ---- $refs
                 this.formchild = this.$refs.c.msg;
             }
           }
    })

7.8.1 什么是slot(插槽)

為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個過程被稱為 內(nèi)容分發(fā).Vue.js 實現(xiàn)了一個內(nèi)容分發(fā) API,使用特殊的 ‘slot’ 元素作為原始內(nèi)容的插槽。

7.8.2 編譯的作用域

在深入內(nèi)容分發(fā) API 之前,我們先明確內(nèi)容在哪個作用域里編譯。假定模板為:

<child-component>
    {{ message }}
</child-component>

message 應(yīng)該綁定到父組件的數(shù)據(jù),還是綁定到子組件的數(shù)據(jù)?答案是父組件。組件作用
域簡單地說是:

  • 父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;
  • 子組件模板的內(nèi)容在子組件作用域內(nèi)編譯。

7.8.3 插槽的用法

父組件的內(nèi)容與子組件相混合,從而彌補了視圖的不足混合父組件的內(nèi)容與子組件自己的模板

單個插槽:
<div id="app" >
    <my-component>
        <p>我是父組件的內(nèi)容</p>
    </my-component>
</div>

Vue.component('my-component',{
    template:'<div>\
                 <slot>\
                   如果父組件沒有插入內(nèi)容,我就作為默認(rèn)出現(xiàn)\
                 </slot>\
              </div>'
  })

具名插槽:
<name-component>
    <h3 slot="header">我是標(biāo)題</h3>
    <p>我是正文內(nèi)容</p>
    <p>正文內(nèi)容有兩段</p>
    <p slot="footer">我是底部信息</p>
</name-component>

Vue.component('name-component',{
    template:'<div>\
                 <div class="header">\n' +
                  '<slot name="header">\n' +
                    '\n' +
                  '</slot>\n' +
                '</div>\n' +
                '<div class="contatiner">\n' +
                  '<slot>\n' +
                    ' \n' +
                  '</slot>\n' +
                '</div>\n' +
                '<div class="footer">\n' +
                  '<slot name="footer">\n' +
                     '\n' +
                  '</slot> \n' +
                '</div>'+
             '</div>'
})

7.8.4 作用域插槽

作用域插槽是一種特殊的slot,使用一個可以復(fù)用的模板來替換已經(jīng)渲染的元素
——從子組件獲取數(shù)據(jù)
====template模板是不會被渲染的

Vue.component('my-component',{
    template:'<div>\
                 <slot text="我是來自子組件的數(shù)據(jù)" ss="fdjkfjlsd" name="abc">\
                 </slot>\
              </div>'
})

7.8.5 訪問slot

通過this.$slots.(NAME)

mounted:function () {
    //訪問插槽
    var header = this.$slots.header;
    var text = header[0].elm.innerText;
    var html = header[0].elm.innerHTML;
    console.log(header)
    console.log(text)
    console.log(html)
}

7.9 組件高級用法–動態(tài)組件
VUE給我們提供 了一個元素叫component
作用是: 用來動態(tài)的掛載不同的組件
實現(xiàn):使用is特性來進行實現(xiàn)的

 <div id="app" style="border: 1px solid; height: 340px">
    Vue提供了一個元素叫component,再使用is特性來實現(xiàn) <br>
    動態(tài):必須用到v-bind  <br>
    <br>
    <hr>
    <component v-bind:is="thisval"></component>
    <button @click="handle('A')">我是A</button>
    <button @click="handle('B')">我是B</button>
    <button @click="handle('C')">我是C</button>
  </div>

<script>
    var app = new Vue({
      el: '#app',
      data: {
        thisval: 'compA'
      },
      methods: {
        handle: function(val){
          this.thisval = 'comp' + val
        }
      },
      components: {
        compA: {
          template: '<div>我是A</div>'
        },
        compB: {
          template: '<div>我是B</div>'
        },
        compC: {
          template: '<div>我是C</div>'
        }
      }


    })
  </script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容