深入理解Vue中的插槽、內容分發、具名插槽

2022-10-12 22:00:23
本篇文章給大家分享進階技巧,深入理解下Vue中的插槽、內容分發、具名插槽,希望對大家有所幫助。

前端(vue)入門到精通課程:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:

插槽 Slots簡介

Vue中元件的資料可以通過props進行傳遞,或者通過事件的方式進行獲取傳遞,但當需要接收模板內容(任意合法的模板內容,程式碼片段、Vue元件)時,就需要使用插槽來實現了。當然也可以通過函數語言程式設計間接實現;【相關推薦:】

1.png

  • 可以將插槽理解為js中的函數進行編譯
// 父元素傳入插槽內容
FancyButton('Click me!')

// FancyButton 在自己的模板中渲染插槽內容
function FancyButton(slotContent) {
    return `<button class="fancy-btn">
      ${slotContent}
    </button>`
}
登入後複製
  • 最好的封裝方式是將共性抽取到元件中,將不同點暴露為插槽 - 抽取共性,保留不同
  • 父元件模板的所有東西都會在父元件作用域中編譯,子元件模板的所有東西都會在子元件作用域內編譯 - 編譯作用域

slot-scope淺析

常規的slot可以用於自定義元件的模板,但只是限制於固定的模板,無法自定義內部的具體的某一項,即常規的slot無法實現對元件迴圈體的每一項進行不同的內容分發,此時可以通過slot-scope進行實現,本質上和slot一樣,不同點在於可以進行引數傳遞

//普通的元件定義
<ul>
    <li v-for="book in books" :key="book.id">
    {{ book.name }}
    </li>
</ul>


//slot-scope元件定義
<ul>
    <li v-for="book in books" :key="book.id">
        <slot :book="book" name="bookInfo">
            <!-- 預設內容 -->
            {{ book.name }}
        </slot>
    </li>
</ul>

//父元件使用
<book-list :books="books">
    <template slot-scope="slotProps" slot="bookInfo">
        <span v-if="slotProps.book.sale">限時優惠</span>
        {{ slotProps.book.name }}
    </template>
</book-list>
登入後複製

使用slot-scope時,當父元件使用該API,對應的插槽會替換模板中的slot進行展示

常用API淺析

具名插槽

在元件中定義多個插槽出口可以相容多個不同需求的相容性,使得多個插槽內容傳入到各自的插槽出口中;當插槽中設定了name屬性時,此插槽就被稱為具名插槽(named slots),沒有提供name的插槽會隱式命名為「default」

2.png

  • v-slot 可以簡寫為#,其值對應於插槽中的name對應的值;
  • 當在一個元件中同時存在預設插槽和具名插槽時,所有位於頂級的非template節點都被隱式的視為預設插槽的內容,因此可以省略預設插槽的template節點標籤;
<Com>
    <!-- 隱式的預設插槽 -->
    <!-- <p>A paragraph for the main content.</p>
    <p>And another one.</p> -->
    <template #default>
        <p>A paragraph for the main content.</p>
        <p>And another one.</p>
    </template>
    <template #footer>
        <p>Here's some contact info</p>
    </template>
</Com>
登入後複製
作用域插槽

普通的插槽,是無法獲取其他作用域下的資料的,即父元件模板中的表示式只能存取父元件的作用域;子元件模板中的表示式只能存取子元件的作用域
但在某些情況下,插槽中的內容想要同時使用父元件和子元件內的資料,可以通過像元件傳遞資料props那樣,讓子元件在渲染時將一部分資料提供給插槽,這樣在元件外部(父元件)中就可以使用子元件中的資料了-通過slot的方式

子元件傳入插槽的 props 作為了 v-slot 指令的值,可以在插槽內的表示式中存取,其中name是Vue特意保留的attribute,不會作為props進行傳遞

  • 資料傳遞
//子元件
<template> 
    <slot :shopInfo="shopInfo" :userInfo="userInfo"></slot> 
</template>
登入後複製
  • 資料接收
    • 預設插槽接收
    //父元件 - 使用方
    <MyCom v-slot="{shopInfo,userInfo}">
      {{ shopInfo }} {{ userInfo }}
    </MyCom>
    登入後複製
    • 具名插槽接收
    <MyCom>
      <template v-slot:header="shopInfo">
        {{ shopInfo }}
      </template>
    
      <template #default="introduction">
        {{ introduction }}
      </template>
    
      <template #footer="userInfo">
        {{ userInfo }}
      </template>
    </MyCom>
    登入後複製
  • 使用slot-scope時,用最後一個slot-scope替換模板中的slot
