做一个正直的人 發表於 2024-2-1 21:45:00

uni-app+vue3会遇到哪些问题

<blockquote>
<p>已经用 uni-app+vue3+ts 开发了一段时间,记录一下日常遇见的问题和解决办法</p>
</blockquote>
<h2 id="uni-app-中的单端代码">uni-app 中的单端代码</h2>
<p>uni-app 是支持多端,如果你想让你的代码,只在部分平台使用,那么就需要用的它的单端处理语法 <code>//#ifdef</code> 和 <code>//#ifndef</code> 等。</p>
<h3 id="1-ifdef-xxx-只在xxx平台生效">1. <code>//#ifdef xxx</code> 只在xxx平台生效</h3>
<pre><code class="language-ts">//#ifdef MP-WEIXIN
menuButtonInfo = '微信'
//#endif
</code></pre>
<h3 id="2-ifndef-xxx-除了xxx平台其他都生效">2. <code>//#ifndef xxx</code> 除了xxx平台,其他都生效</h3>
<pre><code class="language-ts">//#ifndef MP-WEIXIN
menuButtonInfo = '只要不是微信,其他都可以'
//#endif
</code></pre>
<h2 id="安全边距">安全边距</h2>
<h3 id="1-异形屏">1. 异形屏</h3>
<p>因为有异形手机屏的存在,最顶部有摄像头,最下面有导航条,为了避免界面内容出现在这些位置,所以每次在界面初始要设置安全边距。</p>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
&lt;/script&gt;

&lt;template&gt;
&lt;view class="specification-panel flex-column" :style="{ paddingTop: safeAreaInsets.top + 'px' }"&gt;
    &lt;!-- 底部导航 --&gt;
    &lt;view class="bottomNav" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }"&gt;&lt;/view&gt;
&lt;/view&gt;
&lt;/template&gt;
</code></pre>
<h3 id="2-微信胶囊">2. 微信胶囊</h3>
<p>由于微信小程序右上角有微信胶囊,很多时候我们为了保持界面整齐,需要获取微信胶囊的位置,来让我们得元素和它对齐。</p>
<pre><code class="language-ts">// 微信胶囊一定处于安全位置,所以有微信胶囊就拿胶囊的位置,否则再去获取安全边距
export const safeTop = () =&gt; {
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
}
}
</code></pre>
<h2 id="全局组件">全局组件</h2>
<p><code>全局组件</code> 目前只能在 <code>src/pages.json</code> 里配置,代码如下:</p>
<pre><code class="language-json">// 组件自动导入
"easycom": {
// 开启自动扫描
"autoscan": true,
"custom": {
    // 使用了uni-ui 规则如下配置
    "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
    // 自定义组件,需要使用正则表达式
    "^Weiz(.*)": "@/components/Weiz$1/index.vue"
}
}
</code></pre>
<p>使用的时候,直接在界面使用即可,无需再导入。</p>
<pre><code class="language-html">&lt;WeizCarousel class="categories-banner" size="small" /&gt;
</code></pre>
<h2 id="获取dom信息">获取DOM信息</h2>
<p>有的时候我们需要去拿到界面元素的相关信息,然后进行一些处理,uni-app 提供了相关API,但需要和 vue3 配合使用</p>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()

const getNodeInfo = () =&gt; {
const query = uni.createSelectorQuery().in(instance)
query
    .select('.similar') // 获取界面元素,也可以传id
    .boundingClientRect((data) =&gt; {
      const nodeInfo: UniApp.NodeInfo = data as UniApp.NodeInfo
      console.log(nodeInfo)
    })
    .exec()
}
&lt;/script&gt;
</code></pre>
<p><strong>是的你没看错,不需要给元素设置 <code>ref</code></strong></p>
<h2 id="url-传参">url 传参</h2>
<p>url 跳转界面有两种方式,一种是使用 <code>navigator</code> 标签,一种是使用 <code>uni.navigateTo</code> 方法。<br>
需要注意的是url有长度限制,太长的字符串会传递失败,而且参数中出现空格等特殊字符时需要对参数进行编码,如使用 <code>encodeURIComponent</code> 等。</p>
<h3 id="1-传递参数">1. 传递参数</h3>
<pre><code class="language-ts">uni.navigateTo({
        url: 'pages/test?id=1&amp;name=uniapp'
});
</code></pre>
<p>或者</p>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
const item = ref({ id: 1, name: 'uniapp' })
&lt;/script&gt;
&lt;template&gt;
&lt;navigator :url="'/pages/test/test?item='+ encodeURIComponent(JSON.stringify(item))"&gt;&lt;/navigator&gt;
&lt;/template&gt;
</code></pre>
<h3 id="2-接受参数">2. 接受参数</h3>
<p>在 <code>pages/test</code> 界面</p>
<pre><code class="language-ts">onLoad: function(option) {
console.log(option.id, option.name)
// 如果传递的是经过编码的参数
const item = JSON.parse(decodeURIComponent(option.item));
}
</code></pre>
<h2 id="父子组件通信">父子组件通信</h2>
<h3 id="简单参数">简单参数</h3>
<p>子组件:</p>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
// 使用 defineProps 来接受参数,非必要参数使用 xxx? 的形式
defineProps&lt;{
    title: string
    subTitle?: string
}&gt;()
&lt;/script&gt;

&lt;template&gt;
&lt;view class="weiz-title"&gt;
    &lt;view class="title"&gt;{{ title }}&lt;/view&gt;
    &lt;view class="sub-title"&gt;{{ subTitle }}&lt;/view&gt;
