vue組件自學

Vue組件

什麼是組件?

組件 (Component) 是 Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠表現爲用 is 特性進行了擴展的原生 HTML 元素。
使用組件vue

全局註冊

咱們已經知道,能夠經過如下方式建立一個 Vue 實例:webpack

new Vue({
  el: '#some-element',
  // 選項
})

要註冊一個全局組件,可使用 Vue.component(tagName, options)。例如:web

Vue.component('my-component', {
  // 選項
})

請注意,對於自定義標籤的命名 Vue.js 不強制遵循 W3C 規則 (小寫,而且包含一個短槓),儘管這被認爲是最佳實踐。數組

組件在註冊以後,即可以做爲自定義元素 <my-component></my-component> 在一個實例的模板中使用。注意確保在初始化根實例以前註冊組件:瀏覽器

<div id="example">
  <my-component></my-component>
</div>
// 註冊
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 建立根實例
new Vue({
  el: '#example'
})
渲染爲:
<div id="example">
  <div>A custom component!</div>
</div>
A custom component!

局部註冊

你沒必要把每一個組件都註冊到全局。你能夠經過某個 Vue 實例/組件的實例選項 components 註冊僅在其做用域中可用的組件:緩存

var Child = {
  template: '<div>A custom component!</div>'
}
new Vue({
  // ...
  components: {
    // <my-component> 將只在父組件模板中可用
    'my-component': Child
  }
})

這種封裝也適用於其它可註冊的 Vue 功能,好比指令。
DOM 模板解析注意事項服務器

當使用 DOM 做爲模板時 (例如,使用 el 選項來把 Vue 實例掛載到一個已有內容的元素上),你會受到 HTML 自己的一些限制,由於 Vue 只有在瀏覽器解析、規範化模板以後才能獲取其內容。尤爲要注意,像 <ul>、<ol>、<table>、<select> 這樣的元素裏容許包含的元素有限制,而另外一些像 <option> 這樣的元素只能出如今某些特定元素的內部。
在自定義組件中使用這些受限制的元素時會致使一些問題,例如:app

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

自定義組件 <my-row> 會被看成無效的內容,所以會致使錯誤的渲染結果。變通的方案是使用特殊的 is 特性:異步

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

應當注意,若是使用來自如下來源之一的字符串模板,則沒有這些限制:
<script type="text/x-template">
JavaScript 內聯模板字符串
.vue 組件
所以,請儘量使用字符串模板。
data 必須是函數async

構造 Vue 實例時傳入的各類選項大多數均可以在組件裏使用。只有一個例外:data 必須是函數。實際上,若是你這麼作:

Vue.component('my-component', {
  template: '<span>{{ message }}</span>',
  data: {
    message: 'hello'
  }
})

那麼 Vue 會中止運行,並在控制檯發出警告,告訴你在組件實例中 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 的確是一個函數了,所以 Vue 不會警告,
  // 可是咱們卻給每一個組件實例返回了同一個對象的引用
  data: function () {
    return data
  }
})
new Vue({
  el: '#example-2'
})
0 0 0

因爲這三個組件實例共享了同一個 data 對象,所以遞增一個 counter 會影響全部組件!這就錯了。咱們能夠經過爲每一個組件返回全新的數據對象來修復這個問題:

data: function () {
  return {
    counter: 0
  }
}
如今每一個 counter 都有它本身內部的狀態了:
0 0 0

組件組合

組件設計初衷就是要配合使用的,最多見的就是造成父子組件的關係:組件 A 在它的模板中使用了組件 B。它們之間必然須要相互通訊:父組件可能要給子組件下發數據,子組件則可能要將它內部發生的事情告知父組件。然而,經過一個良好定義的接口來儘量將父子組件解耦也是很重要的。這保證了每一個組件的代碼能夠在相對隔離的環境中書寫和理解,從而提升了其可維護性和複用性。
在 Vue 中,父子組件的關係能夠總結爲 prop 向下傳遞,事件向上傳遞。父組件經過 prop 給子組件下發數據,子組件經過事件給父組件發送消息。看看它們是怎麼工做的。

