用Vue3 實做一個簡單的 modal hook 吧 (2)
Opened this issue · 0 comments
HelloChunWei commented
前言
在第一篇中製作了一個很簡單的hook 去呼叫 modal ,(前情提要) 那麼接下來我們就慢慢的把發現的問題給排除吧。
能夠動態決定使用哪個 modal
目前的 hook 只能開啟一個 modal,沒辦法透過 openModal 決定開哪一個modal,所以我們先來把這個問題給解決了吧。
那麼要解決這個問題的關鍵技術是利用vue的is,利用 is 就可以去動態的切換 component。
首先我們先建立一個modals/index.vue當作我們的主要進入點:
<template>
<!-- 這裡也用 v-if 決定是否顯示,然後利用 is 決定哪個 modal component -->
<component v-if="isShow" :is="componentName" />
</template>
<script lang="ts">
import { defineComponent, defineAsyncComponent, toRefs } from 'vue'
export default defineComponent({
name: 'ModalIndex',
props: ['isShow', 'component'],
setup(props) {
const { component } = toRefs(props)
const componentName = defineAsyncComponent(() => import(`./${component.value}.vue`))
return {
componentName
}
}
})
</script>這裡我們也用了 defineAsyncComponent 去動態的引入我們要的 component。 這樣就不用引入全部的 modal 進來了。
接下來修改一下我們的 index.ts:
import { createApp } from 'vue'
// 改用 index.vue
import Index from './index.vue'
type componentNameType = 'testModal'
export const useModal = () => {
// 在呼叫openModal 的時候順便指定是要用哪一個 modal
// component 利用 type 去指定,那目前只有 testModal 所以就先指定為 testModal
const openModal = ({ component }: { component: componentNameType }) => {
const container = document.createElement('div')
// 將 prop 傳入
const vnode = createApp(Index, {
isShow: true,
component
})
document.body.appendChild(container)
vnode.mount(container)
}
return {
openModal
}
}這樣子後我們要使用的話就是:
// 在需要呼叫的地方引入:
import { defineComponent, onMounted } from 'vue'
import { useModal } from '@hook/modal/index'
export const defineComponent({
setup () {
const { openModal } = useModal()
onMounted(() => {
openModal({ component: 'testModal' })
})
}
})那假設今天我們新增了一個 testModal2.vue 檔案,我們只要修改:
type componentNameType = 'testModal' | 'testModal2'那在使用的話只要
openModal({ component: 'testModal2' })這樣就可以直接使用了。
該如何關閉 modal?
目前我們可以動態切換想要用的 modal ,那們我們該如何關閉它?有一種最簡單暴力的方法,既然我們是在 body 外在新增一層 dom,那我們直接remove 掉就好了。
import { createApp } from 'vue'
// 改用 index.vue
import Index from './index.vue'
type componentNameType = 'testModal'
export const useModal = () => {
// 在呼叫openModal 的時候順便指定是要用哪一個 modal
// component 利用 type 去指定,那目前只有 testModal 所以就先指定為 testModal
const openModal = ({ component }: { component: componentNameType }) => {
const container = document.createElement('div')
// 直接remove
const closeModal = () => {
document.body.removeChild(container)
}
// 將 prop 傳入
const vnode = createApp(Index, {
isShow: true,
component,
closeModal
// 將 closeModal 傳入
})
document.body.appendChild(container)
vnode.mount(container)
}
return {
openModal
}
}<template>
<!-- 這裡也用 v-if 決定是否顯示,然後利用 is 決定哪個 modal component -->
<!-- 將closeModal 在傳入 component -->
<component v-if="isShow" :is="componentName" @closeModal="closeModal" />
</template>
<script lang="ts">
import { defineComponent, defineAsyncComponent, toRefs } from 'vue'
export default defineComponent({
name: 'ModalIndex',
props: ['isShow', 'component', 'closeModal'],
setup(props) {
const { component } = toRefs(props)
const componentName = defineAsyncComponent(() => import(`./${component.value}.vue`))
return {
componentName
}
}
})
</script>在 testModal 中使用:
<template>
<modal>
<template #header>
我是頭
</template>
<template #body>
body
</template>
<template>
<button @click="$emit('closeModal')">
關閉
</button>
</template>
</modal>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import Modal from './template.vue'
export default defineComponent({
name: 'test',
emits: ['closeModal'],
components: {
Modal
},
})
</script>到這裡我們就把上一篇提到的問題給解決了,但是卻還有幾個問題:
- 假如我們的 modal 是需要傳入 props 的話該怎們辦?
- 目前只要新增一個 modal 就要在 index.ts 中的 componentNameType 新增一個type
- 目前的 close function 是傳遞了兩個component的方式進行,該怎麼進行優化?
第一個問題會是比較嚴重的問題,如果自定義的 modal 無法傳 props 的話,使用的情境就會非常的少,第二以及第三的問題則是屬於重構 code 的問題,目前還不影響 modal 的使用情境。
那這些問題該怎麼解決呢?我們等到下一篇再說吧。