欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue中slot的使用詳解

 更新時間:2022年05月23日 12:00:15   作者:perryhuan9  
這篇文章主要介紹了Vue中slot的使用詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

在Vue中,我們使用組件來組織頁面和組織代碼,類似于搭積木,每一個組件都是一個積木,使用一些相同或者不同組件就能搭建出我們想要的頁面。

slot(插槽)是組件功能的重要組成部分,插槽必須用于組件才有意義。

它為組件提供了對外的接口,允許從組件外部傳遞內(nèi)容,并將這部分內(nèi)容放置到指定的位置。

使用 slot

當(dāng)一個組件可能被使用至少兩次并且兩次使用內(nèi)容(這里指組件視圖的組成)不同時,插槽才有存在的必要。注意: 本文的代碼都是基于Vue3編寫。

基礎(chǔ)用法

Link.vue

<template>
? <a :href="href" rel="external nofollow"  class="link">
? ? <!-- 留個插槽,外界傳入內(nèi)容放置在這里 -->
? ? <slot></slot>
? </a>
</template>
<script>
export default {
? props: {
? ? href: {
? ? ? required: true,
? ? ? type: String,
? ? },
? },
};

</script>
<style lang="less" scoped>
.link {
? display: inline-block;
? line-height: 1;
? white-space: nowrap;
? cursor: pointer;
? background: #fff;
? border: 1px solid #dcdfe6;
? color: #606266;
? -webkit-appearance: none;
? text-align: center;
? box-sizing: border-box;
? outline: none;
? margin: 0;
? transition: 0.1s;
? font-weight: 500;
? padding: 12px 20px;
? font-size: 14px;
? border-radius: 4px;
}
</style>

App.vue

<template>
? <div class="app">
? ? <Link  rel="external nofollow" > 百度</Link>
? ? <br />
? ? <Link  rel="external nofollow"  style="margin-top: 10px">
? ? ? <!-- 這里允許放置任意的內(nèi)容,包括字符串和標(biāo)簽 -->
? ? ? <span>Icon</span>谷歌</Link
? ? >
? </div>
</template>
<script>
import Link from "./Link.vue";

export default {
? components: {
? ? Link,
? },
};
</script>

視覺效果:

image.png

以上實現(xiàn)了兩個組件Link.vue和App.vue,Link.vue是一個鏈接組件,在組件內(nèi)部已經(jīng)定義好了樣式,然后鏈接的內(nèi)容交由外界使用時填充。

在App.vue組件內(nèi)則使用了Link.vue組件兩次,并且兩次傳入的內(nèi)容不同。

具名插槽

上面的Link.vue只要求填充一份內(nèi)容,那么當(dāng)我們需要在組件的好幾個位置都填充不同的內(nèi)容應(yīng)該怎么辦?這時候可以使用具名插槽,就是給組件的每個填充區(qū)域都取個名字,這樣在使用的時候就可以往對應(yīng)名字的那個區(qū)域填充內(nèi)容。

Page.vue

<template>
  <div class="page">
    <header class="page-header">
      <slot name="header"></slot>
    </header>
    <div class="page-center">
      <aside class="page-aside">
        <slot name="aside"></slot>
      </aside>
      <div class="page-content">
        <slot name="content"></slot>
      </div>
    </div>
    <footer class="page-footer">
      <slot name="footer"></slot>
    </footer>
  </div>
</template>


<script>
export default {
? setup() {
? ? return {};
? },
};
</script>
<style lang="less">
body {
? margin: 0;
}
.page {
? border: 1px solid #333;
? width: 100vw;
? height: 100vh;
? display: flex;
? flex-direction: column;
? &-header {
? ? height: 50px;
? ? border-bottom: 1px solid #333333;
? }
? &-center {
? ? flex: 1;
? ? display: flex;
? }
? &-aside {
? ? width: 150px;
? ? border-right: 1px solid #333333;
? }
? &-content {
? ? flex: 1;
? }

? &-footer {
? ? border-top: 1px solid #333;
? ? height: 30px;
? }
}
</style>

App.vue 

<template>
? <Page style="width: 500px; height: 300px; margin: 30px 30px">
? ? <template v-slot:header>這是標(biāo)題</template>
? ? <template v-slot:aside>這是側(cè)邊欄</template>
? ? <template v-slot:content>這是內(nèi)容區(qū)域</template>
? ? <template v-slot:footer>這是頁腳</template>
? </Page>

? <Page style="width: 500px; height: 300px; margin: 30px 30px">
? ? <template v-slot:header>
? ? ? <h2>走過路過</h2>
? ? </template>
? ? <template v-slot:aside>
? ? ? <ul>
? ? ? ? <li>東臨碣石</li>
? ? ? ? <li>以觀滄海</li>
? ? ? </ul>
? ? </template>
? ? <template v-slot:content>這是內(nèi)容區(qū)域</template>
? ? <template v-slot:footer>這是頁腳</template>
? </Page>
</template>
<script>
import Page from "./Page.vue";

export default {
? components: {
? ? Page,
? },
};
</script>

效果圖:

image.png

作用域插槽

為啥叫作用域插槽?首先要搞清楚作用域這個概念。在JS中,作用域表示的是當(dāng)前的執(zhí)行上下文,只有在當(dāng)前作用域中變量才可以被使用。作用域有層次之分,分為父作用域和子作用域,子作用域可以訪問父作用域中的變量,這一層層的往上則形成了作用域鏈。JS中只有全局作用域和函數(shù)作用域,ES6新增了塊級作用域。關(guān)于作用域,這里不再贅言,有需要的同學(xué)可以去MDN作用域查看。

Vue本質(zhì)上還是js,模板最終會被編譯成render函數(shù),每個組件都有一個render函數(shù)。下面先看個例子:

Count.vue

<template>
? <div>
? ? <p>當(dāng)前數(shù)字:{{ count }}</p>
? ? <button @click="onAdd">+</button>
? ? <button @click="onMinus">-</button>
? ? <slot></slot>
? </div>
</template>
<script>
export default {
? data() {
? ? return {
? ? ? count: 0,
? ? };
? },
? methods: {
? ? onAdd() {
? ? ? this.count++;
? ? },
? ? onMinus() {
? ? ? this.count--;
? ? },
? },
};
</script>

App.vue

<template>
? <div>
? ? <Count style="border: 1px solid red">
? ? ? <p>這就是填充Count組件的插槽</p>
? ? ? <p>appCount:{{ appCount }}</p>
? ? ? <p>Count組件中的count變量:{{ count }}</p>
? ? </Count>
? ? <br />
? ? <button @click="onClick">app add</button>
? </div>
</template>
<script>
import Count from "./Count.vue";

export default {
? components: {
? ? Count,
? },
? data() {
? ? return {
? ? ? appCount: 0,
? ? };
? },
? methods: {
? ? onClick() {
? ? ? this.appCount++;
? ? },
? },
};
</script>

效果圖:

image.png

從上面的效果圖中可以看到,在App.vue組件中使用Count.vue組件時,在Count.vue組件的插槽中,能夠訪問appCount變量,但是不能訪問Count.vue組件的Count變量,這是為什么呢?理論上,插槽傳入的內(nèi)容最終會插入到Count.vue組件中,那么也應(yīng)該可以訪問Count.vue組件的變量才對?。?/p>

父級模板里的所有內(nèi)容都是在父級作用域中編譯的;子模板里的所有內(nèi)容都是在子作用域中編譯的。

上面的一段引用摘自Vue文檔,這段文字表明了,在App.vue中的一切,包括Count.vue組件的插槽內(nèi)容都是在App.vue組件下編譯的,也就是Count.vue組件的插槽模板可以訪問App.vue組件的所有變量,但不能訪問Count.vue的任意變量。如果我一定要在插槽中訪問Count.vue的count變量呢?這個時候作用域插槽就派上用場了。

