已經用 uni-app+vue3+ts 開發了一段時間,記錄一下日常遇見的問題和解決辦法 uni-app 中的單端代碼 uni-app 是支持多端,如果你想讓你的代碼,只在部分平臺使用,那麼就需要用的它的單端處理語法 //#ifdef 和 //#ifndef 等。 1. //#ifdef xxx 只 ...
已經用 uni-app+vue3+ts 開發了一段時間,記錄一下日常遇見的問題和解決辦法
uni-app 中的單端代碼
uni-app 是支持多端,如果你想讓你的代碼,只在部分平臺使用,那麼就需要用的它的單端處理語法 //#ifdef
和 //#ifndef
等。
1. //#ifdef xxx
只在xxx平臺生效
//#ifdef MP-WEIXIN
menuButtonInfo = '微信'
//#endif
2. //#ifndef xxx
除了xxx平臺,其他都生效
//#ifndef MP-WEIXIN
menuButtonInfo = '只要不是微信,其他都可以'
//#endif
安全邊距
1. 異形屏
因為有異形手機屏的存在,最頂部有攝像頭,最下麵有導航條,為了避免界面內容出現在這些位置,所以每次在界面初始要設置安全邊距。
<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. 微信膠囊
由於微信小程式右上角有微信膠囊,很多時候我們為了保持界面整齊,需要獲取微信膠囊的位置,來讓我們得元素和它對齊。
// 微信膠囊一定處於安全位置,所以有微信膠囊就拿膠囊的位置,否則再去獲取安全邊距
export const safeTop = () => {
const { safeAreaInsets } = uni.getWindowInfo()
// 獲取膠囊信息 https://uniapp.dcloud.net.cn/api/ui/menuButton.html#getmenubuttonboundingclientrect
let menuButtonInfo = { top: 0 }
//#ifdef MP-WEIXIN
menuButtonInfo = uni.getMenuButtonBoundingClientRect()
//#endif
const top = menuButtonInfo.top || safeAreaInsets?.top
return {
top
}
}
全局組件
全局組件
目前只能在 src/pages.json
里配置,代碼如下:
// 組件自動導入
"easycom": {
// 開啟自動掃描
"autoscan": true,
"custom": {
// 使用了uni-ui 規則如下配置
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
// 自定義組件,需要使用正則表達式
"^Weiz(.*)": "@/components/Weiz$1/index.vue"
}
}
使用的時候,直接在界面使用即可,無需再導入。
<WeizCarousel class="categories-banner" size="small" />
獲取DOM信息
有的時候我們需要去拿到界面元素的相關信息,然後進行一些處理,uni-app 提供了相關API,但需要和 vue3 配合使用
<script setup lang="ts">
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
const getNodeInfo = () => {
const query = uni.createSelectorQuery().in(instance)
query
.select('.similar') // 獲取界面元素,也可以傳id
.boundingClientRect((data) => {
const nodeInfo: UniApp.NodeInfo = data as UniApp.NodeInfo
console.log(nodeInfo)
})
.exec()
}
</script>
是的你沒看錯,不需要給元素設置 ref
url 傳參
url 跳轉界面有兩種方式,一種是使用 navigator
標簽,一種是使用 uni.navigateTo
方法。
需要註意的是url有長度限制,太長的字元串會傳遞失敗,而且參數中出現空格等特殊字元時需要對參數進行編碼,如使用 encodeURIComponent
等。
1. 傳遞參數
uni.navigateTo({
url: 'pages/test?id=1&name=uniapp'
});
或者
<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
界面
onLoad: function(option) {
console.log(option.id, option.name)
// 如果傳遞的是經過編碼的參數
const item = JSON.parse(decodeURIComponent(option.item));
}
父子組件通信
簡單參數
子組件:
<script setup lang="ts">
// 使用 defineProps 來接受參數,非必要參數使用 xxx? 的形式
defineProps<{
title: string
subTitle?: string
}>()
</script>
<template>
<view class="weiz-title">
<view class="title">{{ title }}</view>
<view class="sub-title">{{ subTitle }}</view>
</view>
</template>
父組件:
// 由於是全局組件,所以無需再引入,如果不是全局組件,需要單獨引入 <WeizTitle title="詳情" />
複雜參數
如果參數比較複雜,可以直接用 TS 去定義類型,下麵舉例:
子組件:
<script setup lang="ts">
// 引入參數類型
import type { CategoryItem } from '@/types/api'
// 定義 props 接收數據
defineProps<{
list: CategoryItem[]
}>()
</script>
父組件:
<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. 子組件暴露方法
// 定義方法
const getCurrentSpec = () => {
return currentSpec.value
}
// 暴露方法給父組件
defineExpose({
getCurrentSpec
})
2. 父組件拿到實例調用
可參考章節 TS 相關 - 定義組件實例類型
,調用子組件需要先拿到子組件的實例,拿到實例後直接調用即可。
// 拿到子組件實例 WeizCategoryInstance 需要我們去 ts 里定義
const weizCategory = ref<WeizCategoryInstance>()
// 調用子組件實例的方法
weizCategory.value.getCurrentSpec()
子調父方法
子調父方法,需要父組件去給子組件添加自定義事件,然後子組件通過 defineEmits
去觸發。
1. 父組件聲明自定義事件
<script setup lang="ts">
const handleUpdate = (value: string) => {
console.log('拿到子組件的傳值,並且調用了父組件', value)
}
</script>
<template>
<WeizCategory :list="categoryList" @update="handleUpdate" />
</template>
2. 子組件使用 defineEmits
<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
// 導入組件
import WeizCardList from '@/components/WeizCardList/index.vue'
// 什麼全局類型
declare module 'vue' {
export interface GlobalComponents {
WeizCardList: typeof WeizCardList
}
}
// 導出組件實例類型, 需要用到 InstanceType
export type CardListInstance = InstanceType<typeof WeizCardList>
在 vue 頁面里使用:
// 導入組件實例類型
import type { CardListInstance } from '@/types/components'
// 拿到組件實例
const cardList = ref<CardListInstance>()
// 調用組件實例的方法
cardList.value?.resetData()
https://www.cnblogs.com/weizwz/p/18002189
本博客所有文章除特別聲明外,均採用 「CC BY-NC-SA 4.0 DEED」 國際許可協議,轉載請註明出處!
內容粗淺,如有錯誤,歡迎大佬批評指正