prop 向下傳遞,事件向上傳遞
Prop

使用 Prop 傳遞數據

組件實例的做用域是孤立的。這意味着不能 (也不該該) 在子組件的模板內直接引用父組件的數據。父組件的數據須要經過 prop 才能下發到子組件中。
子組件要顯式地用 props 選項聲明它預期的數據:

Vue.component('child', {
  // 聲明 props
  props: ['message'],
  // 就像 data 同樣,prop 也能夠在模板中使用
  // 一樣也能夠在 vm 實例中經過 this.message 來使用
  template: '<span>{{ message }}</span>'
})
而後咱們能夠這樣向它傳入一個普通字符串:
<child message="hello!"></child>
結果:
hello!
camelCase vs. kebab-case

HTML 特性是不區分大小寫的。因此,當使用的不是字符串模板時,camelCase (駝峯式命名) 的 prop 須要轉換爲相對應的 kebab-case (短橫線分隔式命名):

Vue.component('child', {
  // 在 JavaScript 中使用 camelCase
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})

<!-- 在 HTML 中使用 kebab-case -->
<child my-message="hello!"></child>
若是你使用字符串模板,則沒有這些限制。
動態 Prop

與綁定到任何普通的 HTML 特性相相似,咱們能夠用 v-bind 來動態地將 prop 綁定到父組件的數據。每當父組件的數據變化時,該變化也會傳導給子組件:

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>
你也可使用 v-bind 的縮寫語法:
<child :my-message="parentMsg"></child>
結果:

Message from parent
 
Message from parent

若是你想把一個對象的全部屬性做爲 prop 進行傳遞,可使用不帶任何參數的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。例如,已知一個 todo 對象:

todo: {
  text: 'Learn Vue',
  isComplete: false
}
而後:
<todo-item v-bind="todo"></todo-item>
將等價於:
<todo-item
  v-bind:text="todo.text"
  v-bind:is-complete="todo.isComplete"
></todo-item>

字面量語法 vs 動態語法

初學者常犯的一個錯誤是使用字面量語法傳遞數值:
<!-- 傳遞了一個字符串 "1" -->
<comp some-prop="1"></comp>
由於它是一個字面量 prop,它的值是字符串 "1" 而不是一個數值。若是想傳遞一個真正的 JavaScript 數值,則須要使用 v-bind,從而讓它的值被看成 JavaScript 表達式計算:
<!-- 傳遞真正的數值 -->
<comp v-bind:some-prop="1"></comp>

單向數據流

Prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,可是反過來不會。這是爲了防止子組件無心間修改了父組件的狀態,來避免應用的數據流變得難以理解。
另外,每次父組件更新時,子組件的全部 prop 都會更新爲最新值。這意味着你不該該在子組件內部改變 prop。若是你這麼作了,Vue 會在控制檯給出警告。
在兩種狀況下,咱們很容易忍不住想去修改 prop 中數據:
Prop 做爲初始值傳入後,子組件想把它看成局部數據來用;
Prop 做爲原始數據傳入,由子組件處理成其它數據輸出。
對這兩種狀況,正確的應對方式是:
定義一個局部變量,並用 prop 的值初始化它:

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}
定義一個計算屬性,處理 prop 的值並返回:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

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

Prop 驗證

咱們能夠爲組件的 prop 指定驗證規則。若是傳入的數據不符合要求,Vue 會發出警告。這對於開發給他人使用的組件很是有用。
要指定驗證規則,須要用對象的形式來定義 prop,而不能用字符串數組:

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
Symbol

type 也能夠是一個自定義構造器函數,使用 instanceof 檢測。
當 prop 驗證失敗,Vue 會拋出警告 (若是使用的是開發版本)。注意 prop 會在組件實例建立以前進行校驗,因此在 default 或 validator 函數裏,諸如 data、computed 或 methods 等實例屬性還沒法使用。
非 Prop 特性

