如何使用Vue3設(shè)計(jì)實(shí)現(xiàn)一個(gè)Model組件淺析
一、組件設(shè)計(jì)
組件就是把圖形、非圖形的各種邏輯均抽象為一個(gè)統(tǒng)一的概念(組件)來實(shí)現(xiàn)開發(fā)的模式
現(xiàn)在有一個(gè)場(chǎng)景,點(diǎn)擊新增與編輯都彈框出來進(jìn)行填寫,功能上大同小異,可能只是標(biāo)題內(nèi)容或者是顯示的主體內(nèi)容稍微不同
這時(shí)候就沒必要寫兩個(gè)組件,只需要根據(jù)傳入的參數(shù)不同,組件顯示不同內(nèi)容即可
這樣,下次開發(fā)相同界面程序時(shí)就可以寫更少的代碼,意義著更高的開發(fā)效率,更少的 Bug和更少的程序體積
二、需求分析
實(shí)現(xiàn)一個(gè)Modal組件,首先確定需要完成的內(nèi)容:
- 遮罩層
- 標(biāo)題內(nèi)容
- 主體內(nèi)容
- 確定和取消按鈕
主體內(nèi)容需要靈活,所以可以是字符串,也可以是一段 html 代碼
特點(diǎn)是它們?cè)诋?dāng)前vue實(shí)例之外獨(dú)立存在,通常掛載于body之上
除了通過引入import的形式,我們還可通過API的形式進(jìn)行組件的調(diào)用
還可以包括配置全局樣式、國際化、與typeScript結(jié)合
三、實(shí)現(xiàn)流程
首先看看大致流程:
- 目錄結(jié)構(gòu)
- 組件內(nèi)容
- 實(shí)現(xiàn) API 形式
- 事件處理
- 其他完善
目錄結(jié)構(gòu)
Modal組件相關(guān)的目錄結(jié)構(gòu)
├── plugins
│ └── modal
│ ├── Content.tsx // 維護(hù) Modal 的內(nèi)容,用于 h 函數(shù)和 jsx 語法
│ ├── Modal.vue // 基礎(chǔ)組件
│ ├── config.ts // 全局默認(rèn)配置
│ ├── index.ts // 入口
│ ├── locale // 國際化相關(guān)
│ │ ├── index.ts
│ │ └── lang
│ │ ├── en-US.ts
│ │ ├── zh-CN.ts
│ │ └── zh-TW.ts
│ └── modal.type.ts // ts類型聲明相關(guān)
因?yàn)?Modal 會(huì)被 app.use(Modal) 調(diào)用作為一個(gè)插件,所以都放在plugins目錄下
組件內(nèi)容
首先實(shí)現(xiàn)modal.vue的主體顯示內(nèi)容大致如下
<Teleport to="body" :disabled="!isTeleport">
<div v-if="modelValue" class="modal">
<div
class="mask"
:style="style"
@click="maskClose && !loading && handleCancel()"
></div>
<div class="modal__main">
<div class="modal__title line line--b">
<span>{{ title || t("r.title") }}</span>
<span
v-if="close"
:title="t('r.close')"
class="close"
@click="!loading && handleCancel()"
>?</span
>
</div>
<div class="modal__content">
<Content v-if="typeof content === 'function'" :render="content" />
<slot v-else>
{{ content }}
</slot>
</div>
<div class="modal__btns line line--t">
<button :disabled="loading" @click="handleConfirm">
<span class="loading" v-if="loading"> ? </span>{{ t("r.confirm") }}
</button>
<button @click="!loading && handleCancel()">
{{ t("r.cancel") }}
</button>
</div>
</div>
</div>
</Teleport>最外層上通過Vue3 Teleport 內(nèi)置組件進(jìn)行包裹,其相當(dāng)于傳送門,將里面的內(nèi)容傳送至body之上
并且從DOM結(jié)構(gòu)上來看,把modal該有的內(nèi)容(遮罩層、標(biāo)題、內(nèi)容、底部按鈕)都實(shí)現(xiàn)了
關(guān)于主體內(nèi)容
<div class="modal__content">
<Content v-if="typeof content==='function'"
:render="content" />
<slot v-else>
{{content}}
</slot>
</div>可以看到根據(jù)傳入content的類型不同,對(duì)應(yīng)顯示不同得到內(nèi)容
最常見的則是通過調(diào)用字符串和默認(rèn)插槽的形式
// 默認(rèn)插槽
<Modal v-model="show"
title="演示 slot">
<div>hello world~</div>
</Modal>
// 字符串
<Modal v-model="show"
title="演示 content"
content="hello world~" />通過 API 形式調(diào)用Modal組件的時(shí)候,content可以使用下面兩種
- h 函數(shù)
$modal.show({
title: '演示 h 函數(shù)',
content(h) {
return h(
'div',
{
style: 'color:red;',
onClick: ($event: Event) => console.log('clicked', $event.target)
},
'hello world ~'
);
}
});- JSX
$modal.show({
title: '演示 jsx 語法',
content() {
return (
<div
onClick={($event: Event) => console.log('clicked', $event.target)}
>
hello world ~
</div>
);
}
});實(shí)現(xiàn) API 形式
那么組件如何實(shí)現(xiàn)API形式調(diào)用Modal組件呢?
在Vue2中,我們可以借助Vue實(shí)例以及Vue.extend的方式獲得組件實(shí)例,然后掛載到body上
import Modal from './Modal.vue';
const ComponentClass = Vue.extend(Modal);
const instance = new ComponentClass({ el: document.createElement("div") });
document.body.appendChild(instance.$el);雖然Vue3移除了Vue.extend方法,但可以通過createVNode實(shí)現(xiàn)
import Modal from './Modal.vue';
const container = document.createElement('div');
const vnode = createVNode(Modal);
render(vnode, container);
const instance = vnode.component;
document.body.appendChild(container);在Vue2中,可以通過this的形式調(diào)用全局 API
export default {
install(vue) {
vue.prototype.$create = create
}
}而在 Vue3 的 setup 中已經(jīng)沒有 this概念了,需要調(diào)用app.config.globalProperties掛載到全局
export default {
install(app) {
app.config.globalProperties.$create = create
}
}事件處理
下面再看看看Modal組件內(nèi)部是如何處理「確定」「取消」事件的,既然是Vue3,當(dāng)然采用Compositon API 形式
// Modal.vue
setup(props, ctx) {
let instance = getCurrentInstance(); // 獲得當(dāng)前組件實(shí)例
onBeforeMount(() => {
instance._hub = {
'on-cancel': () => {},
'on-confirm': () => {}
};
});
const handleConfirm = () => {
ctx.emit('on-confirm');
instance._hub['on-confirm']();
};
const handleCancel = () => {
ctx.emit('on-cancel');
ctx.emit('update:modelValue', false);
instance._hub['on-cancel']();
};
return {
handleConfirm,
handleCancel
};
}在上面代碼中,可以看得到除了使用傳統(tǒng)emit的形式使父組件監(jiān)聽,還可通過_hub屬性中添加 on-cancel,on-confirm方法實(shí)現(xiàn)在API中進(jìn)行監(jiān)聽
app.config.globalProperties.$modal = {
show({}) {
/* 監(jiān)聽 確定、取消 事件 */
}
}下面再來目睹下_hub是如何實(shí)現(xiàn)
// index.ts
app.config.globalProperties.$modal = {
show({
/* 其他選項(xiàng) */
onConfirm,
onCancel
}) {
/* ... */
const { props, _hub } = instance;
const _closeModal = () => {
props.modelValue = false;
container.parentNode!.removeChild(container);
};
// 往 _hub 新增事件的具體實(shí)現(xiàn)
Object.assign(_hub, {
async 'on-confirm'() {
if (onConfirm) {
const fn = onConfirm();
// 當(dāng)方法返回為 Promise
if (fn && fn.then) {
try {
props.loading = true;
await fn;
props.loading = false;
_closeModal();
} catch (err) {
// 發(fā)生錯(cuò)誤時(shí),不關(guān)閉彈框
console.error(err);
props.loading = false;
}
} else {
_closeModal();
}
} else {
_closeModal();
}
},
'on-cancel'() {
onCancel && onCancel();
_closeModal();
}
});
}
};其他完善
關(guān)于組件實(shí)現(xiàn)國際化、與typsScript結(jié)合,大家可以根據(jù)自身情況在此基礎(chǔ)上進(jìn)行更改
總結(jié)
到此這篇關(guān)于如何使用Vue3設(shè)計(jì)實(shí)現(xiàn)一個(gè)Model組件的文章就介紹到這了,更多相關(guān)Vue3設(shè)計(jì)實(shí)現(xiàn)Model組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue?過濾、模糊查詢及計(jì)算屬性?computed詳解
計(jì)算屬性是vue里面為了簡(jiǎn)化在模板語法中對(duì)響應(yīng)式屬性做計(jì)算而存在的,這篇文章主要介紹了vue?過濾、模糊查詢(計(jì)算屬性?computed),需要的朋友可以參考下2022-11-11
Vue實(shí)現(xiàn)當(dāng)前頁面刷新的4種方法舉例
我們?cè)陂_發(fā)vue的頁面的時(shí)候,有時(shí)候會(huì)遇到需要刷新當(dāng)前頁面功能,但是vue框架自帶的router是不支持刷新當(dāng)前頁面功能,下面這篇文章主要給大家介紹了關(guān)于Vue實(shí)現(xiàn)當(dāng)前頁面刷新的4種方法,需要的朋友可以參考下2023-04-04
vue項(xiàng)目中實(shí)現(xiàn)的微信分享功能示例
這篇文章主要介紹了vue項(xiàng)目中實(shí)現(xiàn)的微信分享功能,結(jié)合實(shí)例形式分析了基于vue.js實(shí)現(xiàn)的微信分享功能具體定義與使用方法,需要的朋友可以參考下2019-01-01
vue+element項(xiàng)目中過濾輸入框特殊字符小結(jié)
這篇文章主要介紹了vue+element項(xiàng)目中過濾輸入框特殊字符小結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08

