VUE大神的成長之路--組件

組件是vue最強大的功能之一,組件可以擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素, Vue.js 的編譯器為它添加特殊功能。在有些情況下,組件也可以是原生 HTML 元素的形式,以 is 特性擴展。
(一)注冊一個全局組件:(對于自定義標簽名,Vue.js不要求強制遵循W3C規則(小寫,并且包含一個短杠),盡管遵循這個規則比較好)

Vue.component('my-component',{
    //內容
})

組件在注冊之后,便可以在父實例的模塊中以自定義元素

<my-component></my-component>

的形式使用了,
重點:必須要確保在初始化根實例之前注冊了組件

舉例:

<div id="app">
   <my-component></my-component>
</div>

//全局注冊
Vue.component('my-component', {
    template: '<div>A custom component!</div>'
})

//初始化根實例
new Vue({
    el: "#app",
    data: {

    }
});

渲染結果:

<div id="app">
  <div>A custom component!</div>
</div>

一般在項目中,不必在全局注冊每個組件。通過使用組件實例選項注冊,可以使組件僅在另一個實例/組件的作用域中可用:

var Child = {
    template: '<div>A custom component!</div>'
}

new Vue({
    el: "#app",
    data: {

    },
    components: {
       'my-component': Child
    }
});

(二)DOM模板解析說明:
當使用 DOM 作為模版時(例如,將 el 選項掛載到一個已存在的元素上), 你會受到 HTML 的一些限制,因為 Vue 只有在瀏覽器解析和標準化 HTML 后才能獲取模版內容。尤其像這些元素 <ul> ,<ol>,<table> ,<select> 限制了能被它包裹的元素, 而一些像 <option> 這樣的元素只能出現在某些其它元素內部。

在自定義組件中使用這些受限制的元素時會導致一些問題,例如:

<table>
   <my-row>...</my-row>
</table>

以上中,<mr-row>是會被認為是無效內容,因為table中只能包裹td,th,tr等元素,變通方案是使用特殊的is屬性:

<table>
   <tr is="my-row"></tr>
</table>

(三)data必須是函數

我們先來看一個案例:

<div id="example-2">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>

var data = { counter: 0 }
Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  data: function () {
    return data
  }
})

new Vue({
  el: '#example-2'
})

你會發現,三個按鈕是聯動的!!!
因為上面中,dada是一個函數,我們返回給每個組件的實例引用了同一個data對象,由于這三個組件共享了同一個data,因此增加一個counter會影響所有的組件

為了解決這個問題,我們需要將data設置為一個方法函數

data: function () {
  return {
    counter: 0
  }
}

接下來,每個按鈕都會有了自己的狀態了

(四)構成組件
在Vue中,父子組件的關系可以總結為props down,events up。父組件通過props向下傳遞數據給子組件,子組件通過events給父組件發送消息

先來看看prop
組件實例的作用域是孤立的。這意味著不能(也不應該)在子組件的模板內直接引用父組件的數據。要讓子組件使用父組件的數據,我們需要通過子組件的props選項。
還是舉例說明:
子組件要顯示的用props選項聲明它期待獲得的數據:

Vue.component('my-component', {
    //聲明props
    props: ['message'],
    //就像data一樣,prop可以用在模板內
    //同樣也可以在vm實例中像"this.message" 這樣使用
    template: '<div>{{ message }}</div>'
})

在使用時可以這樣傳入一個普通字符串

<div id="app">
   <my-component message="hello!"></my-component>
</div>

特別注意:

HTML 特性是不區分大小寫的。所以,當使用的不是字符串模版,camelCased (駝峰式) 命名的 prop 需要轉換為相對應的 kebab-case (短橫線隔開式) 命名:

Vue.component('child', {
  // camelCase in JavaScript
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>

動態prop

在模板中,要動態地綁定父組件的數據到子模板的props,與綁定到任何普通的HTML特性相類似,就是用 v-bind。每當父組件的數據變化時,該變化也會傳導給子組件:

<div id="app">
   <my-component :my-message="a"></my-component>
</div>

var Child = {
     props: ['myMessage'],
     template: '<div>{{ myMessage }}</div>'
}

new Vue({
    el: "#app",
    data: {
        a:1
    },
    components: {
        'my-component': Child
    }
});

字面量語法VS動態語法

初學者常犯的一個錯誤就是使用字面量語法傳遞數值:

<!-- 傳遞了一個字符串 "1" -->
<comp some-prop="1"></comp>

因為它是一個字面 prop ,它的值是字符串 "1" 而不是number。如果想傳遞一個實際的number,需要使用 v-bind ,從而讓它的值被當作 JavaScript 表達式計算:

<!-- 傳遞實際的 number -->
<comp v-bind:some-prop="1"></comp>

單項數據流

prop是單向綁定的:當父組件的屬性變化時,將傳導給子組件,但是不會反過來。這是為了防止子組件無意修改了父組件的狀態--這會讓應用的數據流難以理解

另外,每次父組件更新時,子組件的所有prop都會更新為最新值,這意味著你不應該在子組件內改變prop

對于為什么會有修改prop中數據的沖動,VUE官方給出的解釋:
1.prop 作為初始值傳入后,子組件想把它當作局部數據來用;
2.prop 作為初始值傳入,由子組件處理成其它數據輸出。

對于這兩種原因,正確的應對方式時:
1.定義一個局部變量,并用prop的值初始化它:

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

2.定義一個計算屬性,處理 prop 的值并返回。

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

注意在 JavaScript 中對象和數組是引用類型,指向同一個內存空間,如果 prop 是一個對象或數組,在子組件內部改變它會影響父組件的狀態。

prop驗證

我們可以為組件的props指定驗證規格,如果傳入的數據不符合規格,vue會發出警告,當組件給其他人使用時,這很有用。
要指定驗證規格,需要用對象的形式,而不能用字符串數組
比如:

Vue.component('example',{
    props: {
       //基礎類型檢測(null意思是任何類型都可以)
       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 value > 10
          }
       }
    }
})

type可以使下面原生構造器

String, Number, Boolean, Function, Object, Array

type也可以是一個自定義構造器函數,使用instanceof檢測。

(四)自定義事件
父組件是使用props傳遞數據給子組件,但是如果子組件要把數據傳遞回去,應該怎么做呢?那就是接下來要說的是自定義事件

使用v-on綁定自定義事件

來看一個例子
使用 $on(eventName) 監聽事件
使用 $emit(eventName) 觸發事件

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>

Vue.component('button-counter', {
    template: '<button v-on:click="increment">{{ counter }}</button>',
    data: function(){
       return {
          counter: 0
       }
    },
    methods: {
       increment: function() {
          this.counter += 1
          this.$emit('increment')
       }
    }
})
new Vue({
    el: '#counter-event-example',
    data: {
       total: 0
    },
    methods: {
       incrementTotal: function() {
           this.total += 1
       }
    }
})

在本例中,子組件已經和它外部完全解耦了。它所做的只是報告自己的內部事件,至于父組件是否關心則與它無關。留意到這一點很重要。

使用自定義事件的表單輸入組件

自定義事件可以用來創建自定義的表單輸入組件,使用 v-model 來進行數據雙向綁定

非父子組件通信

有時候兩個組件也需要通信(非父子關系)在簡單的場景下,可以使用一個空的 Vue 實例作為中央事件總線:

var bus = new Vue()

// 觸發組件 A 中的事件

bus.$emit('id-selected', 1)

// 在組件 B 創建的鉤子中監聽事件

bus.$on('id-selected', function (id) {
  // ...
})

(五)使用Slot分發內容
為了讓組件可以組合,我們需要一種方式來混合父組件的內容與子組件自己的模板,這個過程被稱作內容分發
(1)編譯作用域
父組件模板的內容在父組件作用域內編譯;子組件模板的內容在子組件作用域內編譯。
如果要綁定作用域內的指令到一個組件的根節點,你應當在組件自己的模板上做:

Vue.component('child-component', {
    //有效,因為是在正確的作用域內
    template: '<div v-show="someChildProperty">Child</div>',
    data: function() {
        return {
          someChildProperty: true
        }
    }
})

(2)單個Slot

--------未完待續

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內容,還有我對于 Vue 1.0 印象不深的內容。關于...
    云之外閱讀 5,072評論 0 29
  • 下載安裝搭建環境 可以選npm安裝,或者簡單下載一個開發版的vue.js文件 瀏覽器打開加載有vue的文檔時,控制...
    冥冥2017閱讀 6,078評論 0 42
  • Vue 實例 屬性和方法 每個 Vue 實例都會代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,233評論 0 6
  • 此文基于官方文檔,里面部分例子有改動,加上了一些自己的理解 什么是組件? 組件(Component)是 Vue.j...
    陸志均閱讀 3,846評論 5 14
  • 什么是組件 組件(Component)是 Vue.js 最強大的功能之一。組件可以擴展 HTML 元素,封裝可重用...
    angelwgh閱讀 788評論 0 0