HelloChunWei / blog

個人部落格,紀錄自己的知識
MIT License
7 stars 0 forks source link

用Vue3 實做一個簡單的 modal hook 吧 (2) #12

Open HelloChunWei opened 2 years ago

HelloChunWei commented 2 years ago

前言

在第一篇中製作了一個很簡單的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>

到這裡我們就把上一篇提到的問題給解決了,但是卻還有幾個問題:

  1. 假如我們的 modal 是需要傳入 props 的話該怎們辦?
  2. 目前只要新增一個 modal 就要在 index.ts 中的 componentNameType 新增一個type
  3. 目前的 close function 是傳遞了兩個component的方式進行,該怎麼進行優化?

第一個問題會是比較嚴重的問題,如果自定義的 modal 無法傳 props 的話,使用的情境就會非常的少,第二以及第三的問題則是屬於重構 code 的問題,目前還不影響 modal 的使用情境。

那這些問題該怎麼解決呢?我們等到下一篇再說吧。