Preface
Whether it is vue or react, when we encounter multiple duplicate codes, we will think about how to reuse these codes instead of A file filled with redundant code.
In fact, both vue and react can achieve reuse by extracting components, but if you encounter some small code fragments and you don’t want to extract another file, compared with In other words, react can declare the corresponding widget in the same file, or implement it through render function, such as:
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')} </> ) }
But for the .vue
template, it cannot be used like react. Declare other components in a single file. If you want to reuse code, you can only extract the components.
But, but! Just like the Demo component above, there are only two or three lines of code here and there. If you extract it into one component and you don’t think it is necessary, then the only solution is the CV method? (Of course, if it is something like a list, you can use v-for code, but this is not the scenario introduced here)
I know you are in a hurry, but don't be anxious yet. If we can circle the template to be reused within the scope of the component, tell Vue, hey, I circled this code because I have to use it in several places, although it seems that you do not support this function at the moment. But, it’s okay, if you can’t achieve it, I will implement it
The rough implementation plan is like this:
<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>
But, what is this plan like? What about implementation? After all, you have already boasted to the sky, and if you can’t achieve it, you still have to endure the hardships. Okay, don’t give it away, the antfu boss has actually implemented it, called createReusableTemplate
, and put it in VueUse. You can click on the document to see the details.
Usage
Get the components that define templates and reuse templates through createReusableTemplate
<script setup> import { createReusableTemplate } from '@vueuse/core' const [DefineTemplate, ReuseTemplate] = createReusableTemplate() </script>
Then use DefineTemplate# where you want to reuse code ##Wrap it up, and then you can use it anywhere in the single file template through
ReuseTemplate:
<template> <DefineTemplate> <!-- 這里定義你要復(fù)用的代碼 --> </DefineTemplate> <!-- 在你要復(fù)用的地方使用ReuseTemplate, --> <ReuseTemplate /> ... <ReuseTemplate /> </template>
?? DefineTemplate must be used before ReuseTemplateWe see that createReusableTemplate returns a Tuple, that is, a component pair of define and reuse. Then, through the above example, multiple codes can be reused in a single file. Also, you can actually return a define and reuse through object destructuring (it’s amazing, I won’t expand on this article. If you are interested, I will share it next time). The usage is the same as above. The example is as follows.
<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>
defineComponent
const Demo = defineComponent({ props: { ..., }, setup(props, { attrs, slots }) { return () => { ... } } })Then let’s observe how to define the template
<DefineFoo v-slot="{ msg }"> <div>Foo: {{ msg }}</div> </DefineFoo>Looks like déjà vu? v-slot?, eh, damn, isn’t this a slot! Also, the template code seems to be placed in the default slot. Okay, let’s take a look at how to implement the Define function. Implementing DefineWe just said that the template is defined in the default slot, then we can define a local variable render, and then when using Define in the template, it will enter the setup. At this time Wouldn't it be good to get slot.default and put it on render? , the code is as follows
let render: Slot | undefined const Define = defineComponent({ setup(props, { slots, }) { return () => { /** 這里拿到默認(rèn)插槽的渲染函數(shù) */ render = slots.default } } })Yes, it is that simple. For Define, the core code is just these two or three linesImplementing ReuseI got the render function above , then where we use Reuse, wouldn't it be good to pass the obtained v-slot, attrs, etc. to render? Similarly, when we use Reuse in template, we will enter setup, then pass all the parameters to render, and return the result of render.
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 }) } }, })The attrs above are also It’s the prop you uploaded in Reuse
<ReuseFoo msg="msg1" />. Why do you need to pass $slots? There is actually an example above. In the template, you can also get the real value of the slot through the dynamic component
Content
<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>
<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>
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 具有更好的語義化)
也就是說,我不想多次調(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 }) } }, })
The above is the detailed content of How to use Vue3 reusable components. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

To achieve partial refresh of the page, we only need to implement the re-rendering of the local component (dom). In Vue, the easiest way to achieve this effect is to use the v-if directive. In Vue2, in addition to using the v-if instruction to re-render the local dom, we can also create a new blank component. When we need to refresh the local page, jump to this blank component page, and then jump back in the beforeRouteEnter guard in the blank component. original page. As shown in the figure below, how to click the refresh button in Vue3.X to reload the DOM within the red box and display the corresponding loading status. Since the guard in the component in the scriptsetup syntax in Vue3.X only has o

Vue implements the blog front-end and needs to implement markdown parsing. If there is code, it needs to implement code highlighting. There are many markdown parsing libraries for Vue, such as markdown-it, vue-markdown-loader, marked, vue-markdown, etc. These libraries are all very similar. Marked is used here, and highlight.js is used as the code highlighting library. The specific implementation steps are as follows: 1. Install dependent libraries. Open the command window under the vue project and enter the following command npminstallmarked-save//marked to convert markdown into htmlnpmins

vue3+vite:src uses require to dynamically import images and error reports and solutions. vue3+vite dynamically imports multiple images. If vue3 is using typescript development, require will introduce image errors. requireisnotdefined cannot be used like vue2 such as imgUrl:require(' .../assets/test.png') is imported because typescript does not support require, so import is used. Here is how to solve it: use awaitimport

tinymce is a fully functional rich text editor plug-in, but introducing tinymce into vue is not as smooth as other Vue rich text plug-ins. tinymce itself is not suitable for Vue, and @tinymce/tinymce-vue needs to be introduced, and It is a foreign rich text plug-in and has not passed the Chinese version. You need to download the translation package from its official website (you may need to bypass the firewall). 1. Install related dependencies npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2. Download the Chinese package 3. Introduce the skin and Chinese package. Create a new tinymce folder in the project public folder and download the

The final effect is to install the VueCropper component yarnaddvue-cropper@next. The above installation value is for Vue3. If it is Vue2 or you want to use other methods to reference, please visit its official npm address: official tutorial. It is also very simple to reference and use it in a component. You only need to introduce the corresponding component and its style file. I do not reference it globally here, but only introduce import{userInfoByRequest}from'../js/api' in my component file. import{VueCropper}from'vue-cropper&

vue3+ts+axios+pinia realizes senseless refresh 1. First download aiXos and pinianpmipinia in the project--savenpminstallaxios--save2. Encapsulate axios request-----Download js-cookienpmiJS-cookie-s//Introduce aixosimporttype{AxiosRequestConfig ,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess

After the vue3 project is packaged and published to the server, the access page displays blank 1. The publicPath in the vue.config.js file is processed as follows: const{defineConfig}=require('@vue/cli-service') module.exports=defineConfig({publicPath :process.env.NODE_ENV==='production'?'./':'/&

Preface Whether it is vue or react, when we encounter multiple repeated codes, we will think about how to reuse these codes instead of filling a file with a bunch of redundant codes. In fact, both vue and react can achieve reuse by extracting components, but if you encounter some small code fragments and you don’t want to extract another file, in comparison, react can be used in the same Declare the corresponding widget in the file, or implement it through renderfunction, such as: constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(
