天天看點

vue2.x學習筆記(十六)

元件中的插槽

在2.6.0的版本中,vue為具名插槽和作用域插槽引入了一個新的統一的文法(即【v-slot】指令),它取代了【slot】和【slot-scope】這兩個目前已經被廢棄但是還沒被移除且仍在文檔中的attribute。

插槽内容

vue實作了一套内容分發的api,這套api的設計靈感來源于web components的規範草案,将<slot>元素作為承載分發内容的出口。

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

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

當這個元件被渲染的時候,<slot></slot>就會被替換為Your Profile。這裡的<slot></slot>就是插槽,插槽内可以包含任何模闆代碼,包括html:

甚至其他元件:

如果<navigation-link>元件沒有包含一個<slot>元素的話,則該元件起始标簽和結束标簽之間的任何内容都會被抛棄,因為它們沒有能夠找到對應的插槽進行内容替換(分發)。

插槽的編譯作用域

當你想要在一個插槽中使用資料的時候,比如:

該插槽跟模闆的其他地方一樣,都可以通路相同的執行個體屬性(也就是和父級模闆相同的作用域),而不能通路<navigation-link>的作用域。例如下例中的url是通路不到的:

請記住一條規則:父級模闆裡的所有内容都是在父級作用域中編譯的,而子模闆裡的所有内容都是在子作用域中編譯的。

後備内容

有的時候為一個插槽設定具體的後備(也就是預設的)内容是十分有用的,這些後備内容隻會在沒有提供内容的時候被渲染。

例如在一個<submit-button>元件中:

我們可能會希望這個<button>内絕大多數情況下都渲染文本submit。為了将submit作為後備内容,我們可以将它放在<slot>标簽内:

現在當在一個父級元件中使用<submit-button>子元件且不提供任何插槽内容的時候:

後備内容submit就會被渲染:

而當我們提供内容的時候,這個提供的内容就會被渲染,進而代替後備的内容。

具名插槽

關于具名插槽的内容自2.6.0起有所更新,這裡是官方文檔日前的最新文法,對于舊的文法不做理會。

有的時候我們可能會需要多個插槽,例如對于一個帶有如下模闆的<base-layout>元件:

對于這樣的場景,<slot>元素擁有一個特殊的屬性:name用來定義額外的插槽:

特别的一點在于,一個不帶name的<slot>出口會帶有隐藏名字【default】。

而在向具名插槽提供内容的時候,我們就可以在一個<template>元素上使用【v-slot】指令,并以【v-slot:插槽名】指令的參數形式提供其名稱:

現在<template>元素中的所有内容都會被傳入到相應的插槽中。任何沒有被包裹在帶有【v-slot】指令的<template>中的内容,都會被視為預設插槽的内容。

雖然機制是這樣的,但是為了讓頁面邏輯更明确一些,依然可以在一個<template>中包裹預設插槽的内容。

任何一種寫法都會渲染出:

要注意的是,【v-slot】指令隻能添加在<template>上(隻有一種例外情況,在後面的作用域插槽會說明),這一點和已經廢棄的【slot】屬性不同。

作用域插槽

關于作用域插槽的内容自2.6.0起有所更新,這裡是官方文檔日前的最新文法,對于舊的文法不做理會。

有的時候讓插槽内容能夠通路子元件中才有的資料是很有用的。例如,設想一個帶有如下模闆的<current-user>元件:

我們可能會想要換掉備用的内容,用名而非姓來顯示,如下:

然而上面的代碼并不會如期望的那樣工作,因為隻有<current-user>元件才可以通路到user對象,而我們提供的插槽内容實在父級模闆中渲染的。

是以,為了讓user對象能夠在父級元件的插槽内容中可用,我們需要将user對象作為<slot>元素的一個綁定屬性:

綁定在<slot>元素上的屬性就被稱為插槽prop。現在在父級的作用域中,我們就可以使用帶值的【v-slot】指令來定義我們提供的插槽prop的名字:

在這個例子中,我們選擇将包含所有插槽prop的對象命名為slotProps,但你也可以使用任意你喜歡的名字。

獨占預設插槽的縮寫文法

在上述的情況下,當被提供的内容隻有預設插槽的時候,元件的标簽是可以被當做插槽的模闆來使用的,這樣我們就可以把【v-slot】指令直接用在元件上而不需要包裹一層<template>:

這種寫法還可以更簡單,就像假定未知名的内容對應預設插槽一樣,不帶參數的【v-slot】指令也被假定對應預設插槽:

