一畏妖、分析一波??????
-
popup
是獨立的組件,放到body下面 -
popup
含兩部分內(nèi)容铸本,一部分為背景蒙版枕赵,一部分為內(nèi)容的包裹容器 -
popup
通過雙向綁定的值進行控制展示和隱藏 -
popup
展示時渴析,頁面滾動應(yīng)該被鎖定 - 內(nèi)容區(qū)域應(yīng)該接收所有的
attrs
,并且應(yīng)該通過插槽讓調(diào)用方指定其內(nèi)容
二吮龄、構(gòu)建?????????
1. 新建 src/libs/popup/index.vue
文件
小知識點:v-model雙向數(shù)據(jù)綁定俭茧,底層機制
defineModel
是一個便利宏。 編譯器將其展開為以下內(nèi)容:
- 一個名為
modelValue
的 prop漓帚,本地 ref 的值與其同步母债; - 一個名為
update:modelValue
的事件,當(dāng)本地 ref 的值發(fā)生變更時觸發(fā)
<template>
<div>
<!-- teleport -->
<teleport to="body">
<!-- 蒙版 -->
<transition name="fade">
<div
v-if="modelValue"
class="w-screen h-screen bg-zinc-900/80 z-40 fixed top-0 left-0"
@click="emits('update:modelValue', false)"
></div>
</transition>
<!-- 內(nèi)容 -->
<transition name="popup-down-up">
<div
v-if="modelValue"
v-bind="$attrs"
class="w-screen bg-white z-50 fixed bottom-0"
>
<slot />
</div>
</transition>
</teleport>
</div>
</template>
<script setup>
import { useScrollLock } from '@vueuse/core'
import { watch } from 'vue'
const props = defineProps({
modelValue: {
required: true,
type: Boolean
}
})
const emits = defineEmits(['update:modelValue'])
// ------ 滾動鎖定 ------
const isLocked = useScrollLock(document.body)
watch(
() => props.modelValue,
(val) => {
isLocked.value = val
},
{
immediate: true
}
)
</script>
<style lang="scss" scoped>
// fade 展示動畫
.fade-enter-active {
transition: all 0.3s;
}
.fade-leave-active {
transition: all 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
// popup-down-up 展示動畫
.popup-down-up-enter-active {
transition: all 0.3s;
}
.popup-down-up-leave-active {
transition: all 0.3s;
}
.popup-down-up-enter-from,
.popup-down-up-leave-to {
transform: translateY(100%);
}
</style>
優(yōu)化一下:
v-model
雙向數(shù)據(jù)綁定尝抖,由于不可以在子組件直接修改響應(yīng)式數(shù)據(jù)毡们,所以有一些不太方便,那應(yīng)該怎么優(yōu)化呢昧辽?可以使用vueuse
提供的 useVModel衙熔,通過 useVModel 獲取到響應(yīng)式數(shù)據(jù),當(dāng)響應(yīng)式數(shù)據(jù)的狀態(tài)變化時搅荞,自動觸發(fā) update:modelValue
<template>
<!-- 無需主動觸發(fā) 事件 -->
<div
v-if="isOpen"
class="w-screen h-screen bg-zinc-900/80 z-40 fixed top-0 left-0"
@click="isOpen = false"
></div>
<div
v-if="isOpen"
v-bind="$attrs"
class="w-screen bg-white z-50 fixed bottom-0"
>
<slot />
</div>
...
</template>
<script setup>
import { useVModel } from '@vueuse/core'
...
// 不需要主動觸發(fā)了
// const emits = defineEmits(['update:modelValue'])
// 通過 useVModel 獲取到響應(yīng)式數(shù)據(jù) isOpen
const isOpen = useVModel(props)
...
// 監(jiān)聽 isOpen 的變化即可
watch(
isOpen,
...
)
</script>
2. 小試牛刀 demo.vue
<template>
<m-popup v-model="isOpenPopup">
<div>測試m-popup</div>
</m-popup>
</template>
<script setup>
// 控制popup顯示/隱藏
const isOpenPopup = ref(true);
</script>