问题重现
在使用uniapp开发微信小程序时,甲方要求将toast改为弹窗展示,并增加按钮用于跳转到电话界面,跳转到电话界面的逻辑很好做,使用uniapp自带的如下命令或者找AI写能够轻松搞定:
javascript
const contact = () => {
uni.makePhoneCall({
phoneNumber: '123456789',
fail: (err) => {
console.error('拨打电话失败:', err);
},
});
}
但是toast改为弹窗展示的这一部分遇到了一些问题,最初我使用传统的vue3代码逻辑来写,先用props+emits的方法来解决,部分伪代码如下:
子组件
html
<template>
<su-popup :show="props?.show"@close="props.show = false">
...
</template>
<script setup>
import { reactive, computed, defineProps, defineEmits } from 'vue';
...
// 负责展示
const props = defineProps({
show: false,
});
const emits = defineEmits();
const closeModal = () => {
emits('close');
};
...
</script>
父组件
html
<template>
<s-lvl-modal :show="state?.show" @close="state?.show=false" />
</template>
<script setup>
const state= reactive({ show: false, })
// 触发弹窗的逻辑
state?.show=true
...
</script>
但是这种方式下H5模式下可以跳出弹窗,而微信小程序无法弹窗
问题探索
为了解决这个问题,我先求助于deepseek,它给出的代码是这样的
父组件
html
<!-- 父组件修改后 -->
<template>
<s-lvl-modal :show="state.show" @close="closeModal" />
<!-- 其他内容不变 -->
</template>
<script setup>
import { reactive } from 'vue';
import sLvlModal from './s-lvl-modal.vue'; // 确保路径正确
const state = reactive({
show: false
});
const closeModal = () => {
state.show = false;
}
// 其他逻辑不变
</script>
子组件
html
<!-- 子组件修改后 -->
<script setup>
import { ref, watch } from 'vue';
// props定义
const props = defineProps({
show: Boolean
});
// 创建响应式变量
const showProp = ref(props.show);
// 监听props变化
watch(() => props.show, (val) => {
showProp.value = val;
});
// emits定义不变
</script>
<template>
<su-popup
:show="showProp"
type="center"
round="10"
@close="closeModal"
:isMaskClick="false"
maskBackgroundColor="rgba(0, 0, 0, 0.7)"
>
<!-- 内容不变 -->
</su-popup>
</template>
结果报错如下:
ini
AppOnError: TypeError: Cannot read property 'openModal' of null
at toCommission (<anonymous>:47:26)
at callWithErrorHandling (vendor.js?t=wechat&s=1753756936479&v=edf8366d112d96098f5089ed6f3e1306:3616)
at callWithAsyncErrorHandling (vendor.js?t=wechat&s=1753756936479&v=edf8366d112d96098f5089ed6f3e1306:3623)
at t.invoke2 (vendor.js?t=wechat&s=1753756936479&v=edf8366d112d96098f5089ed6f3e1306:7035)
at f (WASubContext.js?t=wechat&s=1753756936479&v=3.8.9:1)
at WASubContext.js?t=wechat&s=1753756936479&v=3.8.9:1
at t.<anonymous> (WASubContext.js?t=wechat&s=1753756936479&v=3.8.9:1)
at Function.<anonymous> (WASubContext.js?t=wechat&s=1753756936479&v=3.8.9:1)
解决方法
最后迫不得已,使用expose的方法进行解决,去掉props和emits的传递,子组件伪代码如下:
html
<template>
<su-popup :show="state.show" @close="closeModal()">
...
</template>
<script setup>
import { reactive,defineExpose } from 'vue';
...
const state = reactive({
show: false,
...
});
const openModal = () => {
state.show = true;
}
const closeModal = () => {
state.show = false;
};
...
defineExpose({
openModal,
closeModal
});
</script>
父组件的伪代码如下:
html
<template>
<s-lvl-modal ref="lvlModalRef" />
</template>
<script setup>
import { ref } from 'vue';
import lvlModal from './s-lvl-modal.vue';
const lvlModalRef = ref(null);
// 触发弹窗的逻辑
lvlModalRef.value?.openModal();
...
</script>
之后再用微信开发者工具进行测试,解决