uniapp开发问题汇总

汇总 uniapp 开发过程中遇到的一些问题与对应解决的解决方案。

运行小程序 props 传值,对象方法丢失

自定义组件父子组件 prop 传递的变量为对象时,对象内部含有函数属性,该函数属性会直接被删除。

因为为 uniapp 在传递数据的时候使用的是JSON.parse(JSON.stringify(obj1))这样传递的,无法传递函数。

了解更多,详见

image 组件@load、@error 事件名不能是 onLoad、onError

如果是 onLoad、onError,编译会报错。

小程序无法通过 document 获取 DOM 节点信息

如果有获取元素距离顶部的间距这样一个场景,小程序无法通过 document 来获取相关信息,需要用到uni.createSelectorQuery()。用法以下几点需要注意:

  • 使用 uni.createSelectorQuery() 需要指定的 DOM 节点已渲染完成后。
  • 支付宝小程序不支持 in(component),使用无效果

提取公共的方法,兼容实现方案以下几种:

mixin

// common.mixin.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
getElemRect(selector, all) {
return new Promise(resolve => {
let query = uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this);
// #endif
query[all ? 'selectAll' : 'select'](selector)
.boundingClientRect(rect => {
if (all & Array.isArray(rect) && rect.length) {
resolve(rect);
}
if(!all && rect) {
resolve(rect);
}
})
.exec();
})
}

utils

// utils.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export function getElemRect(selector, all) {
return new Promise((resolve) => {
let query = uni
.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this);
// #endif
query[all ? 'selectAll' : 'select'](selector)
.boundingClientRect((rect) => {
if (all & Array.isArray(rect) && rect.length) {
resolve(rect);
}
if (!all && rect) {
resolve(rect);
}
})
.exec();
});
}

// component.vue

1
2
3
4
5
6
7
8
9
<script>
import { getElemRect } from 'utils.js';

export default {
methods: {
getElemRect
}
};
</script>

页面路由跳转

使用 uni.navigateTo(OBJECT)时,url 可以是相对路径,也可以是绝对路径,注意文件目录层级关系。

swiper 组件@change 从第 2 个开始执行的

不推荐在 change 事件中触发业务场景(如轮播卡片曝光事件),会漏掉第一个。

小程序中子组件 props 值来自父组件 computed 定义的值控制台报 warn 场景

如果父组件 computed 定义的值不具备动态绑定更新特征,而是自变量赋值,子组件 props 引用,小程序控制台可能就会打印警告日志。

具体代码分析

// 父组件

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template>
<view class="Demo">
<image class="img" :src="imgUrl" mode="scaleToFill" @load="onLoadImg" @error="onErrorImg"></image>
<Card :margins="margins" />
</view>
</template>

<script>
import Card from './Card.vue';

export default {
components: { Card },
name: 'Demo',
data() {
return {
imgUrl: ''
};
},
computed: {
margins() {
let result = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
if (this.imgUrl) {
result.bottom = -50;
}
return result;
}
},
methods: {
onErrorImg() {
this.imgUrl = 'https://img.zcool.cn/community/015153563ec9e66ac7259e0fc2d030.jpg@3000w_1l_0o_100sh.jpg';
}
}
};
</script>

<style lang="scss" scoped>
.img {
display: block;
width: 100%;
height: 200rpx;
}
</style>

// 子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<view class="Card">
<view>{{ margins }}</view>
</view>
</template>

<script>
export default {
name: 'Card',
props: {
margins: {
type: Object,
default: () => ({})
}
}
};
</script>

微信小程序控制台日志:

1
[Component] property "margins" of "pagesA/demo/Card" received type-uncompatible value: expected <Object> but got non-object value. Used null instead.

以下方案可以解决

子组件 props 类型兼容(不推荐,可能会存在类型漏掉的情况)

1
2
3
4
5
6
7
8
9
10
11
<script>
export default {
name: 'Card',
props: {
margins: {
type: [Object, null],
default: () => ({})
}
}
};
</script>

父组件 computed 中不具备动态绑定更新特征的值,换成 data

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<template>
<view class="Demo">
<image class="img" :src="imgUrl" mode="scaleToFill" @load="onLoadImg" @error="onErrorImg"></image>
<Card :margins="margins" />
</view>
</template>

<script>
import Card from './Card.vue';

export default {
components: { Card },
name: 'Demo',
data() {
return {
imgUrl: '',
margins: {}
};
},
methods: {
onErrorImg() {
this.imgUrl = 'https://img.zcool.cn/community/015153563ec9e66ac7259e0fc2d030.jpg@3000w_1l_0o_100sh.jpg';
},
getMargins() {
let result = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
if (this.imgUrl) {
result.bottom = -50;
}
this.margins = result;
}
}
};
</script>

<style lang="scss" scoped>
.img {
display: block;
width: 100%;
height: 200rpx;
}
</style>

非页面组件直接用页面生命周期钩子函数无效

非页面组件不具备页面组件的生命周期钩子函数,但是可以同组件通信事件绑定hook:页面生命周期钩子函数名实现

// 子组件

1
2
3
4
5
6
7
8
9
10
<script>
export default {
name: 'Card',
mounted() {
this.$root.$emit('hook:onHide', () => {
// 执行业务场景函数
});
}
};
</script>

打印 this,控制台 this 日志中,vue2 当前是支持 hook 调用页面生命周期钩子函数的。


通过点击 icon 图标使 input 组件聚焦

点击 icon 图标,让 input 组件聚焦,只需要点击图标时,改变 input 组件框的 focus 属性值。

但是,发现一个问题:这个方法只能触发一次。是因为当点击图标以后,focus 的值已经变成 true 了,所以当我们再次点击图标的时候,就不会出现效果了。

解决办法:获取软键盘的位置,如果软键盘的位置变为 0 ,就让 focus 的值变为 false,成功解决问题。

下面是具体的代码:

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
26
27
28
29
30
31
32
33
34
35
36
<template>
<view class="y-input u-flex">
<input class="input" type="text" :focus="focus" @blur="onInputBlur" />
<u-icon name="edit-pen-fill" color="#2979ff" size="60" @click="onEdit"></u-icon>
</view>
</template>

<script>
export default {
name: 'y-input',
data() {
return {
focus: false
};
},
methods: {
onEdit() {
this.focus = true;
uni.onKeyboardHeightChange((res) => {
if (res.height === 0) this.focus = false;
});
},
onInputBlur() {
this.focus = false;
}
}
};
</script>

<style lang="scss" scoped>
.input {
height: 60rpx;
border: 4rpx solid #e5e5e5;
border-radius: 16rpx;
}
</style>

uniapp debugger H5

JavaScript或typescript脚本中单行注入debugger关键词。本地运行,如yarn serve

谷歌浏览器访问控制台打印出的域名,打开调试工具,会触发谷歌自带的debugger调试功能。

如果遇到不能触发debugger调试,需要检查框架忽略列表项,取消或自定义一些规则来触发debugger调试。

image-20240703224606640