Vue3如何處理重復(fù)渲染代碼的問題
普通的場景
最近在做 Vue3 項目的時候,在思考一個小問題,其實是每個人都做過的一個場景,很簡單,看下方代碼
<template> <div v-for="item in data" :key="item.age"> <span>{[ item.name }}</span> <span>今年已經(jīng) </span> <span>{[ item.age }}</span> <span>歲啦</span> </div> </template> <script setup lang="ts"> const data = new Array(20).fill(0).map((_, index) => ({ name:`sunshine-${index}`, age: index })); </script>
其實就是一個普通的不能再普通的循環(huán)遍歷渲染的案例,咱們往下接著看,如果這樣的遍歷在同一個組件里出現(xiàn)了很多次,比如下方代碼
<template> <div> <div v-for="item in data" :key="item.age"> <span>{{ item.name }}</span> <span>今年已經(jīng) </span> <span>{{ item.age }}</span> <span>歲啦</span> </div> <!-- 多處使用 --> <div v-for="item in data" :key="item.age"> <span>{[ item.name }}</span> <span>今年已經(jīng) </span> <span>{{ item.age }}</span> <span>歲啦</span> </div> <!-- 多處使用 --> <div v-for="item in data" :key="item.age"> <span>{[ item.name }}</span> <span>今年已經(jīng) </span> <span>{[ item.age }}</span> <span>歲啦</span> </div> <!-- 多處使用 --> <div v-for="item in data" :key="item.age"> <span>{ item.name }}</span> <span>今年已經(jīng) </span> <span>{[ item.age }}</span> <span>歲啦</span> </div> </div> </temple>
這個時候我們應(yīng)該咋辦呢?誒!很多人很快就能想出來了,那就是把循環(huán)的項抽取出來成一個組件,這樣就能減少很多代碼量了,比如我抽取成 Item.vue 這個組件
<!-- Item.vue --> <template> <div> <span>{{ name }}</span> <span>今年已經(jīng) </span> <span>{ age }}</span> <span>歲啦</span> </div> </template> <script lang="ts" setup>defineProps({ name: { type: String, default:'', }, age: { type:Number default:0, }; }) </script>
然后直接可以引用并使用它,這樣大大減少了代碼量,并且統(tǒng)一管理,提高代碼可維護性?。?!
<template> <div> <Item v-for="item in data" :key="item.age" :age="item.age" :name="item.name" /></div> </template> <script setup lang="ts"> import Item from './Item.vue'; const data = new Array(20).fill(0).map((_, index) => ({ name:`sunshine-${index}`, age: index </script>
不難受嗎?
但是我事后越想越難受,就一個這么丁點代碼量的我都得抽取成組件,那我不敢想象以后我的項目組件數(shù)會多到什么地步,而且組件粒度太細,確實也增加了后面開發(fā)者的負擔~
那么有沒有辦法,可以不抽取成組件呢?我可以在當前組件里去提取嗎,而不需要去重新定義一個組件呢?例如下面的效果
<template> <div> <!-- 在本組件中定義一個復(fù)用模板 --> <DefineTemplate v-slot="[ age, name }"> <div> <span>{{ name }}</span> <span>今年已經(jīng) </span> <span>{{ age }}</span> <span>歲啦</span> </div> </DefineTemplate> <!-- 可多次使用復(fù)用模板 --> <ReuseTemplate v-for="item in data" :key="item.age" :age-"item.age" :name-"item.name"> <ReuseTemplate v-for="item in data" :key="item,age" :age="item,age" :name-"item, name"> <ReuseTemplate v-for="item in data" :key="item.age" :age="item.age" :name="item.name"> </div> </template> <script setup lang="ts"> import { useTemplate } from '@/hooks/useTemplate'; const data = new Array(20).fill(0).map((_, index) => ({ name:`sunshine-$[index}`, age: index, }); const [DefineTemplate, ReuseTemplate] = useTemplate(); </script>
useTemplate 代碼實現(xiàn)
想到這,馬上行動起來,需要封裝一個 useTemplate
來實現(xiàn)這個功能
import { defineComponent, shallowRef } from 'vue'; import { camelCase } from 'lodash'; import type { Slot } from 'vue'; //將橫線命名轉(zhuǎn)大小駝峰 function keysToCamelKebabCase(obj: Record<string,any>)[ const newObj: typeof obj = ;for (const key in obj) newObj[camelCase(key)] = obj[key]; return newObj; } export const useTemplate = () => { const render = shallowRef<Slot undefined>(); const define = defineComponent({ setup(_,[ slots }) { return () => { //將復(fù)用模板的渲染函數(shù)內(nèi)容保存起來 render.value = slotsdefault; } } }) const reuse = defineComponent({ setup(_, [ attrs, slots }) { return () => { // 還沒定義復(fù)用模板,則拋出錯誤 if (!render.value){ throw new Error( 你還沒定義復(fù)用模板呢!); } // 執(zhí)行渲染函數(shù),傳入 attrs、slots const vnode = render.value([ ...keysToCamelKebabCase(attrs), $slots: slots }); return vnode.length === 1 ? vnode[0] : vnode; } }, }); return [define reuse]; }
用的不爽
盡管做到這個地步,我還是覺得用的不爽,因為沒有類型提示
我們想要的是比較爽的使用,那肯定得把類型的提示給支持上?。。?!于是給 useTemplate 加上泛型??!加上之后就有類型提示啦~~~~
加上泛型后的 useTemplate 代碼如下:
import { defineComponent, shallowRef } from 'vue'; import { camelCase } from 'lodash'; import type { DefineComponent, Slot } from 'vue'; // 將橫線命名轉(zhuǎn)換為駝峰命名 function keysToCamelKebabCase(obj: Record<string, any>) { const newObj: typeof obj = {}; for (const key in obj) newObj[camelCase(key)] = obj[key]; return newObj; } // 定義 DefineTemplateComponent 類型,該類型表示定義模板的組件 export type DefineTemplateComponent< Bindings extends object, Slots extends Record<string, Slot | undefined>, > = DefineComponent<object> & { new (): { $slots: { default(_: Bindings & { $slots: Slots }): any } }; }; // 定義 ReuseTemplateComponent 類型,該類型表示復(fù)用模板的組件 export type ReuseTemplateComponent< Bindings extends object, Slots extends Record<string, Slot | undefined>, > = DefineComponent<Bindings> & { new (): { $slots: Slots }; }; // 定義 ReusableTemplatePair 類型,表示一個定義模板和復(fù)用模板的組件對 export type ReusableTemplatePair< Bindings extends object, Slots extends Record<string, Slot | undefined>, > = [DefineTemplateComponent<Bindings, Slots>, ReuseTemplateComponent<Bindings, Slots>]; // useTemplate 函數(shù),返回一個定義模板和復(fù)用模板的組件對 export const useTemplate = < Bindings extends object, Slots extends Record<string, Slot | undefined> = Record<string, Slot | undefined>, >(): ReusableTemplatePair<Bindings, Slots> => { const render = shallowRef<Slot | undefined>(); // 定義 DefineTemplateComponent 組件 const define = defineComponent({ setup(_, { slots }) { return () => { // 將復(fù)用模板的渲染函數(shù)內(nèi)容保存起來 render.value = slots.default; }; }, }) as DefineTemplateComponent<Bindings, Slots>; // 定義 ReuseTemplateComponent 組件 const reuse = defineComponent({ setup(_, { attrs, slots }) { return () => { // 還沒定義復(fù)用模板,則拋出錯誤 if (!render.value) { throw new Error('你還沒定義復(fù)用模板呢!'); } // 執(zhí)行渲染函數(shù),傳入 attrs、slots const vnode = render.value({ ...keysToCamelKebabCase(attrs), $slots: slots }); return vnode.length === 1 ? vnode[0] : vnode; }; }, }) as ReuseTemplateComponent<Bindings, Slots>; return [define, reuse]; };
以上就是Vue3如何處理重復(fù)渲染代碼的問題的詳細內(nèi)容,更多關(guān)于Vue3重復(fù)渲染的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue模擬數(shù)據(jù),實現(xiàn)路由進入商品詳情頁面的示例
今天小編就為大家分享一篇Vue模擬數(shù)據(jù),實現(xiàn)路由進入商品詳情頁面的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08Vue?warn:Property?"state"?was?accessed?during
這篇文章主要為大家介紹了Vue?warn:Property?"state"?was?accessed?during?render的報錯解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06解決removeEventListener 無法清除監(jiān)聽的問題
這篇文章主要介紹了解決removeEventListener 無法清除監(jiān)聽的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Vue3+vuedraggable實現(xiàn)動態(tài)配置頁面
這篇文章主要為大家詳細介紹了Vue3如何利用vuedraggable實現(xiàn)動態(tài)配置頁面,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的可以參考一下2023-12-12