所謂非 prop 特性,就是指它能夠直接傳入組件,而不須要定義相應的 prop。
儘管爲組件定義明確的 prop 是推薦的傳參方式,組件的做者卻並不總能預見到組件被使用的場景。因此,組件能夠接收任意傳入的特性,這些特性都會被添加到組件的根元素上。
例如,假設咱們使用了第三方組件 bs-date-input,它包含一個 Bootstrap 插件,該插件須要在 input 上添加 data-3d-date-picker 這個特性。這時能夠把特性直接添加到組件上 (不須要事先定義 prop):
<bs-date-input data-3d-date-picker="true"></bs-date-input>
添加屬性 data-3d-date-picker="true" 以後,它會被自動添加到 bs-date-input 的根元素上。
替換/合併現有的特性

假設這是 bs-date-input 的模板:
<input type="date" class="form-control">
爲了給該日期選擇器插件增長一個特殊的主題,咱們可能須要增長一個特殊的 class,好比:

<bs-date-input
  data-3d-date-picker="true"
  class="date-picker-theme-dark"
></bs-date-input>

在這個例子當中,咱們定義了兩個不一樣的 class 值:
form-control,來自組件自身的模板
date-picker-theme-dark,來自父組件
對於多數特性來講,傳遞給組件的值會覆蓋組件自己設定的值。即例如傳遞 type="large" 將會覆蓋 type="date" 且有可能破壞該組件!所幸咱們對待 class 和 style 特性會更聰明一些,這兩個特性的值都會作合併 (merge) 操做,讓最終生成的值爲:form-control date-picker-theme-dark。
自定義事件

咱們知道,父組件使用 prop 傳遞數據給子組件。但子組件怎麼跟父組件通訊呢?這個時候 Vue 的自定義事件系統就派得上用場了。
使用 v-on 綁定自定義事件

每一個 Vue 實例都實現了事件接口,即:
使用 $on(eventName) 監聽事件
使用 $emit(eventName) 觸發事件
Vue 的事件系統與瀏覽器的 EventTarget API 有所不一樣。儘管它們的運行起來相似,可是 $on 和 $emit 並非addEventListener 和 dispatchEvent 的別名。

另外,父組件能夠在使用子組件的地方直接用 v-on 來監聽子組件觸發的事件。
不能用 $on 偵聽子組件釋放的事件,而必須在模板裏直接用 v-on 綁定,參見下面的例子。

下面是一個例子:

<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="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
0
0 0

在本例中,子組件已經和它外部徹底解耦了。它所作的只是報告本身的內部事件,由於父組件可能會關心這些事件。請注意這一點很重要。
給組件綁定原生事件

有時候,你可能想在某個組件的根元素上監聽一個原生事件。可使用 v-on 的修飾符 .native。例如:
<my-component v-on:click.native="doTheThing"></my-component>
.sync 修飾符

2.3.0+
在一些狀況下,咱們可能會須要對一個 prop 進行「雙向綁定」。事實上,這正是 Vue 1.x 中的 .sync 修飾符所提供的功能。當一個子組件改變了一個帶 .sync 的 prop 的值時,這個變化也會同步到父組件中所綁定的值。這很方便,但也會致使問題,由於它破壞了單向數據流。因爲子組件改變 prop 的代碼和普通的狀態改動代碼毫無區別,當光看子組件的代碼時,你徹底不知道它什麼時候悄悄地改變了父組件的狀態。這在 debug 複雜結構的應用時會帶來很高的維護成本。
上面所說的正是咱們在 2.0 中移除 .sync 的理由。可是在 2.0 發佈以後的實際應用中,咱們發現 .sync 仍是有其適用之處,好比在開發可複用的組件庫時。咱們須要作的只是讓子組件改變父組件狀態的代碼更容易被區分。
從 2.3.0 起咱們從新引入了 .sync 修飾符,可是此次它只是做爲一個編譯時的語法糖存在。它會被擴展爲一個自動更新父組件屬性的 v-on 監聽器。
以下代碼
<comp :foo.sync="bar"></comp>
會被擴展爲:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
當子組件須要更新 foo 的值時,它須要顯式地觸發一個更新事件:
this.$emit('update:foo', newValue)
使用自定義事件的表單輸入組件

