已经用 uni-app+vue3+ts 开发了一段时间,记录一下日常遇见的问题和解决办法
uni-app 中的单端代码
uni-app 是支持多端,如果你想让你的代码,只在部分平台使用,那么就需要用的它的单端处理语法 //#ifdef
和 //#ifndef
等。
1. //#ifdef xxx
只在xxx平台生效
2. //#ifndef xxx
除了xxx平台,其他都生效
1 2 3
| menuButtonInfo = '只要不是微信,其他都可以'
|
安全边距
1. 异形屏
因为有异形手机屏的存在,最顶部有摄像头,最下面有导航条,为了避免界面内容出现在这些位置,所以每次在界面初始要设置安全边距。
1 2 3 4 5 6 7 8 9 10 11
| <script setup lang="ts">
const { safeAreaInsets } = uni.getSystemInfoSync() </script>
<template> <view class="specification-panel flex-column" :style="{ paddingTop: safeAreaInsets.top + 'px' }"> <view class="bottomNav" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }"></view> </view> </template>
|
2. 微信胶囊
由于微信小程序右上角有微信胶囊,很多时候我们为了保持界面整齐,需要获取微信胶囊的位置,来让我们得元素和它对齐。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export const safeTop = () => { const { safeAreaInsets } = uni.getWindowInfo() let menuButtonInfo = { top: 0 } menuButtonInfo = uni.getMenuButtonBoundingClientRect()
const top = menuButtonInfo.top || safeAreaInsets?.top return { top } }
|
全局组件
全局组件
目前只能在 src/pages.json
里配置,代码如下:
1 2 3 4 5 6 7 8 9 10 11
| "easycom": { "autoscan": true, "custom": { "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue", "^Weiz(.*)": "@/components/Weiz$1/index.vue" } }
|
使用的时候,直接在界面使用即可,无需再导入。
1
| <WeizCarousel class="categories-banner" size="small" />
|
获取DOM信息
有的时候我们需要去拿到界面元素的相关信息,然后进行一些处理,uni-app 提供了相关API,但需要和 vue3 配合使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script setup lang="ts"> import { getCurrentInstance } from 'vue' const instance = getCurrentInstance()
const getNodeInfo = () => { const query = uni.createSelectorQuery().in(instance) query .select('.similar') .boundingClientRect((data) => { const nodeInfo: UniApp.NodeInfo = data as UniApp.NodeInfo console.log(nodeInfo) }) .exec() } </script>
|
是的你没看错,不需要给元素设置 ref
url 传参
url 跳转界面有两种方式,一种是使用 navigator
标签,一种是使用 uni.navigateTo
方法。
需要注意的是url有长度限制,太长的字符串会传递失败,而且参数中出现空格等特殊字符时需要对参数进行编码,如使用 encodeURIComponent
等。
1. 传递参数
1 2 3
| uni.navigateTo({ url: 'pages/test?id=1&name=uniapp' });
|
或者
1 2 3 4 5 6
| <script setup lang="ts"> const item = ref({ id: 1, name: 'uniapp' }) </script> <template> <navigator :url="'/pages/test/test?item='+ encodeURIComponent(JSON.stringify(item))"></navigator> </template>
|
2. 接受参数
在 pages/test
界面
1 2 3 4 5
| onLoad: function(option) { console.log(option.id, option.name) const item = JSON.parse(decodeURIComponent(option.item)); }
|
父子组件通信
简单参数
子组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script setup lang="ts"> defineProps<{ title: string subTitle?: string }>() </script>
<template> <view class="weiz-title"> <view class="title">{{ title }}</view> <view class="sub-title">{{ subTitle }}</view> </view> </template>
|
父组件:
1
| // 由于是全局组件,所以无需再引入,如果不是全局组件,需要单独引入 <WeizTitle title="详情" />
|
复杂参数
如果参数比较复杂,可以直接用 TS 去定义类型,下面举例:
子组件:
1 2 3 4 5 6 7 8 9
| <script setup lang="ts"> import type { CategoryItem } from '@/types/api'
defineProps<{ list: CategoryItem[] }>() </script>
|
父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <script setup lang="ts"> import { ref } from 'vue' import { onLoad } from '@dcloudio/uni-app' import type { CategoryItem } from '@/types/api' import { getCategoryIndexAPI } from '@/api/category'
const categoryList = ref<CategoryItem[]>([]) const getCategoryList = async () => { const res = await getCategoryIndexAPI() categoryList.value = res.result } onLoad(() => { getCategoryList() }) </script>
<template> <WeizCategory :list="categoryList" @refresh="getCategoryList" /> </template>
|
父调子方法
父调子需要子组件通过 defineExpose
暴露方法给父组件,然后父组件拿到子组件实例再去调用子组件方法。
1. 子组件暴露方法
1 2 3 4 5 6 7 8
| const getCurrentSpec = () => { return currentSpec.value }
defineExpose({ getCurrentSpec })
|
2. 父组件拿到实例调用
可参考章节 TS 相关 - 定义组件实例类型
,调用子组件需要先拿到子组件的实例,拿到实例后直接调用即可。
1 2 3 4
| const weizCategory = ref<WeizCategoryInstance>()
weizCategory.value.getCurrentSpec()
|
子调父方法
子调父方法,需要父组件去给子组件添加自定义事件,然后子组件通过 defineEmits
去触发。
1. 父组件声明自定义事件
1 2 3 4 5 6 7 8 9
| <script setup lang="ts"> const handleUpdate = (value: string) => { console.log('拿到子组件的传值,并且调用了父组件', value) } </script>
<template> <WeizCategory :list="categoryList" @update="handleUpdate" /> </template>
|
2. 子组件使用 defineEmits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script setup lang="ts"> import { ref, defineEmits } from 'vue'
const message = ref('子组件的值') const popupEmit = defineEmits(['update'])
function sendMessage() { popupEmit('update', message.value) } </script>
<template> <div> <button @click="sendMessage">触发父组件方法</button> </div> </template>
|
TS 相关
定义组件实例类型
定义组件实例类型文件 xxx.d.ts
1 2 3 4 5 6 7 8 9 10
| import WeizCardList from '@/components/WeizCardList/index.vue'
declare module 'vue' { export interface GlobalComponents { WeizCardList: typeof WeizCardList } }
export type CardListInstance = InstanceType<typeof WeizCardList>
|
在 vue 页面里使用:
1 2 3 4 5 6
| import type { CardListInstance } from '@/types/components'
const cardList = ref<CardListInstance>()
cardList.value?.resetData()
|