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

想到頭禿也想不到的Vue3復(fù)用組件還可以這么hack的用法

 更新時(shí)間:2023年04月17日 11:44:54   作者:暴走老七  
這篇文章主要為大家介紹了想到頭禿也想不到的Vue3復(fù)用組件還可以這么hack的用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

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

實(shí)際上,vue 和 react 都可以通過(guò)抽組件的方式來(lái)達(dá)到復(fù)用,但如果遇到一些很小的代碼片段,你又不想抽到另外一個(gè)文件的情況下,相比而言,react 可以在相同文件里面聲明對(duì)應(yīng)的小組件,或者通過(guò) render function 來(lái)實(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 模板 來(lái)說(shuō),沒(méi)法像 react 一樣在單個(gè)文件里面聲明其他組件,如果你要復(fù)用代碼,那就只能通過(guò)抽離組件的方式。

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

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

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

接下來(lái)我會(huì)給大家介紹下 createreusabletemplate 如何使用,以及是如何實(shí)現(xiàn)的(不用緊張,上手成本為 0,且看懂只需要 1 分鐘不到)

用法

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

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

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

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

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

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

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

<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>

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

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

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

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

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

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

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

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

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

我們剛才說(shuō),模板是定義在默認(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ì)的,就是這么簡(jiǎn)單,對(duì)于 Define 來(lái)說(shuō),核心代碼就是這兩三行而已

實(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 () => {
        /**
         * 沒(méi)有render,有兩種可能
         * 1. 你沒(méi)Define
         * 2. Define寫(xiě)在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è)例子,模板里面還可以通過(guò)動(dòng)態(tài)組件<component :is="$slots.default" />的方式來(lái)拿到插槽的真正內(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>

當(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>

具體怎么玩出花,你來(lái)定~

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

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

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

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

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

添加類型支持

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

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

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

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

而對(duì)于 Define 類型來(lái)說(shuō),我們知道其 v-slot 也有對(duì)應(yīng)的類型,且能通過(guò)$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,相信我開(kāi)頭說(shuō)的看懂只需要 1 分鐘不到應(yīng)該不是吹的,確實(shí)實(shí)現(xiàn)很簡(jiǎn)單,但功能又很好用,解決了無(wú)法在單文件復(fù)用代碼的問(wèn)題。

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

  • 通過(guò) Define 來(lái)將你所需要復(fù)用的代碼包起來(lái),通過(guò) v-slot 拿到傳過(guò)來(lái)的參數(shù),同時(shí)支持渲染其他插槽內(nèi)容
  • 通過(guò) Reuse 來(lái)復(fù)用代碼,通過(guò)傳參渲染出不同的內(nèi)容
  • 為了提升開(kāi)發(fā)體驗(yàn),加入泛型參數(shù),所以 Define 和 Reuse 都有對(duì)應(yīng)的參數(shù)類型提示
  • 要記住使用條件,Define 在上,Reuse 在下,且不允許只使用 Reuse,因?yàn)槟貌坏?render function,所以會(huì)報(bào)錯(cuò)

加個(gè)彩蛋吧

到了上面,實(shí)際上各位讀者已經(jīng)知道如何使用和實(shí)現(xiàn)了,但我比較喜歡瞎折騰,所以就對(duì)代碼稍稍魔改一下(大家這里看個(gè)樂(lè)就行,實(shí)際上多次調(diào)用 createReusableTemplate 得到相應(yīng)的 DefineXXX、ReuseXXX 具有更好的語(yǔ)義化

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

如何魔改

實(shí)際上也很簡(jiǎn)單,既然要支持 prop name來(lái)作為唯一的 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 支持通過(guò) name 來(lái)區(qū)分不同的模板,那么我們判斷到傳入了 name,就映射對(duì)應(yīng)的的 render 不就好?

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

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ù)用代碼的過(guò)程了,確實(shí)很 hack 吧,至少我是這么覺(jué)得的。正所謂牛逼的代碼不在于量,而在于質(zhì),可稱之為真正的短小精悍。

最后我們來(lái)總結(jié)一下:

Vue3 單文件復(fù)用代碼只能通過(guò)抽組件的方式,但是如果只有幾行代碼的情況下,很多人就可能覺(jué)得麻煩,直接 CV 大法了。

所以這時(shí)候 createReusableTemplate 就橫空出世了,其通過(guò)定義模板的方式,在 setup 里面拿到 render function,然后在你復(fù)用代碼的地方調(diào)用該 render function 來(lái)實(shí)現(xiàn)代碼的復(fù)用。

當(dāng)然,需要注意的是,必須先定義模板,然后再?gòu)?fù)用模板,不然找不到對(duì)應(yīng)的 render function,代碼會(huì)報(bào)錯(cuò)。

最后,我們又魔改了一下代碼,直接只使用一次 createReusableTemplate,通過(guò) name 的方式來(lái)區(qū)分不同了的模板,當(dāng)然,還是推薦用魔改之前的用法,因?yàn)檫@種方式語(yǔ)義化不好。

以上就是想到頭禿也想不到的Vue3復(fù)用組件還可以這么hack的詳細(xì)內(nèi)容,更多關(guān)于Vue3復(fù)用組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論