Vue3如何處理重復(fù)渲染代碼的問題
普通的場景
最近在做 Vue3 項(xiàng)目的時(shí)候,在思考一個(gè)小問題,其實(shí)是每個(gè)人都做過的一個(gè)場景,很簡單,看下方代碼
<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>
其實(shí)就是一個(gè)普通的不能再普通的循環(huán)遍歷渲染的案例,咱們往下接著看,如果這樣的遍歷在同一個(gè)組件里出現(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>
這個(gè)時(shí)候我們應(yīng)該咋辦呢?誒!很多人很快就能想出來了,那就是把循環(huán)的項(xiàng)抽取出來成一個(gè)組件,這樣就能減少很多代碼量了,比如我抽取成 Item.vue 這個(gè)組件
<!-- 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)一管理,提高代碼可維護(hù)性!?。?/p>
<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>
不難受嗎?
但是我事后越想越難受,就一個(gè)這么丁點(diǎn)代碼量的我都得抽取成組件,那我不敢想象以后我的項(xiàng)目組件數(shù)會(huì)多到什么地步,而且組件粒度太細(xì),確實(shí)也增加了后面開發(fā)者的負(fù)擔(dān)~
那么有沒有辦法,可以不抽取成組件呢?我可以在當(dāng)前組件里去提取嗎,而不需要去重新定義一個(gè)組件呢?例如下面的效果
<template> <div> <!-- 在本組件中定義一個(gè)復(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 代碼實(shí)現(xiàn)
想到這,馬上行動(dòng)起來,需要封裝一個(gè) useTemplate
來實(shí)現(xiàn)這個(gè)功能
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ù)用模板,則拋出錯(cuò)誤 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]; }
用的不爽
盡管做到這個(gè)地步,我還是覺得用的不爽,因?yàn)闆]有類型提示
我們想要的是比較爽的使用,那肯定得把類型的提示給支持上?。。?!于是給 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 類型,表示一個(gè)定義模板和復(fù)用模板的組件對 export type ReusableTemplatePair< Bindings extends object, Slots extends Record<string, Slot | undefined>, > = [DefineTemplateComponent<Bindings, Slots>, ReuseTemplateComponent<Bindings, Slots>]; // useTemplate 函數(shù),返回一個(gè)定義模板和復(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ù)用模板,則拋出錯(cuò)誤 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ù)渲染代碼的問題的詳細(xì)內(nèi)容,更多關(guān)于Vue3重復(fù)渲染的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用idea創(chuàng)建vue項(xiàng)目的圖文教程
Vue.js是一套構(gòu)建用戶界面的框架,只關(guān)注視圖層,它不僅易于上手,還便于與第三方庫或既有項(xiàng)目整合,下面這篇文章主要給大家介紹了關(guān)于使用idea創(chuàng)建vue項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2022-08-08nginx配置域名后的二級(jí)目錄訪問不同項(xiàng)目的配置操作
這篇文章主要介紹了nginx配置域名后的二級(jí)目錄訪問不同項(xiàng)目的配置操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11Vue模擬數(shù)據(jù),實(shí)現(xiàn)路由進(jìn)入商品詳情頁面的示例
今天小編就為大家分享一篇Vue模擬數(shù)據(jù),實(shí)現(xiàn)路由進(jìn)入商品詳情頁面的示例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08Vue?warn:Property?"state"?was?accessed?during
這篇文章主要為大家介紹了Vue?warn:Property?"state"?was?accessed?during?render的報(bào)錯(cuò)解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06解決removeEventListener 無法清除監(jiān)聽的問題
這篇文章主要介紹了解決removeEventListener 無法清除監(jiān)聽的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Vue3+vuedraggable實(shí)現(xiàn)動(dòng)態(tài)配置頁面
這篇文章主要為大家詳細(xì)介紹了Vue3如何利用vuedraggable實(shí)現(xiàn)動(dòng)態(tài)配置頁面,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-12-12