作用域插槽允許在組件中對插槽所在的上下文暴露某一些變量,改寫以上的Count.vue組件,

Count.vue

<template>
  <div>
    <p>當(dāng)前數(shù)字:{{ count }}</p>
    <button @click="onAdd">+</button>
    <button @click="onMinus">-</button>
    <!-- 把count變量暴露到插槽作用域 -->
    <slot :count="count"></slot>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    onAdd() {
      this.count++;
    },
    onMinus() {
      this.count--;
    },
  },
};
</script>

App.vue

<template>
  <div>
    <Count style="border: 1px solid red">
     <!--Count組件插槽暴露的所有變量都放在 slotProps對象中 -->
      <template v-slot="slotProps">
        <p>這就是填充Count組件的插槽</p>
        <p>appCount:{{ appCount }}</p>
        <p>Count組件中的count變量:{{ slotProps.count }}</p>
      </template>
    </Count>
    <br />
    <button @click="onClick">app add</button>
  </div>
</template>

<script>
import Count from "./Count.vue";

export default {
  components: {
    Count,
  },
  data() {
    return {
      appCount: 0,
    };
  },
  methods: {
    onClick() {
      this.appCount++;
    },
  },
};
</script>

這就是作用域插槽,本質(zhì)上了是允許在父組件作用域訪問到子組件作用域,它為插槽模板區(qū)域提供了一個數(shù)據(jù)來源于子組件的上下文。

作用域插槽的用處還是挺廣的,總的來說當(dāng)你需要它時自然會用到它,如果想提前學(xué)習(xí),可以看一下elementUI的table組件。

slot 實現(xiàn)

上面就插槽的使用說了一大堆,關(guān)于插槽的實現(xiàn)還是沒有涉及,下文講解在Vue中插槽是如何實現(xiàn)的?

首先,我們都知道,無論是使用jsx還是模板,最終都會編譯成render函數(shù),并且render函數(shù)在執(zhí)行之后會輸出 Virtual Dom ,下面先看一個組件在編譯完成之后是什么樣子?

Comp.vue

<template>
  <div>
   	<p>count: {{count}}</p>
    <button @click="onClick">
      ADD
    </button>
    <slot :count="count"></slot>
  </div>
</template>
<script>
 import {defineComponent, ref} from 'vue'

 export default defineComponent((props) => {
   const count = ref(0);
   const onClick = () => {
     count.value++
   }
   return {
     count,
     onClick
   }
 }) 
</script>

App.vue

<template>
  <div>
   <Comp>
   	<template v-slot="slotProps">
      <p>
         {{magRef}}: {{slotProps.count}}
      </p>
    </template> 
   </Comp>
  </div>
</template>

<script>
   import {defineComponent, ref} from 'vue'
  import Comp from './Comp.vue'

  
 export default defineComponent({
   components: {Comp},
   setup(props) {
     const magRef = ref('當(dāng)前的數(shù)字是')
     return {
       magRef
     }
   }
 }) 
</script>

Comp.vue編譯之后:

/* Analyzed bindings: {} */
import {
  defineComponent,
  ref
} from 'vue'

const __sfc__ = defineComponent((props) => {
  const count = ref(0);
  const onClick = () => {
    count.value++
  }
  return {
    count,
    onClick
  }
})

import {
  toDisplayString as _toDisplayString,
  createElementVNode as _createElementVNode,
  renderSlot as _renderSlot,
  openBlock as _openBlock,
  createElementBlock as _createElementBlock
} from "vue"

function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("p", null, "count: " + _toDisplayString(_ctx.count), 1 /* TEXT */ ),
    _createElementVNode("button", {
      onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.onClick && _ctx.onClick(...args)))
    }, " ADD "),
    _renderSlot(_ctx.$slots, "default", {
      count: _ctx.count
    })
  ]))
}
__sfc__.render = render
__sfc__.__file = "Comp.vue"
export default __sfc__