&lt;/view&gt;
&lt;/template&gt;
</code></pre>
<p>父组件:</p>
<pre><code class="language-html">// 由于是全局组件,所以无需再引入,如果不是全局组件,需要单独引入 &lt;WeizTitle title="详情" /&gt;
</code></pre>
<h3 id="复杂参数">复杂参数</h3>
<p>如果参数比较复杂,可以直接用 TS 去定义类型,下面举例:</p>
<p>子组件:</p>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
// 引入参数类型
import type { CategoryItem } from '@/types/api'

// 定义 props 接收数据
defineProps&lt;{
    list: CategoryItem[]
}&gt;()
&lt;/script&gt;
</code></pre>
<p>父组件:</p>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
// 引入数据类型
import type { CategoryItem } from '@/types/api'
// 引入接口
import { getCategoryIndexAPI } from '@/api/category'

// 定义响应式数据
const categoryList = ref&lt;CategoryItem[]&gt;([])
// 获取数据方法
const getCategoryList = async () =&gt; {
    const res = await getCategoryIndexAPI()
    // 拿到数据赋值
    categoryList.value = res.result
}
// 调用方法
onLoad(() =&gt; {
    getCategoryList()
})
&lt;/script&gt;

&lt;template&gt;
&lt;WeizCategory :list="categoryList" @refresh="getCategoryList" /&gt;
&lt;/template&gt;
</code></pre>
<h3 id="父调子方法">父调子方法</h3>
<p>父调子需要子组件通过 <code>defineExpose</code> 暴露方法给父组件,然后父组件拿到子组件实例再去调用子组件方法。</p>
<h4 id="1-子组件暴露方法">1. 子组件暴露方法</h4>
<pre><code class="language-ts">// 定义方法
const getCurrentSpec = () =&gt; {
return currentSpec.value
}
// 暴露方法给父组件
defineExpose({
getCurrentSpec
})
</code></pre>
<h4 id="2-父组件拿到实例调用">2. 父组件拿到实例调用</h4>
<p>可参考章节 <code>TS 相关 - 定义组件实例类型</code>,调用子组件需要先拿到子组件的实例,拿到实例后直接调用即可。</p>
<pre><code class="language-ts">// 拿到子组件实例 WeizCategoryInstance 需要我们去 ts 里定义
const weizCategory = ref&lt;WeizCategoryInstance&gt;()
// 调用子组件实例的方法
weizCategory.value.getCurrentSpec()
</code></pre>
<h3 id="子调父方法">子调父方法</h3>
<p>子调父方法,需要父组件去给子组件添加自定义事件,然后子组件通过 <code>defineEmits</code> 去触发。</p>
<h4 id="1-父组件声明自定义事件">1. 父组件声明自定义事件</h4>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
const handleUpdate = (value: string) =&gt; {
    console.log('拿到子组件的传值,并且调用了父组件', value)
}
&lt;/script&gt;

&lt;template&gt;
&lt;WeizCategory :list="categoryList" @update="handleUpdate" /&gt;
&lt;/template&gt;
</code></pre>
<h4 id="2-子组件使用-defineemits">2. 子组件使用 <code>defineEmits</code></h4>
<pre><code class="language-html">&lt;script setup lang="ts"&gt;
import { ref, defineEmits } from 'vue'

const message = ref('子组件的值')
const popupEmit = defineEmits(['update'])

function sendMessage() {
    popupEmit('update', message.value)
}
&lt;/script&gt;

&lt;template&gt;
&lt;div&gt;
    &lt;button @click="sendMessage"&gt;触发父组件方法&lt;/button&gt;
&lt;/div&gt;
&lt;/template&gt;
</code></pre>
<h2 id="ts-相关">TS 相关</h2>
<h3 id="定义组件实例类型">定义组件实例类型</h3>
<p>定义组件实例类型文件 <code>xxx.d.ts</code></p>
<pre><code class="language-ts">// 导入组件
import WeizCardList from '@/components/WeizCardList/index.vue'
// 什么全局类型
declare module 'vue' {
export interface GlobalComponents {
    WeizCardList: typeof WeizCardList
}
}
// 导出组件实例类型, 需要用到 InstanceType
export type CardListInstance = InstanceType&lt;typeof WeizCardList&gt;
</code></pre>
<p>在 vue 页面里使用:</p>
<pre><code class="language-ts">// 导入组件实例类型
import type { CardListInstance } from '@/types/components'
// 拿到组件实例
const cardList = ref&lt;CardListInstance&gt;()
// 调用组件实例的方法
cardList.value?.resetData()
</code></pre>


</div>
<div id="MySignature" role="contentinfo">
    <div id="copyrightWeizwz">
    <p><span class="post-copyright-meta">文章作者:</span><span class="post-copyright-info">唯知为之</span></p>
    <p><span class="post-copyright-meta">文章出处:</span><span class="post-copyright-info">https://www.cnblogs.com/weizwz/p/18002189</span></p>
    <p><span class="post-copyright-meta">版权声明:</span><span class="post-copyright-info">本博客所有文章除特别声明外,均采用 「CC BY-NC-SA 4.0 DEED」 国际许可协议,转载请注明出处!</span></p>
    <p>内容粗浅,如有错误,欢迎大佬批评指正</p>
</div><br><br>
来源:https://www.cnblogs.com/weizwz/p/18002189
頁: [1]
查看完整版本: uni-app+vue3会遇到哪些问题