但是要注意,預設插槽的縮寫文法不能和具名插槽混用,因為它會導緻作用域不明确:

建議還是依照規範去寫而非使用縮寫,這樣能有效避免一些不必要的錯誤或麻煩。是以在存在多個插槽的情況下,請始終為所有的插槽使用完整的基于<template>的文法:

結構插槽prop

作用域插槽的内部工作原理其實就是将你的插槽内容包括在一個傳入單個參數的函數裡:

這就意味着,【v-slot】指令的值實際上可以是任何能夠作為函數定義的參數的javascript表達式。是以在支援的環境下(單檔案元件或現代浏覽器),你都可以使用es205解構來傳入具體的插槽prop,如下:

這樣就能夠使得模闆更加簡潔,尤其是在該插槽提供了多個prop的時候,它同樣開啟了prop重命名等其他可能,比如将user重命名為person:

你甚至可以定義後備内容,用于插槽prop是undefined的情形:

關于這部分的内容了解起來比較抽象,需要對javascript中解構的知識有一定的基礎。

動态插槽名

在vue2.6.0中新增了一個文法,可以将動态指令參數用在【v-slot】指令上,來定義動态的插槽名:

具名插槽的縮寫

在vue2.6.0中,新增了具名插槽的縮寫。即跟【v-on】指令和【v-bind】指令一樣,【v-slot】指令也有縮寫,即把參數之前的所有内容(v-slot:)替換為字元【#】,例如【v-slot:header】可以被縮寫為【#header】:

然而和其他的指令一樣,該縮寫隻有在其有參數的時候才可用,這就意味着以下的文法是無效的:

如果你希望使用縮寫的話,必須始終以明确的插槽名取而代之:

其他示例

插槽prop允許我們将插槽轉換為可複用的模闆,這些模闆可以基于輸入的prop渲染出不同的内容。這一特性在設計封裝資料邏輯同時允許父級元件自定義部分布局的可複用元件的場景中是最有用的。

例如,我們要實作一個<todo-list>元件,它是一個清單且包含布局和過濾邏輯:

我們可以将每個todo作為父級元件的插槽,以此來通過父級元件對其進行控制,然後将todo作為一個插槽的prop進行綁定:

現在當我們使用<todo-list>元件的時候,我們就可以選擇為todo定義一個不一樣的<template>作為替代方案,并且可以從子元件中擷取資料。

這隻是作用域插槽用武之地的冰山一角,可以通過浏覽各種第三方庫的源碼實作來深入了解作用域插槽的應用,諸如vue virtual scroller、vue promised和portal vue等庫。

廢棄了的文法

【v-slot】指令自vue 2.6.0起被引入,提供更好的支援【slot】和【slot-scope】屬性的api替代方案。雖然在接下來的所有2.x版本中的【slot】和【slot-scope】屬性依然會被支援,但是它們已經被官方廢棄且不會再在vue 3中出現。

帶有【slot】屬性的具名插槽

在<template>上使用特殊的【slot】屬性,可以将内容從父級傳給具名插槽(新的文法使用【v-slot:插槽名】指令代替)。

或者可以直接把【slot】屬性使用在一個普通的元素上:

這裡其實還有一個未命名的插槽(預設插槽),用來捕獲所有未被比對的内容。上述兩個示例的html渲染結果均為:

帶有【slot-scope】屬性的作用域插槽

在<template>上使用特殊的【slot-scope】屬性,可以接收傳遞給插槽的prop(新的文法使用【v-slot:插槽名="參數對象名"】指令代替)。

這裡的【slot-scope】屬性聲明了被接收的prop對象會作為slotProps變量存在于<template>的作用域中,你可以像命名javascript的函數參數一樣随意命名slotProps。

這裡的slot="default"還可以被忽略為隐性寫法:

另外,【slot-scope】屬性也可以直接用于非<template>元素(包括元件):

【slot-scope】屬性的值還可以接收任何有效的可以出現在函數定義的參數位置上的javascript表達式,這意味着在支援的環境(單檔案元件或現代浏覽器)下,你都可以在表達式中使用es2015解構,如下:

使用上面的<todo-list>作為示例,與它等價的使用【slot-scope】屬性的代碼是:

因為新的文法(v-slot)是在2.6.0+的版本才被支援的,是以如果低于此版本的話要注意使用舊版的文法或更新新的vue版本(更建議)。

"我還是很喜歡你,像木石前盟心誓許,無論結局。"

你要去做一個大人,不要回頭,不要難過。

繼續閱讀