App.vue編譯之后:

/* Analyzed bindings: {} */
import {
  defineComponent,
  ref
} from 'vue'
import Comp from './Comp.vue'


const __sfc__ = defineComponent({
  components: {
    Comp
  },
  setup(props) {
    const magRef = ref('當(dāng)前的數(shù)字是')
    return {
      magRef
    }
  }
})

import {
  toDisplayString as _toDisplayString,
  createElementVNode as _createElementVNode,
  resolveComponent as _resolveComponent,
  withCtx as _withCtx,
  createVNode as _createVNode,
  openBlock as _openBlock,
  createElementBlock as _createElementBlock
} from "vue"

function render(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_Comp = _resolveComponent("Comp")

  return (_openBlock(), _createElementBlock("div", null, [
    _createVNode(_component_Comp, null, {
      default: _withCtx((slotProps) => [
        _createElementVNode("p", null, _toDisplayString(_ctx.magRef) + ": " + _toDisplayString(slotProps.count), 1 /* TEXT */ )
      ]),
      _: 1 /* STABLE */
    })
  ]))
}
__sfc__.render = render
__sfc__.__file = "App.vue"
export default __sfc__

這里給大家推薦一個尤雨溪搞的測試網(wǎng)站Vue SFC Playground 可以直接看到組件編譯之后的js代碼。

image.png

這個編譯是在加載.vue文件的時候就執(zhí)行了,runtime階段是不存在模板字符串了(使用UMD的時候會存在),在瀏覽器中執(zhí)行的都是編譯之后的js。下面具體分析一下以上Comp.vue和App.vue編譯之后的js代碼。

首先在Comp.vue中,<slot :count="count"></slot>會被編譯成_renderSlot(_ctx.$slots, "default", {count: _ctx.count}),下面看看_renderSlot中干了什么?

export type Slot = (...args: any[]) => VNode[]

export type InternalSlots = {
  [name: string]: Slot | undefined
}
export function renderSlot(
  slots: Slots,
  name: string,
  props: Data = {},
  // this is not a user-facing function, so the fallback is always generated by
  // the compiler and guaranteed to be a function returning an array
  fallback?: () => VNodeArrayChildren,
  noSlotted?: boolean
): VNode {
  let slot = slots[name]
 
  openBlock()
  const validSlotContent = slot && ensureValidVNode(slot(props))
  const rendered = createBlock(
    Fragment,
    { key: props.key || `_${name}` },
    validSlotContent || (fallback ? fallback() : []),
    validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE
      ? PatchFlags.STABLE_FRAGMENT
      : PatchFlags.BAIL
  )
  return rendered
}

_renderSlot(_ctx.$slots, "default", {count: _ctx.count})這一句顯然是執(zhí)行_ctx.$slots.default({count: _ctx.count}),這說明在父組件中,每個插槽模板最終會被編譯成一個函數(shù),并且這個函數(shù)會被傳遞到子組件,在子組件里面會以props(這里是{count: _ctx.count})作為參數(shù)執(zhí)行插槽函數(shù),最終_ctx.$slots.default({count: _ctx.count})會返回virtual dom對象。

下面再看一下App.vue組件:

<Comp>
    <template v-slot="slotProps">
        <p>
            {{magRef}}: {{slotProps.count}}
        </p>
    </template> 
</Comp>

被編譯成了:

_createVNode(_component_Comp, null, {
    default: _withCtx((slotProps) => [
    _createElementVNode("p", null, _toDisplayString(_ctx.magRef) + ": " + _toDisplayString(slotProps.count), 1 /* TEXT */ )
    ]),
    _: 1 /* STABLE */
})

請忽略_withCtx,顯然模板會編譯成一個函數(shù),并傳遞到子組件,進而在子組件中構(gòu)建出完整的virtual dom, 上面中_ctx是當(dāng)前組件的上下文,slotProps則是作用域插槽暴露的參數(shù)。

由此可以做一個總結(jié),vue slot的實現(xiàn)原理:

  • 所有的模板會被編譯成創(chuàng)建vnode的函數(shù)。
  • 父組件中傳遞給子組件的插槽(每個插槽都是一個函數(shù),即名字不同的插槽為不同的函數(shù))內(nèi)容模板也會被編譯成函數(shù)并且傳遞給子組件,模板中如果使用了父組件的變量,那么會通過閉包的形式在插槽函數(shù)中被使用。
  • 子組件在接收到父組件傳遞的插槽內(nèi)容函數(shù),會以在slot暴露的變量(只有作用域插槽有這些變量)為參數(shù)執(zhí)行這個函數(shù),返回vnode,這個vnode會作為子組件vnode的一部分。

總結(jié)

本文從使用和實現(xiàn)兩個方面講解了vue slot,有一定的深度,但忽略了一些使用和實現(xiàn)上的細節(jié),有不足之處還請指出且諒解。 

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue-cli3簡單使用(圖文步驟)

    Vue-cli3簡單使用(圖文步驟)

    這篇文章主要介紹了Vue-cli3簡單使用(圖文步驟),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-04-04
  • vue2從數(shù)據(jù)變化到視圖變化發(fā)布訂閱模式詳解

    vue2從數(shù)據(jù)變化到視圖變化發(fā)布訂閱模式詳解

    這篇文章主要為大家介紹了vue2從數(shù)據(jù)變化到視圖變化發(fā)布訂閱模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • vue實現(xiàn)高德地圖添加多個點標(biāo)記

    vue實現(xiàn)高德地圖添加多個點標(biāo)記

    地圖多點標(biāo)注其實是個非常簡單的問題,這篇文章主要給大家介紹了關(guān)于vue實現(xiàn)高德地圖添加多個點標(biāo)記的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-06-06
  • vue中正確使用jsx語法的姿勢分享

    vue中正確使用jsx語法的姿勢分享

    這篇文章主要給大家介紹了關(guān)于vue中正確使用jsx的相關(guān)資料,JSX就是Javascript和XML結(jié)合的一種格式,React發(fā)明了JSX,利用HTML語法來創(chuàng)建虛擬DOM,當(dāng)遇到<,JSX就當(dāng)HTML解析,遇到{就當(dāng)JavaScript解析,需要的朋友可以參考下
    2021-07-07
  • Vue2遞歸組件實現(xiàn)樹形菜單

    Vue2遞歸組件實現(xiàn)樹形菜單

    這篇文章主要為大家詳細介紹了Vue2遞歸組件實現(xiàn)樹形菜單的相關(guān)代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Vue3 響應(yīng)式 API 及 reactive 和 ref 的用法示例詳解

    Vue3 響應(yīng)式 API 及 reactive 和 ref&

    響應(yīng)式是一種允許以聲明式的方式去適應(yīng)變化的編程范例,這篇文章主要介紹了關(guān)于Vue3響應(yīng)式API及reactive和ref的用法,需要的朋友可以參考下
    2023-06-06
  • Element el-upload上傳組件使用詳解

    Element el-upload上傳組件使用詳解

    本文主要介紹了Element el-upload上傳組件使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • vue中如何去掉空格的方法實現(xiàn)

    vue中如何去掉空格的方法實現(xiàn)

    這篇文章主要介紹了vue中如何去掉空格的方法實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • vue基于el-table拓展之table-plus組件

    vue基于el-table拓展之table-plus組件

    本文主要介紹了vue基于el-table拓展之table-plus組件,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Vue2與Vue3兄弟組件通訊bus的區(qū)別及用法

    Vue2與Vue3兄弟組件通訊bus的區(qū)別及用法

    這篇文章主要介紹了Vue2與Vue3兄弟組件通訊bus的區(qū)別及用法,有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家多多進步早日升職加薪
    2021-09-09

最新評論