溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Vue3復(fù)用組件怎么使用

發(fā)布時(shí)間:2023-04-17 15:07:00 來源:億速云 閱讀:132 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“Vue3復(fù)用組件怎么使用”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Vue3復(fù)用組件怎么使用”吧!

    前言

    無論是 vue 還是 react,當(dāng)遇到多處重復(fù)代碼的時(shí)候,我們都會(huì)想著如何復(fù)用這些代碼,而不是一個(gè)文件里充斥著一堆冗余代碼。

    實(shí)際上,vue 和 react 都可以通過抽組件的方式來達(dá)到復(fù)用,但如果遇到一些很小的代碼片段,你又不想抽到另外一個(gè)文件的情況下,相比而言,react 可以在相同文件里面聲明對(duì)應(yīng)的小組件,或者通過 render function 來實(shí)現(xiàn),如:

    const Demo: FC<{ msg: string}> = ({ msg }) => {
      return <div>demo msg is { msg } </div>
    }
    const App: FC = () => {
      return (
      <>
        <Demo msg="msg1"/>
        <Demo msg="msg2"/>
      </>
      )
    }
    /** render function的形式 */
    const App: FC = () => {
      const renderDemo = (msg : string) => {
        return <div>demo msg is { msg } </div>
      }
      return (
      <>
        {renderDemo('msg1')}
        {renderDemo('msg2')}
      </>
      )
    }

    但對(duì)于 .vue 模板 來說,沒法像 react 一樣在單個(gè)文件里面聲明其他組件,如果你要復(fù)用代碼,那就只能通過抽離組件的方式。

    可是啊可是??!就如上面 Demo 組件,就零零散散兩三行代碼,抽成一個(gè)組件你又覺得沒太必要,那唯一的解決方案就是 CV 大法?(當(dāng)然,如果是諸如 list 之類的,可以用 v-for 代碼,但這里介紹的不是這種場景)

    我知道你很急,但你先別急。如果我們可以通過在組件范圍內(nèi)將要復(fù)用的模板圈起來,跟 vue 說,喂,這代碼是我圈起來的,因?yàn)槲矣泻脦滋幰玫剑m然目前你看起來好像不支持這功能,不過,沒事,你實(shí)現(xiàn)不了的,我來實(shí)現(xiàn)

    那大致實(shí)現(xiàn)方案就是這樣子啦:

    <template>
      <DefineFoo v-slot="{ msg }">
        <div>Foo: {{ msg }}</div>
      </DefineFoo>
      ...
      <ReuseFoo msg="msg1" />
      <div>xxx</div>
      <ReuseFoo msg="msg2" />
      <div>yyy</div>
      <ReuseFoo msg="msg3" />
    </template>

    Vue3復(fù)用組件怎么使用

    可是,這種方案究竟如何實(shí)現(xiàn)呢?畢竟牛都已經(jīng)吹上天了,實(shí)現(xiàn)不了也要硬著頭皮折騰。好了,不賣關(guān)子,antfu 大佬實(shí)際上已經(jīng)實(shí)現(xiàn)了,叫做createReusableTemplate,且放到 VueUse 里面了,具體可以點(diǎn)擊文檔看看。

    用法

    通過 createReusableTemplate 拿到定義模板和復(fù)用模板的組件

    <script setup>
    import { createReusableTemplate } from '@vueuse/core'
    const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
    </script>

    然后在你要復(fù)用代碼的地方,將其用DefineTemplate包起來,之后就可以通過ReuseTemplate在單文件 template 的任意地方使用了:

    <template>
      <DefineTemplate>
        <!-- 這里定義你要復(fù)用的代碼 -->
      </DefineTemplate>
        <!-- 在你要復(fù)用的地方使用ReuseTemplate, -->
        <ReuseTemplate />
        ...
        <ReuseTemplate />
    </template>

    ?? DefineTemplate 務(wù)必在 ReuseTemplate 之前使用

    我們看到,createReusableTemplate 返回了一個(gè) Tuple,即 define 和 reuse 的組件對(duì),然后,通過上面的例子就可以在單文件里面復(fù)用多處代碼了。

    還有,實(shí)際上還可以通過對(duì)象解構(gòu)的方式返回一個(gè) define 和 reuse(很神奇吧,這篇文章就不展開了,有興趣下次再分享下),用法同上,例子如下

    <script setup lang="ts">
    const [DefineFoo, ReuseFoo] = createReusableTemplate<{ msg: string }>()
    const TemplateBar = createReusableTemplate<{ msg: string }>()
    const [DefineBiz, ReuseBiz] = createReusableTemplate<{ msg: string }>()
    </script>
    <template>
      <DefineFoo v-slot="{ msg }">
        <div>Foo: {{ msg }}</div>
      </DefineFoo>
      <ReuseFoo msg="world" />
      <!-- 看這里 -->
      <TemplateBar.define v-slot="{ msg }">
        <div>Bar: {{ msg }}</div>
      </TemplateBar.define>
      <TemplateBar.reuse msg="world" />
      <!-- Slots -->
      <DefineBiz v-slot="{ msg, $slots }">
        <div>Biz: {{ msg }}</div>
        <component :is="$slots.default" />
      </DefineBiz>
      <ReuseBiz msg="reuse 1">
        <div>This is a slot from Reuse</div>
      </ReuseBiz>
      <ReuseBiz msg="reuse 2">
        <div>This is another one</div>
      </ReuseBiz>
    </template>

    Vue3復(fù)用組件怎么使用

    真是神奇,那咋實(shí)現(xiàn)呢

    上面我們介紹了用法,相信應(yīng)該沒人看不懂,上手成本確實(shí)為 0,那接下來我們一起看看是如何實(shí)現(xiàn)的。

    我們知道,Vue3 定義組件除了通過 script setup 的方式之外, 還可以通過defineComponent的方式

    const Demo = defineComponent({
      props: {
        ...,
      },
      setup(props, { attrs, slots }) {
        return () => {
          ...
        }
      }
    })

    然后我們觀察下是如何定義模板的

    <DefineFoo v-slot="{ msg }">
        <div>Foo: {{ msg }}</div>
    </DefineFoo>

    好像似曾相識(shí)?v-slot?,誒,臥槽,這不是插槽嗎!還有,模板代碼看起來就是放在默認(rèn)插槽的。

    好,我們接下來看下如何實(shí)現(xiàn) Define 的功能。

    實(shí)現(xiàn) Define

    我們剛才說,模板是定義在默認(rèn)插槽里面,那我們可以定義個(gè)局部變量 render,之后當(dāng)在 template 里面使用 Define 時(shí)會(huì)進(jìn)入 setup,這時(shí)候拿到 slot.default,放在 render 上不就好?,代碼如下

    let render: Slot | undefined
    const Define = defineComponent({
      setup(props, { slots, }) {
        return () => {
          /** 這里拿到默認(rèn)插槽的渲染函數(shù) */
          render = slots.default
        }
      }
    })

    對(duì)的,就是這么簡單,對(duì)于 Define 來說,核心代碼就是這兩三行而已

    實(shí)現(xiàn) Reuse

    上面拿到 render function 了,那我們?cè)谑褂?Reuse 的地方,將所得到的 v-slot、attrs 等傳給 render 不就好?

    同樣,當(dāng)我們?cè)?template 使用 Reuse 的時(shí)候,就會(huì)進(jìn)入 setup,然后將得到的參數(shù)都傳給 render,并 return render 的結(jié)果即可

      const reuse = defineComponent({
        setup(_, { attrs, slots }) {
          return () => {
            /**
             * 沒有render,有兩種可能
             * 1. 你沒Define
             * 2. Define寫在Reuse后面
             **/
            if (!render && process.env.NODE_ENV !== 'production')
              throw new Error(`[vue-reuse-template] Failed to find the definition of template${name ? ` "${name}"` : ''}`)
            return render?.({ ...attrs, $slots: slots })
          }
        },
      })

    上面的 attrs 也就是你在 Reuse 上傳的 prop 了

    <ReuseFoo msg="msg1" />

    而為啥還要傳個(gè)$slots?

    上面實(shí)際上有個(gè)例子,模板里面還可以通過動(dòng)態(tài)組件<component :is="$slots.default" />的方式來拿到插槽的真正內(nèi)容

    <DefineBiz v-slot="{ msg, $slots }">
        <div>Biz: {{ msg }}</div>
        <component :is="$slots.default" />
    </DefineBiz>
    <ReuseBiz msg="reuse 1">
        <div>This is a slot from Reuse</div>
      </ReuseBiz>
    <ReuseBiz msg="reuse 2">
      <div>This is another one</div>
    </ReuseBiz>

    Vue3復(fù)用組件怎么使用

    當(dāng)然,不只默認(rèn)插槽啦,其他具名插槽都可以

    <DefineBiz v-slot="{ msg, $slots }">
        <component :is="$slots.header" />
        <div>Biz: {{ msg }}</div>
        <component :is="$slots.default" />
      </DefineBiz>
      <ReuseBiz msg="reuse 1">
        <template #header>
          <div>我是 reuse1的header</div>
        </template>
        <div>This is a slot from Reuse</div>
      </ReuseBiz>
      <ReuseBiz msg="reuse 2">
        <template #header>
          <div>我是 reuse1的header</div>
        </template>
        <div>This is another one</div>
      </ReuseBiz>

    Vue3復(fù)用組件怎么使用

    具體怎么玩出花,你來定~

    類型支持,提升開發(fā)體驗(yàn)

    我們定義了模板,但模板接收什么參數(shù),傳入什么參數(shù),你得告訴我對(duì)不對(duì),如果沒有類型的提示,那么開發(fā)體驗(yàn)會(huì)極其糟糕,不過,不用擔(dān)心,大佬這些都考慮好了。

    createReusableTemplate 支持泛型參數(shù),也就是說你要復(fù)用的模板需要什么參數(shù),只需要通過傳入對(duì)應(yīng)類型即可,比如你有個(gè) msg,是 string 類型,那么用法如下

    const [DefineFoo, ReuseFoo] = createReusableTemplate<{ msg: string }>()

    然后你就會(huì)發(fā)現(xiàn),DefineFoo, ReuseFoo 都會(huì)對(duì)應(yīng)的類型提示了

    添加類型支持

    我們上面說是用 defineComponent 得到 Define 和 Reuse,而 defineComponent 返回的類型就是 DefineComponent 呀

    type DefineComponent<PropsOrPropOptions = {}, RawBindings = {}, ...>

    假設(shè)模板參數(shù)類型為 Bindings 的話,那么對(duì)于 Reuse 來說,其既支持傳參,也支持添加插槽內(nèi)容,所以類型如下

    type ReuseTemplateComponent<
      Bindings extends object,
      Slots extends Record<string, Slot | undefined>,
      /** Bindings使之有類型提示 */
    > = DefineComponent<Bindings> & {
     /** 插槽相關(guān) */
      new(): { $slots: Slots }
    }

    而對(duì)于 Define 類型來說,我們知道其 v-slot 也有對(duì)應(yīng)的類型,且能通過$slots 拿到插槽內(nèi)容,所以類型如下

    type DefineTemplateComponent<
     Bindings extends object,
     Slots extends Record<string, Slot | undefined>,
     Props = {},
    > = DefineComponent<Props> & {
      new(): { $slots: { default(_: Bindings & { $slots: Slots }): any } }
    }

    小結(jié)一下

    ok,相信我開頭說的看懂只需要 1 分鐘不到應(yīng)該不是吹的,確實(shí)實(shí)現(xiàn)很簡單,但功能又很好用,解決了無法在單文件復(fù)用代碼的問題。

    我們來小結(jié)一下:

    • 通過 Define 來將你所需要復(fù)用的代碼包起來,通過 v-slot 拿到傳過來的參數(shù),同時(shí)支持渲染其他插槽內(nèi)容

    • 通過 Reuse 來復(fù)用代碼,通過傳參渲染出不同的內(nèi)容

    • 為了提升開發(fā)體驗(yàn),加入泛型參數(shù),所以 Define 和 Reuse 都有對(duì)應(yīng)的參數(shù)類型提示

    • 要記住使用條件,Define 在上,Reuse 在下,且不允許只使用 Reuse,因?yàn)槟貌坏?render function,所以會(huì)報(bào)錯(cuò)

    加個(gè)彩蛋吧

    實(shí)際上多次調(diào)用 createReusableTemplate 得到相應(yīng)的 DefineXXX、ReuseXXX 具有更好的語義化Vue3復(fù)用組件怎么使用

    Vue3復(fù)用組件怎么使用

    也就是說,我不想多次調(diào)用 createReusableTemplate 了,直接讓 define 和 reuse 支持 name 參數(shù)(作為唯一的 template key),只要兩者都有相同的 name,那就意味著它們是同一對(duì)

    如何魔改

    實(shí)際上也很簡單,既然要支持 prop name來作為唯一的 template key,那 define 和 reuse 都添加 prop name 不就好?

      const define = defineComponent({
        props {
          name: String
        }
      })
      const reuse = defineComponent({
        props {
          name: String
        }
      })

    然后之前不是有個(gè) render 局部變量嗎?因?yàn)楝F(xiàn)在要讓一個(gè) Define 支持通過 name 來區(qū)分不同的模板,那么我們判斷到傳入了 name,就映射對(duì)應(yīng)的的 render 不就好?

    這里我們通過 Map 的方式存儲(chǔ)不同 name 對(duì)應(yīng)的 render,然后 define setup 的時(shí)候存入對(duì)應(yīng) name 的 render,reuse setup 的時(shí)候通過 name 拿到對(duì)應(yīng)的 render,當(dāng)然如果沒傳入 name,默認(rèn)值是 default,也就是跟沒有魔改之前是一樣的

    const renderMap = new Map<string, Slot | undefined>()
    const define = defineComponent({
        props: {
          /** template name */
          name: String,
        },
        setup(props, { slots }) {
          return () => {
            const templateName: string = props.name || 'default'
            if (!renderMap.has(templateName)) {
              // render = slots.default
              renderMap.set(templateName, slots.default)
            }
          }
        },
      })
      const reuse = defineComponent({
        inheritAttrs: false,
        props: {
          name: String,
        },
        setup(props, { attrs, slots }) {
          return () => {
            const templateName: string = props.name || 'default'
            const render = renderMap.get(templateName)
            if (!render && process.env.NODE_ENV !== 'production')
              throw new Error(`[vue-reuse-template] Failed to find the definition of template${templateName}`)
            return render?.({ ...attrs, $slots: slots })
          }
        },
      })

    感謝各位的閱讀,以上就是“Vue3復(fù)用組件怎么使用”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Vue3復(fù)用組件怎么使用這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI