Vue3復用元件怎麼使用
前言
無論是vue 還是react,當遇到多處重複程式碼的時候,我們都會想著如何重複使用這些程式碼,而不是一個文件裡充斥著一堆冗餘程式碼。
實際上,vue 和react 都可以透過抽組件的方式來達到復用,但如果遇到一些很小的程式碼片段,你又不想抽到另外一個檔案的情況下,相較而言,react 可以在相同文件裡面宣告對應的小元件,或者透過render function 來實現,如:
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')} </> ) }
但對於.vue
模板來說,沒法像react 一樣在單一檔案裡面聲明其他元件,如果你要重複使用程式碼,那就只能透過抽離元件的方式。
可是啊可是啊!就如上面 Demo 組件,就零零散散兩三行程式碼,抽成一個組件你又覺得沒太必要,那唯一的解決方案就是 CV 大法? (當然,如果是諸如 list 之類的,可以用 v-for 程式碼,但這裡介紹的不是這種場景)
我知道你很急,但你先別急。如果我們可以透過在組件範圍內將要復用的模板圈起來,跟vue 說,餵,這代碼是我圈起來的,因為我有好幾處要用到,雖然目前你看起來好像不支持這功能,不過,沒事,你實現不了的,我來實現
那大致實現方案就是這樣子啦:
<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>
可是,這個方案究竟如何實現呢?畢竟牛都已經吹上天了,實現不了也要硬著頭皮折騰。好了,不賣關子,antfu 大佬其實已經實現了,叫做createReusableTemplate
#,且放到 VueUse 裡面了,具體可以點擊文檔看看。
用法
透過createReusableTemplate 拿到定義範本和重複使用範本的元件
<script setup> import { createReusableTemplate } from '@vueuse/core' const [DefineTemplate, ReuseTemplate] = createReusableTemplate() </script>
然後在你要重複使用程式碼的地方,將其用DefineTemplate
包起來,之後就可以透過ReuseTemplate
在單一檔案template 的任意地方使用了:
<template> <DefineTemplate> <!-- 这里定义你要复用的代码 --> </DefineTemplate> <!-- 在你要复用的地方使用ReuseTemplate, --> <ReuseTemplate /> ... <ReuseTemplate /> </template>
⚠️ DefineTemplate 務必在ReuseTemplate 之前使用
#我們看到,createReusableTemplate 回傳了一個Tuple,也就是define 和reuse 的元件對,然後,透過上面的範例就可以在單一檔案裡面複用多處程式碼了。
還有,實際上還可以透過物件解構的方式回傳一個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>
真是神奇,那咋實現呢
上面我們介紹了用法,相信應該沒人看不懂,上手成本確實為0,那接下來我們一起來看看是如何實現的。
我們知道,Vue3 定義元件除了透過script setup 的方式之外, 還可以透過defineComponent
的方式
const Demo = defineComponent({ props: { ..., }, setup(props, { attrs, slots }) { return () => { ... } } })
然後我們觀察下是如何定義模板的
<DefineFoo v-slot="{ msg }"> <div>Foo: {{ msg }}</div> </DefineFoo>
好像似曾相識? v-slot?,誒,臥槽,這不是插槽嗎!還有,模板程式碼看起來就是放在預設插槽的。
好,我們接下來看如何實現 Define 的功能。
實作Define
我們剛才說,模板是定義在預設插槽裡面,那我們可以定義個局部變數render,之後當在template 裡面使用Define 時會進入setup,這時候拿到slot.default,放在render 上不就好? ,程式碼如下
let render: Slot | undefined const Define = defineComponent({ setup(props, { slots, }) { return () => { /** 这里拿到默认插槽的渲染函数 */ render = slots.default } } })
對的,就是這麼簡單,對於Define 來說,核心程式碼就是這兩三行而已
實作Reuse
上面拿到render function 了,那我們在使用Reuse 的地方,將所得到的v-slot、attrs 等傳給render 不就好?
同樣,當我們在template 使用Reuse 的時候,就會進入setup,然後將得到的參數都傳給render,並return 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 }) } }, })
上面的attrs 也就是你在Reuse 上傳的prop 了
<ReuseFoo msg="msg1" />
而為啥還要傳個$slots?
上面其實有個例子,模板裡面還可以透過動態元件<component :is="$slots.default" />
的方式來拿到插槽的真正內容
<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 支持泛型参数,也就是说你要复用的模板需要什么参数,只需要通过传入对应类型即可,比如你有个 msg,是 string 类型,那么用法如下
const [DefineFoo, ReuseFoo] = createReusableTemplate<{ msg: string }>()
然后你就会发现,DefineFoo, ReuseFoo 都会对应的类型提示了
添加类型支持
我们上面说是用 defineComponent 得到 Define 和 Reuse,而 defineComponent 返回的类型就是 DefineComponent 呀
type DefineComponent<PropsOrPropOptions = {}, RawBindings = {}, ...>
假设模板参数类型为 Bindings 的话,那么对于 Reuse 来说,其既支持传参,也支持添加插槽内容,所以类型如下
type ReuseTemplateComponent< Bindings extends object, Slots extends Record<string, Slot | undefined>, /** Bindings使之有类型提示 */ > = DefineComponent<Bindings> & { /** 插槽相关 */ new(): { $slots: Slots } }
而对于 Define 类型来说,我们知道其 v-slot 也有对应的类型,且能通过$slots 拿到插槽内容,所以类型如下
type DefineTemplateComponent< Bindings extends object, Slots extends Record<string, Slot | undefined>, Props = {}, > = DefineComponent<Props> & { new(): { $slots: { default(_: Bindings & { $slots: Slots }): any } } }
小结一下
ok,相信我开头说的看懂只需要 1 分钟不到应该不是吹的,确实实现很简单,但功能又很好用,解决了无法在单文件复用代码的问题。
我们来小结一下:
通过 Define 来将你所需要复用的代码包起来,通过 v-slot 拿到传过来的参数,同时支持渲染其他插槽内容
通过 Reuse 来复用代码,通过传参渲染出不同的内容
为了提升开发体验,加入泛型参数,所以 Define 和 Reuse 都有对应的参数类型提示
要记住使用条件,Define 在上,Reuse 在下,且不允许只使用 Reuse,因为拿不到 render function,所以会报错
加个彩蛋吧
实际上多次调用 createReusableTemplate 得到相应的 DefineXXX、ReuseXXX 具有更好的语义化)
也就是说,我不想多次调用 createReusableTemplate 了,直接让 define 和 reuse 支持 name 参数(作为唯一的 template key),只要两者都有相同的 name,那就意味着它们是同一对
如何魔改
实际上也很简单,既然要支持 prop name
来作为唯一的 template key,那 define 和 reuse 都添加 prop name 不就好?
const define = defineComponent({ props { name: String } }) const reuse = defineComponent({ props { name: String } })
然后之前不是有个 render 局部变量吗?因为现在要让一个 Define 支持通过 name 来区分不同的模板,那么我们判断到传入了 name,就映射对应的的 render 不就好?
这里我们通过 Map 的方式存储不同 name 对应的 render,然后 define setup 的时候存入对应 name 的 render,reuse setup 的时候通过 name 拿到对应的 render,当然如果没传入 name,默认值是 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復用元件怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

vue3+vite:src使用require動態導入圖片報錯和解決方法vue3+vite動態的導入多張圖片vue3如果使用的是typescript開發,就會出現require引入圖片報錯,requireisnotdefined不能像使用vue2這樣imgUrl:require(' …/assets/test.png')導入,是因為typescript不支援require所以用import導入,下面介紹如何解決:使用awaitimport

tinymce是一個功能齊全的富文本編輯器插件,但在vue中引入tinymce並不像別的Vue富文本插件一樣那麼順利,tinymce本身並不適配Vue,還需要引入@tinymce/tinymce-vue,並且它是國外的富文本插件,沒有透過中文版本,需要在其官網下載翻譯包(可能需要翻牆)。 1.安裝相關依賴npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下載中文包3.引入皮膚和漢化包在項目public資料夾下新建tinymce資料夾,將下載的

Vue實作部落格前端,需要實作markdown的解析,如果有程式碼則需要實作程式碼的高亮。 Vue的markdown解析函式庫很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。這些庫都大同小異。這裡選用的是marked,程式碼高亮的函式庫選用的是highlight.js。具體實現步驟如下:一、安裝依賴庫在vue專案下開啟命令窗口,並輸入以下命令npminstallmarked-save//marked用於將markdown轉換成htmlnpmins

想要實現頁面的局部刷新,我們只需要實現局部元件(dom)的重新渲染。在Vue中,想要實現這效果最簡單的方式方法就是使用v-if指令。在Vue2中我們除了使用v-if指令讓局部dom的重新渲染,也可以新建一個空白元件,需要刷新局部頁面時跳轉至這個空白元件頁面,然後在空白元件內的beforeRouteEnter守衛中又跳轉回原來的頁面。如下圖所示,如何在Vue3.X中實現點擊刷新按鈕實現紅框範圍內的dom重新加載,並展示對應的加載狀態。由於Vue3.X中scriptsetup語法中組件內守衛只有o

vue3專案打包發佈到伺服器後存取頁面顯示空白1、處理vue.config.js檔案中的publicPath處理如下:const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({publicPath :process.env.NODE_ENV==='production'?'./':'/&

前言無論是vue還是react,當我們遇到多處重複程式碼的時候,我們都會想著如何重複使用這些程式碼,而不是一個檔案裡充斥著一堆冗餘程式碼。實際上,vue和react都可以透過抽組件的方式來達到復用,但如果遇到一些很小的程式碼片段,你又不想抽到另外一個檔案的情況下,相比而言,react可以在相同文件裡面宣告對應的小元件,或透過renderfunction來實現,如:constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

最終效果安裝VueCropper組件yarnaddvue-cropper@next上面的安裝值針對Vue3的,如果時Vue2或想使用其他的方式引用,請訪問它的npm官方地址:官方教程。在元件中引用使用時也很簡單,只需要引入對應的元件和它的樣式文件,我這裡沒有在全域引用,只在我的元件檔案中引入import{userInfoByRequest}from'../js/api' import{VueCropper}from'vue-cropper&

vue3+ts+axios+pinia實作無感刷新1.先在專案中下載aiXos和pinianpmipinia--savenpminstallaxios--save2.封裝axios請求-----下載js-cookienpmiJS-cookie-s//引入aixosimporttype{AxiosRequestConfigig ,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess
