1. 程式人生 > >【vue大師晉級之路第二集:深入瞭解元件】第4章——插槽

【vue大師晉級之路第二集:深入瞭解元件】第4章——插槽

插槽內容

Vue 實現了一套內容分發的 API,這套 API 基於當前的 Web Components 規範草案,將 < slot> 元素作為承載分發內容的出口。

它允許你像這樣合成元件:

<navigation-link url="/profile">
  Your Profile
</navigation-link>

然後你在 < navigation-link> 的模板中可能會寫為:

<a
  v-bind:href="url"
  class="nav-link"
>
  <slot></slot>
</a>

當元件渲染的時候,這個 < slot> 元素將會被替換為“Your Profile”。插槽內可以包含任何模板程式碼,包括 HTML:

<navigation-link url="/profile">
  <!-- 新增一個 Font Awesome 圖示 -->
  <span class="fa fa-user"></span>
  Your Profile
</navigation-link>

甚至其它的元件:

<navigation-link url="/profile">
  <!-- 新增一個圖示的元件 -->
  <font-awesome-icon name="user"></font-awesome-icon>
  Your Profile
</navigation-link>

如果 < navigation-link> 沒有包含一個 < slot> 元素,則任何傳入它的內容都會被拋棄。

具名插槽 有些時候我們需要多個插槽。例如,一個假設的 < base-layout> 元件的模板如下:

<div class="container">
  <header>
    <!-- 我們希望把頁頭放這裡 -->
  </header>
  <main>
    <!-- 我們希望把主要內容放這裡 -->
  </main>
  <footer>
    <!-- 我們希望把頁尾放這裡 -->
  </footer>
</div>

對於這樣的情況,< slot> 元素有一個特殊的特性:name。這個特性可以用來定義額外的插槽:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

在向具名插槽提供內容的時候,我們可以在一個父元件的 < template> 元素上使用 slot 特性:

<base-layout>
  <template slot="header">
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template slot="footer">
    <p>Here's some contact info</p>
  </template>
</base-layout>

另一種 slot 特性的用法是直接用在一個普通的元素上:

<base-layout>
  <h1 slot="header">Here might be a page title</h1>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <p slot="footer">Here's some contact info</p>
</base-layout>

我們還是可以保留一個未命名插槽,這個插槽是預設插槽,也就是說它會作為所有未匹配到插槽的內容的統一出口。上述兩個示例渲染出來的 HTML 都將會是:

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

插槽的預設內容 有的時候為插槽提供預設的內容是很有用的。例如,一個 < submit-button> 元件可能希望這個按鈕的預設內容是“Submit”,但是同時允許使用者覆寫為“Save”、“Upload”或別的內容。

你可以在元件模板裡的 < slot> 標籤內部指定預設的內容來做到這一點。

<button type="submit">
  <slot>Submit</slot>
</button>

如果父元件為這個插槽提供了內容,則預設的內容會被替換掉。

編譯作用域 當你想在插槽內使用資料時,例如:

<navigation-link url="/profile">
  Logged in as {{ user.name }}
</navigation-link>

該插槽可以訪問跟這個模板的其它地方相同的例項屬性 (也就是說“作用域”是相同的)。但這個插槽不能訪問 < navigation-link> 的作用域。例如嘗試訪問 url 是不會工作的。牢記一條準則:

父元件模板的所有東西都會在父級作用域內編譯;子元件模板的所有東西都會在子級作用域內編譯。

作用域插槽 2.1.0+ 新增

有的時候你希望提供的元件帶有一個可從子元件獲取資料的可複用的插槽。例如一個簡單的 元件的模板可能包含了如下程式碼:

<ul>
  <li
    v-for="todo in todos"
    v-bind:key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

但是在我們應用的某些部分,我們希望每個獨立的待辦項渲染出和 todo.text 不太一樣的東西。這也是作用域插槽的用武之地。

為了讓這個特性成為可能,你需要做的全部事情就是將待辦項內容包裹在一個 < slot> 元素上,然後將所有和其上下文相關的資料傳遞給這個插槽:在這個例子中,這個資料是 todo 物件:

<ul>
  <li
    v-for="todo in todos"
    v-bind:key="todo.id"
  >
    <!-- 我們為每個 todo 準備了一個插槽,-->
    <!-- 將 `todo` 物件作為一個插槽的 prop 傳入。-->
    <slot v-bind:todo="todo">
      <!-- 回退的內容 -->
      {{ todo.text }}
    </slot>
  </li>
</ul>

現在當我們使用 < todo-list> 元件的時候,我們可以選擇為待辦項定義一個不一樣的 < template> 作為替代方案,並且可以通過 slot-scope 特性從子元件獲取資料:

<todo-list v-bind:todos="todos">
  <!-- 將 `slotProps` 定義為插槽作用域的名字 -->
  <template slot-scope="slotProps">
    <!-- 為待辦項自定義一個模板,-->
    <!-- 通過 `slotProps` 定製每個待辦項。-->
    <span v-if="slotProps.todo.isComplete">✓</span>
    {{ slotProps.todo.text }}
  </template>
</todo-list>

在 2.5.0+,slot-scope 不再限制在 < template> 元素上使用,而可以用在插槽內的任何元素或元件上。

解構 slot-scope 如果一個 JavaScript 表示式在一個函式定義的引數位置有效,那麼這個表示式實際上就可以被 slot-scope 接受。也就是說你可以在支援的環境下 (單檔案元件或現代瀏覽器),在這些表示式中使用 ES2015 解構語法。例如:

<todo-list v-bind:todos="todos">
  <template slot-scope="{ todo }">
    <span v-if="todo.isComplete">✓</span>
    {{ todo.text }}
  </template>
</todo-list>

這會使作用域插槽變得更乾淨一些。