汇总 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() .in(this); 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() .in(this); 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 组件聚焦,只需要点击图标时,改变 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调试。