自定義事件能夠用來建立自定義的表單輸入組件,使用 v-model 來進行數據雙向綁定。要牢記:

<input v-model="something">
這不過是如下示例的語法糖:
<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">
因此在組件中使用時,它至關於下面的簡寫:
<custom-input
  v-bind:value="something"
  v-on:input="something = arguments[0]">
</custom-input>

因此要讓組件的 v-model 生效,它應該 (從 2.2.0 起是可配置的):
接受一個 value prop
在有新的值時觸發 input 事件並將新值做爲參數
咱們來看一個很是簡單的貨幣輸入的自定義控件:

<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
  template: '\
    <span>\
      $\
      <input\
        ref="input"\
        v-bind:value="value"\
        v-on:input="updateValue($event.target.value)"\
      >\
    </span>\
  ',
  props: ['value'],
  methods: {
    // 不是直接更新值,而是使用此方法來對輸入值進行格式化和位數限制
    updateValue: function (value) {
      var formattedValue = value
        // 刪除兩側的空格符
        .trim()
        // 保留 2 位小數
        .slice(
          0,
          value.indexOf('.') === -1
            ? value.length
            : value.indexOf('.') + 3
        )
      // 若是值尚不合規,則手動覆蓋爲合規的值
      if (formattedValue !== value) {
        this.$refs.input.value = formattedValue
      }
      // 經過 input 事件帶出數值
      this.$emit('input', Number(formattedValue))
    }
  }
})
$

固然,上面的例子仍是比較初級的。好比,用戶輸入多個小數點或句號也是容許的,好惡心吧!所以咱們須要一個複雜一些的例子,下面是一個更加完善的貨幣過濾器:

自定義組件的 v-model

2.2.0 新增
默認狀況下,一個組件的 v-model 會使用 value prop 和 input 事件。可是諸如單選框、複選框之類的輸入類型可能把 value 用做了別的目的。model 選項能夠避免這樣的衝突:

Vue.component('my-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean,
    // 這樣就容許拿 `value` 這個 prop 作其它事了
    value: String
  },
  // ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
上述代碼等價於:
<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>
注意你仍然須要顯式聲明 checked 這個 prop。

非父子組件的通訊

有時候,非父子關係的兩個組件之間也須要通訊。在簡單的場景下,可使用一個空的 Vue 實例做爲事件總線:

var bus = new Vue()
// 觸發組件 A 中的事件
bus.$emit('id-selected', 1)
// 在組件 B 建立的鉤子中監聽事件
bus.$on('id-selected', function (id) {
  // ...
})

在複雜的狀況下,咱們應該考慮使用專門的狀態管理模式。
使用插槽分發內容

在使用組件時,咱們經常要像這樣組合它們:

<app>
  <app-header></app-header>
  <app-footer></app-footer>
</app>

注意兩點:
<app> 組件不知道它會收到什麼內容。這是由使用 <app> 的父組件決定的。
<app> 組件極可能有它本身的模板。
爲了讓組件能夠組合,咱們須要一種方式來混合父組件的內容與子組件本身的模板。這個過程被稱爲內容分發 (即 Angular 用戶熟知的「transclusion」)。Vue.js 實現了一個內容分發 API,參照了當前 Web Components 規範草案,使用特殊的 <slot> 元素做爲原始內容的插槽。
編譯做用域

在深刻內容分發 API 以前,咱們先明確內容在哪一個做用域裏編譯。假定模板爲:
<child-component>
{{ message }}
</child-component>
message 應該綁定到父組件的數據,仍是綁定到子組件的數據?答案是父組件。組件做用域簡單地說是:
父組件模板的內容在父組件做用域內編譯;子組件模板的內容在子組件做用域內編譯。
一個常見錯誤是試圖在父組件模板內將一個指令綁定到子組件的屬性/方法:
<!-- 無效 -->
<child-component v-show="someChildProperty"></child-component>
假定 someChildProperty 是子組件的屬性,上例不會如預期那樣工做。父組件模板並不感知子組件的狀態。
若是要綁定子組件做用域內的指令到一個組件的根節點,你應當在子組件本身的模板裏作:

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

相似地,被分發的內容會在父做用域內編譯。
單個插槽

除非子組件模板包含至少一個 <slot> 插口,不然父組件的內容將會被丟棄。當子組件模板只有一個沒有屬性的插槽時,父組件傳入的整個內容片斷將插入到插槽所在的 DOM 位置,並替換掉插槽標籤自己。
最初在 <slot> 標籤中的任何內容都被視爲備用內容。備用內容在子組件的做用域內編譯,而且只有在宿主元素爲空,且沒有要插入的內容時才顯示備用內容。
假定 my-component 組件有以下模板:

<div>
  <h2>我是子組件的標題</h2>
  <slot>
    只有在沒有要分發的內容時纔會顯示。
  </slot>
</div>
父組件模板:
<div>
  <h1>我是父組件的標題</h1>
  <my-component>
    <p>這是一些初始內容</p>
    <p>這是更多的初始內容</p>
  </my-component>
</div>
渲染結果:
<div>
  <h1>我是父組件的標題</h1>
  <div>
    <h2>我是子組件的標題</h2>
    <p>這是一些初始內容</p>
    <p>這是更多的初始內容</p>
  </div>
</div>
具名插槽

<slot> 元素能夠用一個特殊的特性 name 來進一步配置如何分發內容。多個插槽能夠有不一樣的名字。具名插槽將匹配內容片斷中有對應 slot 特性的元素。
仍然能夠有一個匿名插槽,它是默認插槽,做爲找不到匹配的內容片斷的備用插槽。若是沒有默認插槽,這些找不到匹配的內容片斷將被拋棄。
例如,假定咱們有一個 app-layout 組件,它的模板爲:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
父組件模板:
<app-layout>
  <h1 slot="header">這裏多是一個頁面標題</h1>
  <p>主要內容的一個段落。</p>
  <p>另外一個主要段落。</p>
  <p slot="footer">這裏有一些聯繫信息</p>
</app-layout>
渲染結果爲:
<div class="container">
  <header>
    <h1>這裏多是一個頁面標題</h1>
  </header>
  <main>
    <p>主要內容的一個段落。</p>
    <p>另外一個主要段落。</p>
  </main>
  <footer>
    <p>這裏有一些聯繫信息</p>
  </footer>
</div>
在設計組合使用的組件時,內容分發 API 是很是有用的機制。
做用域插槽

2.1.0 新增
做用域插槽是一種特殊類型的插槽,用做一個 (能被傳遞數據的) 可重用模板,來代替已經渲染好的元素。
在子組件中,只需將數據傳遞到插槽,就像你將 prop 傳遞給組件同樣:

<div class="child">
  <slot text="hello from child"></slot>
</div>

在父級中,具備特殊特性 slot-scope 的 <template> 元素必須存在,表示它是做用域插槽的模板。slot-scope 的值將被用做一個臨時變量名,此變量接收從子組件傳遞過來的 prop 對象:

<div class="parent">
  <child>
    <template slot-scope="props">
      <span>hello from parent</span>
      <span>{{ props.text }}</span>
    </template>
  </child>
</div>
若是咱們渲染上述模板,獲得的輸出會是:
<div class="parent">
  <div class="child">
    <span>hello from parent</span>
    <span>hello from child</span>
  </div>
</div>

在 2.5.0+,slot-scope 能被用在任意元素或組件中而再也不侷限於 <template>。
做用域插槽更典型的用例是在列表組件中,容許使用者自定義如何渲染列表的每一項:

<my-awesome-list :items="items">
  <!-- 做用域插槽也能夠是具名的 -->
  <li
    slot="item"
    slot-scope="props"
    class="my-fancy-item">
    {{ props.text }}
  </li>
</my-awesome-list>
列表組件的模板:
<ul>
  <slot name="item"
    v-for="item in items"
    :text="item.text">
    <!-- 這裏寫入備用內容 -->
  </slot>
</ul>
解構

slot-scope 的值其實是一個能夠出如今函數簽名參數位置的合法的 JavaScript 表達式。這意味着在受支持的環境 (單文件組件或現代瀏覽器) 中,您還能夠在表達式中使用 ES2015 解構:
<child>
<span slot-scope="{ text }">{{ text }}</span>
</child>
動態組件

經過使用保留的 <component> 元素,動態地綁定到它的 is 特性,咱們讓多個組件可使用同一個掛載點,並動態切換:

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
<component v-bind:is="currentView">
  <!-- 組件在 vm.currentview 變化時改變! -->
</component>
也能夠直接綁定到組件對象上:
var Home = {
  template: '<p>Welcome home!</p>'
}
var vm = new Vue({
  el: '#example',
  data: {
    currentView: Home
  }
})
keep-alive

若是把切換出去的組件保留在內存中,能夠保留它的狀態或避免從新渲染。爲此能夠添加一個 keep-alive 指令參數:
<keep-alive>
<component :is="currentView">

<!-- 非活動組件將被緩存! -->

</component>
</keep-alive>
在 API 參考中查看更多 <keep-alive> 的細節。
雜項

編寫可複用組件

在編寫組件時,最好考慮好之後是否要進行復用。一次性組件間有緊密的耦合不要緊,可是可複用組件應當定義一個清晰的公開接口,同時也不要對其使用的外層數據做出任何假設。
Vue 組件的 API 來自三部分——prop、事件和插槽:
Prop 容許外部環境傳遞數據給組件;
事件容許從組件內觸發外部環境的反作用;
插槽容許外部環境將額外的內容組合在組件中。
使用 v-bind 和 v-on 的簡寫語法,模板的意圖會更清楚且簡潔:

<my-component
  :foo="baz"
  :bar="qux"
  @event-a="doThis"
  @event-b="doThat"
>
  <img slot="icon" src="...">
  <p slot="main-text">Hello!</p>
</my-component>

子組件引用

儘管有 prop 和事件,可是有時仍然須要在 JavaScript 中直接訪問子組件。爲此可使用 ref 爲子組件指定一個引用 ID。例如:
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 訪問子組件實例
var child = parent.$refs.profile
當 ref 和 v-for 一塊兒使用時,獲取到的引用會是一個數組,包含和循環數據源對應的子組件。
$refs 只在組件渲染完成後才填充,而且它是非響應式的。它僅僅是一個直接操做子組件的應急方案——應當避免在模板或計算屬性中使用 $refs。

異步組件

在大型應用中,咱們可能須要將應用拆分爲多個小模塊,按需從服務器下載。爲了進一步簡化,Vue.js 容許將組件定義爲一個工廠函數,異步地解析組件的定義。Vue.js 只在組件須要渲染時觸發工廠函數,而且把結果緩存起來,用於後面的再次渲染。例如:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 將組件定義傳入 resolve 回調函數
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

工廠函數接收一個 resolve 回調,在收到從服務器下載的組件定義時調用。也能夠調用 reject(reason) 指示加載失敗。這裏使用 setTimeout 只是爲了演示,實際上如何獲取組件徹底由你決定。推薦配合 webpack 的代碼分割功能 來使用:

Vue.component('async-webpack-example', function (resolve) {
  // 這個特殊的 require 語法告訴 webpack
  // 自動將編譯後的代碼分割成不一樣的塊,
  // 這些塊將經過 Ajax 請求自動下載。
  require(['./my-async-component'], resolve)
})
你能夠在工廠函數中返回一個 Promise,因此當使用 webpack 2 + ES2015 的語法時能夠這樣:
Vue.component(
  'async-webpack-example',
  // 該 `import` 函數返回一個 `Promise` 對象。
  () => import('./my-async-component')
)
當使用局部註冊時,也能夠直接提供一個返回 Promise 的函數:
new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

若是你是 Browserify 用戶,可能就沒法使用異步組件了,它的做者已經代表 Browserify 將「永遠不會支持異步加載」。Browserify 社區發現了一些解決方法,可能會有助於已存在的複雜應用。對於其餘場景,咱們推薦使用 webpack,由於它對異步加載進行了內置、全面的支持。

其餘敬請期待。。。

相關文章
相關標籤/搜索