一、父组件使用ref模板调用子组件方法的问题

1. 问题表象

父组件使用ref调用子组件的方法时,在dev模式正常运行,无任何报错。在build后生产环境运行时,提示子组件方法找不到。 ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2308212/1634603273292-9f3fb6b1-9855-4283-ac3a-dee75df8de6b.png#clientId=uce65eacf-388f-4&from=paste&height=545&id=u663738a8&margin=[object Object]&name=image.png&originHeight=545&originWidth=1296&originalType=binary&ratio=1&size=231548&status=done&style=none&taskId=u57fca917-db03-4aad-8ace-81dadfa8882&width=1296)

2. 场景复现

  • 父组件代码
<template>
……
</template>

<script setup>
import Tabbar from "@/components/common/Tabbar.vue";
import LoginPopup from "@/components/common/LoginPopup.vue";
import {computed, onMounted, reactive, ref} from "vue";


// 引入公共方法
let {refLoginPopup} = global()

// 引入系统与设置模块
let {……} = setting(……)

// 公共方法
function global() {
  ……
  // 提示登录组件对象
  const refLoginPopup = ref(null)
  ……
  return {
    refLoginPopup
  }
}

function setting(……) {
  ……
  return {……}
}
</script>
<style scoped lang="scss">
……
</style>
  • 子组件代码
<template>
  <van-popup v-model:show="popupIsShow">
    <section>
      <div class="tip-img">
        <img :src="'/src/assets/images/login-popup.png'" alt=""/>
      </div>
      <div class="tip-content">
        <h3>您还没有登录</h3>
        <p>请先登录或注册再进行此操作</p>
      </div>
      <div class="tip-btn">
        <van-button color="linear-gradient(to right, #ffb061, #ff795d)" round
                    @click="toLogin">立即登录
        </van-button>
        <p @click="toRegister">还未注册?</p>
      </div>
    </section>
  </van-popup>
</template>

<script setup>
import {Popup, Button} from 'vant';
import {ref} from "vue";
import {useRouter} from "vue-router";


const router = useRouter()
// 默认不显示登录提示弹窗
const popupIsShow = ref(false);
// 显示登录提示弹窗
const showPopup = () => {
  popupIsShow.value = true;
};
……
defineExpose({
  showPopup, popupIsShow
})
</script>

<style lang="scss" scoped>
……
</style>

3. 问题分析

由于该页面功能模块较多,为便于开发,按功能模块拆分到了不同的函数中,但定义ref模板时,也将其放在单独的函数中,导致vite打包时不能识别正常打包。

4. 解决办法

父组件定义ref模板时,一定要放在<script setup>标签内,不要通过function定义函数然后return返回的方式定义。

# 父组件
<template>
……
</template>

<script setup>
import Tabbar from "@/components/common/Tabbar.vue";
import LoginPopup from "@/components/common/LoginPopup.vue";
import {computed, onMounted, reactive, ref} from "vue";

// 提示登录组件对象
  const refLoginPopup = ref(null)
// 引入公共方法
let {……} = global()

// 引入系统与设置模块
let {……} = setting(……)

// 公共方法
function global() {
  ……
  return {
    ……
  }
}

function setting(……) {
  ……
  return {……}
}
</script>
<style scoped lang="scss">
……
</style>

二、子组件使用父组件传参时双向绑定的问题

1. 问题表象

子组件接收并使用父组件参数时,在dev模式正常运行,无任何报错。在build后生产环境运行时,提示子组件方法找不到。 ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2308212/1634607776736-afb2fb4a-2406-46c4-8757-5c7c4fda50e1.png#clientId=ue6a0c369-c05c-4&from=paste&height=458&id=u88352662&margin=[object Object]&name=image.png&originHeight=458&originWidth=1275&originalType=binary&ratio=1&size=234989&status=done&style=none&taskId=u0f3005c5-4784-403b-800e-af1a0763168&width=1275)

2. 场景复现

  • 父组件
<template>
  <div v-title="title">
……
    <Tabbar :activeBar="0"></Tabbar>
  </div>
</template>

<script setup>
import Tabbar from '@/components/common/Tabbar.vue'
import {onMounted, reactive, ref} from "vue";
……

</script>
<style lang="scss">
……
</style>

  • 子组件
<template>
  <van-tabbar v-model="props.activeBar" placeholder safe-area-inset-bottom>
    ……
  </van-tabbar>
</template>

<script setup>
import {ref} from 'vue';

const props = defineProps({
  // 当前激活的导航栏id
  activeBar: {
    type: Number,
    required: false,
    default: 0
  }
})
</script>

<style lang="scss" scoped>
</style>

3. 问题分析

父组件向子组件传值时,通常不需要做双向绑定,因为是只读属性,如果需要更改值,子组件使用emit向父组件传递事件。但由于项目中使用vant,当前激活的item必须使用双向绑定。参考文档https://vant-contrib.gitee.io/vant/v3/#/zh-CN/tabbar。因此会出现上述问题

4. 解决办法

子组件模板中使用父组件参数双向绑定时(实际开发过程中不推荐这样做,但vant的tabbar组件只能双向绑定)。要使用props.XXX

# 子组件
<template>
  <van-tabbar v-model="props.activeBar" placeholder safe-area-inset-bottom>
…………
  </van-tabbar>
</template>

<script setup>
import {ref} from 'vue';

const props = defineProps({
  // 当前激活的导航栏id
  activeBar: {
    type: Number,
    required: false,
    default: 0
  }
})

const active = ref(0)
</script>

<style lang="scss" scoped>
</style>

三、vite项目引用本地图片的问题

1. 问题表象

template和script setup使用本地图片资源时,使用文件路径,dev模式正常,build后找不到资源文件。 ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2308212/1634605439670-0f6d4c48-88eb-4191-89ba-8a59e10bda5f.png#clientId=ue6a0c369-c05c-4&from=paste&height=556&id=u4a5c53d2&margin=[object Object]&name=image.png&originHeight=556&originWidth=1311&originalType=binary&ratio=1&size=187000&status=done&style=none&taskId=u4f562e5f-9757-4880-9e04-57b6cd462f5&width=1311)

2. 问题分析

详细原因可参考官方vite文档https://cn.vitejs.dev/guide/assets.html,主要原因是vite打包时,src属性解析成字符串,而不是路径,类似动态地址,所以无法被解析打包。

3. 解决办法

  • template使用图片资源时,直接采用标签来引入图片
<template>
<div class="tip-img">
  <img src="/src/assets/images/login-popup.png" alt=""/>
</div>
</template>
  • script setup使用图片资源时,利用import获取打包文件的动态地址
<script setup>
import QzoneImg from '@/assets/images/qq-zone.png'
const options = [
    [
      {name: 'QQ空间', icon: QzoneImg},
      {name: '微博', icon: 'weibo'},
      {name: 'QQ', icon: 'qq'},
      {name: '微信', icon: 'wechat'},
      {name: '朋友圈', icon: 'wechat-moments'},
    ],
    [
      {name: '复制链接', icon: 'link'},
      {name: '二维码', icon: 'qrcode'}
    ],
]
</script>
  • 或者也可以在script setup中import引入,赋值给变量名,然后在template中使用变量名。