<cpm>
    <!-- 不顯示 -->
    <div>555</div>
    <!-- 不顯示 -->
    <div slot-scope="scope">
        <div>{{scope.name}}</div>
    </div>
    <!-- 顯示 -->
    <div slot-scope="scope">
        <div>{{scope}}</div>
        <div>{{scope.name}}</div>
        <div>{{scope.age}}</div>
    </div>
</cpm>
登入後複製
  • 使用作用域插槽時,可以實現既可以複用子元件slot,又可以使得slot的內容不一致,它允許使用者傳遞一個模板而不是已經渲染好的元素給插槽,所謂作用域是指模板雖然在父級作用域中渲染的,但是卻可以拿到子元件的資料
  • 常規的v-bind需要攜帶引數key值進行傳遞,例如v-bind:info = '123';但是有時候會省略這個key值,直接進行傳遞資料,如v-bind = 'item',這種用法會將整個物件的所有屬性都繫結到當前元素上,適用於需要繫結的屬性過多的場景
// data: {
//     shapes: [
//         { name: 'Square', sides: 4 },
//         { name: 'Hexagon', sides: 6 },
//         { name: 'Triangle', sides: 3 }
//     ],
//     colors: [
//         { name: 'Yellow', hex: '#F4D03F', },
//         { name: 'Green', hex: '#229954' },
//         { name: 'Purple', hex: '#9B59B6' }
//     ]
// }
<my-list title="Shapes" :items="shapes">
    <template scope="shape">
        <div>{{ shape.name }} <small>({{ shape.sides }} sides)</small></div>
    </template>
</my-list>
<my-list title="Colors" :items="colors">
    <template scope="color">
        <div>
            <div class="swatch" :style="{ background: color.hex }"></div>
            {{ color.name }}
        </div>
    </template>
</my-list>
<div id="my-list">
    <div class="title">{{ title }}</div>
    <div class="list">
        <div v-for="scope in items">
            <slot v-bind="scope"></slot>
        </div>
    </div>
</div>


Vue.component('my-list', {
    template: '#my-list',
    props: [ 'title', 'items' ]
});
登入後複製
遞迴元件

遞迴元件就是指元件在模板中呼叫自己,由於是元件自身呼叫,就不能像常規元件定義一樣,可以省略元件的name設定,元件的遞迴需要依賴於自身的name設定(name還用於遍歷元件的name選項來查詢元件的範例);

  • 滿足條件
    • 需要給元件設定一個name屬性
    • 需要有一個明確的結束條件
<template>
    <div>
        <my-component :count="count + 1" v-if="count
        <= 5"></my-component>
    </div>
</template>
<script>
export default {
    name:'my-component',
    props: {
        count: {
            type: Number,
            default: 1
        }
    }
}
</script>
登入後複製
動態元件

有時候我們需要根據一些條件,動態的切換/選擇某個元件,在函數式元件中,沒有上下文的概念,常用於程式化的在多個元件中選擇一個,可以間接的解決動態切換元件的需求,缺點是基於js物件進行開發,不方便開發;
Vue官方提供了一個內建元件<component>和is的屬性,用來解決上述的問題

<component :is="component"></component>
//component 就是js import進的元件範例,其值可以是標籤名、元件名、直接繫結一個物件等
登入後複製
  • 為了使得元件具有快取作用,可以使用的內建元件,這樣只要不離開當前頁面,切換到其他元件後deforeDestory不會執行,因此元件具有了快取功能

拓展

components的第二種寫法

常規的元件components是直接通過參照定義好的元件進行展示的,也可以直接在當前元件內定義,然後通過設定components進行渲染

<div id="app">
    <cpn v-show="isShow"></cpn>
</div>
<template id="com">
    <div>
        <h2>Lbxin</h2>
        <p>class - 11</p>
    </div>
</template>
<script>
var app = new Vue({
    el: '#app',
    data: {
        isShow: true
    },
    components: {
        cpn: {
            template: '#com',
            data() {
                isShow: false
            }
        }
    }
})
</script>
登入後複製

Web Component <slot> 簡介

HTML的slot元素,是Web Components技術套件的一部分,是Web元件內的一個預留位置,該預留位置可以在後期使用自己的標示語言進行填充,這樣可以建立單獨的DOM樹,並將其與其他的元件組合在一起 -- MDN

常見的填充Web元件的shadow DOM的模板有template和slot

  • 模板 - Templates

    • 需要在網頁上重複的使用相同的標記結構時,為了避免CV的操作可以通過模板的方式進行實現
    • 需要注意的是模板 - Template 和其內部的內容是不會在DOM中呈現的,可以通過js進行存取並新增到DOM中,從而在介面上進行展示
    <template id="my-paragraph">
      <p>My paragraph</p>
    </template>
    登入後複製
    let template = document.getElementById('my-paragraph');
    let templateContent = template.content;
    document.body.appendChild(templateContent);
    登入後複製
    • 可以配合Web Component一起使用,實現純js自定義的元件
    customElements.define('my-paragraph',
      class extends HTMLElement {
        constructor() {
          super();
          let template = document.getElementById('my-paragraph');
          let templateContent = template.content;
    
          const shadowRoot = this.attachShadow({mode: 'open'})
            .appendChild(templateContent.cloneNode(true));
      }
    })
    
    // 自定義標籤使用
    <my-paragraph></my-paragraph>
    登入後複製
    • 後續的樣式邏輯也需要加在template中,方便通過後續的相關邏輯(如template.content獲取到然後打入到指定的容器中)
  • Web Component簡介

    • Web Component的一個很重要的屬性就是封裝 - 可以將標記結構、樣式和行為影藏起來,並於介面上的其他程式碼隔離開來,保證程式碼的獨立性

    • Web Component標準非常重要的一個特性是,使得開發者可以將HTML頁面的功能封住成custom elements(自定義標籤)

    • customElements 介面用來實現一個物件,允許開發者註冊一個custom elements的資訊,返回已註冊的自定義標籤的資訊;

    • customElements.define方法用來註冊一個custom element,接收三個引數

      • 引數一:表明建立元素的名稱,其註冊的名稱不能簡單的單詞,需要由短劃線進行拼接

      • 引數二:用於定義元素行為的類

      • 引數三:一個包含extends屬性設定的設定物件,可選,指定了所建立的自定義元素是繼承於哪個內建的元素,可以繼承任何內建的元素;

        customElements.define(
            'word-count', 
            WordCount, 
            { extends: 'p' }
        );
        登入後複製

        可以使用ES2015的類實現

        class WordCount extends HTMLParagraphElement {
          constructor() {
            // 必須首先呼叫 super 方法
            super();
            // 元素的功能程式碼寫在這裡
            ...
          }
        }
        登入後複製
    • 自定義標籤的型別

      • 型別一:Autonomous custom elements 是獨立的元素,它不繼承其他內建的 HTML 元素,可以直接通過標籤的方式進行HTML使用<popup-info>,也可以通過js的方式進行使用document.createElement("popup-info")
      • 型別二:Customized built-in elements 繼承自基本的 HTML 元素。在建立時,你必須指定所需擴充套件的元素,使用時,需要先寫出基本的元素標籤,並通過 is屬性指定 custom element 的名稱;<p is="word-count">document.createElement("p", { is: "word-count" })

      參考文獻 - MDN

  • shadow DOM簡介

    • 圖解Shandow DOM

    3.png

    • Shadow host:一個常規 DOM 節點,Shadow DOM 會被附加到這個節點上。

    • Shadow tree:Shadow DOM 內部的 DOM 樹。

    • Shadow boundary:Shadow DOM 結束的地方,也是常規 DOM 開始的地方。

    • Shadow root: Shadow tree 的根節點。

    shadow DOM主要是將一個隱藏的、獨立的DOM樹附加到常規的DOM樹上,是以shadow DOM節點為起始根節點,在這個根節點的下方,可以是任意的元素,和普通的DOM元素一致

如常見的video標籤,其內部的一些控制器和按鈕等都是通過Shandow DOM進行維護的,開發者可以通過這個API進行自己獨立的邏輯控制

  • 基本用法

    • Element.attachShadow()方法可以將一個shadow DOM新增到任何一個元素上,接收一個設定物件引數,該物件有一個mode的屬性,值可以是open - 可以通過外部js獲取 Shadow DOM和closed - 外部不可以通過js進行獲取 Shadow DOM
    let shadow1 = elementRef.attachShadow({mode: 'open'});
    let shadow2 = elementRef.attachShadow({mode: 'closed'});
    let myShadowDom = shadow1.shadowRoot; // 具體內容
    let myShadowDom = shadow2.shadowRoot; //null
    登入後複製
    • 當需要將一個shadow DOM新增到自定義的標籤上時,可以在自定義的建構函式中新增如下邏輯;
    let shadow = this.attachShadow({mode: 'open'});
    // 將一個shadow DOM新增到一個元素上之後就可以使用DOM API進行操作存取了
    登入後複製

(學習視訊分享:、)

以上就是深入理解Vue中的插槽、內容分發、具名插槽的詳細內容,更多請關注TW511.COM其它相關文章!