1. 程式人生 > >Vue基礎:元件--slot、非同步元件、遞迴元件及其他

Vue基礎:元件--slot、非同步元件、遞迴元件及其他

slot分發內容

為了讓元件可以組合,我們需要一種方式來混合父元件的內容與子元件自己的模板。這個過程被稱為內容分發。Vue中使用特殊的 <slot> 元素作為原始內容的插槽。

問題(編譯作用域)

message 應該繫結到父元件的資料,還是繫結到子元件的資料?

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

答案是父元件。父元件模板的內容在父元件作用域內編譯;子元件模板的內容在子元件作用域內編譯。

單個slot

除非子元件模板包含至少一個 <slot> 插口,否則父元件的內容將會被丟棄

。當子元件模板只有一個沒有屬性的 slot 時,父元件整個內容片段將插入到 slot 所在的 DOM 位置,並替換掉 slot 標籤本身。在 <slot> 標籤中的任何內容都被視為備用內容。備用內容在子元件的作用域內編譯,並且只有在宿主元素為空,且沒有要插入的內容時才顯示備用內容。

具名slot

<slot> 元素可以用一個特殊的屬性 name 來配置如何分發內容。多個 slot 可以有不同的名字。具名 slot 將匹配內容片段中有對應 slot 特性的元素。仍然可以有一個匿名 slot,它是預設 slot,作為找不到匹配的內容片段的備用插槽。如果沒有預設的 slot,這些找不到匹配的內容片段將被拋棄。

<div id="app">
  <my-component>
    <template>
      <p>父元件分發內容</p>
    </template>
  </my-component>
  <my-component2>
    <b>內容a</b>
    <template slot="title">
      父元件分發的title
    </template>
    <b>內容b</b>
  </my-component2
>
</div> <script> const app = new Vue({ el: '#app', components: { 'my-component': { template: `<div><slot><p>備選內容</p></slot></div>` }, 'my-component2': { template: `<div><slot name="title">備選title</slot><slot><b>備選內容</b></slot></div>` } } }); </script>

渲染結果:

<div>
  <p>父元件分發內容</p>
</div> 
<div>
  父元件分發的title
  <b>內容a</b>  
  <b>內容b</b>
</div>

作用域插槽

在子元件插槽中可以通過slot插槽標籤的屬性將資料傳遞到父元件要分發的內容當中,父元件要通過<template scope=""></template>模板來接收子元件插槽傳遞上來的資料。

<my-component>
  <template scope="props">
    <p>父元件分發內容</p>
    <p>{{props.message}}</p>
  </template>
</my-component>

<script>
const app = new Vue({
    el: '#app',
    components: {
    'my-component': {
        template: `<div><slot message="哈哈"></slot></div>`
    }
  }
});
</script>

渲染結果:

<div>
  <p>父元件分發內容</p> 
  <p>哈哈</p>
</div>

具代表性的列表組:

<my-list :list="['a.com', 'b.com']">
  <template slot="item" scope="props">
    <li class="link"><a>{{props.link}}</a></li>
  </template>
</my-list>

<script>
// ...
'my-list': {
  props: ['list'],
  template: `<ul><slot name="item" v-for="item in list" :link="item"></slot></ul>`
}
</script>

渲染結果:

<ul>
  <li class="link"><a>a.com</a></li>
  <li class="link"><a>b.com</a></li>
</ul>

動態元件

動態地繫結到它的 is 特性,讓多個元件可以使用同一個掛載點,並動態切換:

<component v-bind:is="currentView">
  <!-- 元件在 vm.currentview 變化時改變! -->
</component>
var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})

keep-alive可以把切換出去的元件保留在記憶體中,保留它的狀態,避免重新渲染。

<keep-alive>
  <component :is="currentView">
    <!-- 非活動元件將被快取! -->
  </component>
</keep-alive>

雜項

編寫可複用元件

可複用元件應當定義一個清晰的公開介面,同時也不要對其使用的外層資料作出任何假設。

  • Prop 允許外部環境傳遞資料給元件;
  • 事件允許從元件內觸發外部環境的副作用;
  • 插槽允許外部環境將額外的內容組合在元件中。
<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

注意:$refs 只在元件渲染完成後才填充,並且它是非響應式的。它僅僅是一個直接操作子元件的應急方案——應當避免在模板或計算屬性中使用 $refs
ref 被用來給元素或子元件註冊引用資訊。引用資訊將會註冊在父元件的 $refs 物件上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子元件上,引用就指向元件例項,可以通過$el,獲取DOM元素。

非同步元件

Vue.js 允許將元件定義為一個工廠函式,非同步地解析元件的定義。

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 將元件定義傳入 resolve 回撥函式
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})
Vue.component('async-webpack-example', function (resolve) {
  // 這個特殊的 require 語法告訴 webpack
  // 自動將編譯後的程式碼分割成不同的塊,
  // 這些塊將通過 Ajax 請求自動下載。
  require(['./my-async-component'], resolve)
})

webpack 2 + ES2015 的語法時可以這樣:

Vue.component(
  'async-webpack-example',
  () => import('./my-async-component')
)

元件命名約定

當註冊元件 (或者 prop) 時,可以使用 kebab-case (短橫線分隔命名)、camelCase (駝峰式命名) 或 PascalCase (單詞首字母大寫命名);在 HTML 模板中,請使用 kebab-case。

components: {
  myComponent: { /* ... */ }
}
<my-component/>

遞迴元件

一定要確保遞迴呼叫有終止條件,可以通過v-if進行控制。

元件間的迴圈引用

內聯模板

如果子元件有 inline-template 特性,元件將把它的內容當作它的模板,而不是把它當作分發內容。這讓模板編寫起來更靈活。

<my-component inline-template>
  <div>
    <p>這些將作為元件自身的模板。</p>
    <p>而非父元件透傳進來的內容。</p>
  </div>
</my-component>

但是 inline-template 讓模板的作用域難以理解。使用 template 選項在元件內定義模板或者在 .vue 檔案中使用 template 元素才是最佳實踐。

X-Template

另一種定義模板的方式是在 JavaScript 標籤裡使用 text/x-template 型別,並且指定一個 id。例如:

<script type="text/x-template" id="hello-world-template">
  <p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
  template: '#hello-world-template'
})

注意:在有很多大模板的演示應用或者特別小的應用中可能有用,其它場合應該避免使用,因為這將模板和元件的其它定義分離了。

對低開銷的靜態元件使用v-once

儘管在 Vue 中渲染 HTML 很快,不過當元件中包含大量靜態內容時,可以考慮使用 v-once將渲染結果快取起來,就像這樣:

Vue.component('terms-of-service', {
  template: '\
    <div v-once>\
      <h1>Terms of Service</h1>\
      ...很多靜態內容...\
    </div>\
  '
})