大家好,我是编程小6,很高兴遇见你,有问题可以及时留言哦。
这里主要记录这段时间开发小程序过程中遇到的坑和要注意的点。主要是希望在以后开发小程序的过程中能在评审需求的时候就发现哪些能实现,哪些实现起来比较困难。这样就能在定设计和交互之前尽可能的减少后期踩坑的风险。
微信自带的下拉刷新需要在配置中开启:
app.json
中的 window
或者单个页面的json
文件属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
backgroundTextStyle | string | dark | 下拉 loading 的样式,仅支持 dark / light |
enablePullDownRefresh | boolean | false | true 开启 false关闭 |
onPullDownRefresh
方法监听下拉动作 onPullDownRefresh() {
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
}
这里有个要注意的问题:
在onPullDownRefresh
中不能调用wx.startPullDownRefresh()
,否则会死循环
自定义的下拉刷新,目前只支持整个页面的下拉刷新,对于局部的下拉刷新,就没有办法了:
局部的下拉刷新
这种我们可以使用自定义下拉刷新的方式来实现。详细的可以看我的另一篇博客,这里也就不赘述了。
navigationBar
小程序自带的navigationBar
仅可以在json
文件中配置。提供的可配置项也是少的可怜。主要如下:
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
navigationBarBackgroundColor | HexColor | #000000 | 导航栏标题文字内容 |
navigationBarTextStyle | string | white | 导航栏标题颜色,仅支持 black / white |
navigationBarTitleText | string | 导航栏标题文字内容 |
从上面的表格可以看出来,顶部导航栏样式固定,我们仅可以修改文字,字体颜色和背景色。
不过还好小程序还提供了自定义顶部导航的配置,window.navigationStyle
,这个配置项支持两个值default|custom
。默认是default
,表示的是用小程序自带的导航栏,配置为custom
时是自定义导航栏,其实就是小程序隐藏掉导航栏,然后我们自己实现。
但是自定义导航栏不仅带来了设计上的自由,也带来了很多坑。
不同于web
开发,小程序和app
对于下拉刷新的需求非常的多,也算是基本功能之一,小程序本身也自带下拉刷新。我们只需将window.enablePullDownRefresh
设置为true
,然后在页面监听onPullDownRefresh
即可。
但是当我们用到自定义navigationBar
的时候,会发现,本来fixed
定位在顶端的navigationBar
会被一起拉下来。
系统自带的navigationBar的下拉刷新
自定义navigationBar的下拉刷新
这个时候我们就必须要使用自定义的下拉刷新。关于自定义的下拉刷新的实现原理这里就不多说了。但是实现起来在真机中发现,android
手机会有明显的卡顿。
按照正常的navigationBar
,一般这个组件的层级是最高,仅次于遮罩层和弹窗这些组件。一般的组件可以使用z-index
属性来控制层级。
但是小程序中有种概念叫做:原生组件
这些组件包括camera
,canvas
,input
(仅在focus时表现为原生组件),map
,textarea
,video
,live-player
,live-pusher
这种组件脱离于WebView渲染流程之外,层级也是最高的,因此无论z-index
设置多大,都无法覆盖原生组件。
系统自带的navigationBar
自定义navigationBar
虽然理论上我们可以用cover-view
和cover-image
来实现自定义的navigationBar
,但是个人觉得还是尽量避免使用自定义的navigationBar
。
虽然
cover-view
和cover-image
组件可以覆盖在部分原生组件上面。对于原生组件之间,可以使用z-index
来控制他们的层级。但是小程序
cover-view
组件内部只支持嵌套cover-view
和cover-image
以及button
。这在很大程度上不支持我们作出多么有个性化的组件。而且实现起来坑也很多。而且关于用
z-index
来控制层级这点也存疑,虽然文档上这样说明,但是,我在开发中发现,实际上还是看渲染的顺序。后渲染的始终在先渲染的上层。
这个是针对textarea
组件在页面底部的时候,准确来说是textarea
组件距离底部的距离没有键盘高的时候。在键盘弹起时造成了整个页面上移,从而导致了导航栏会移到页面外。
当键盘未弹起时
当键盘弹起时
当然这个不算是硬伤,毕竟出现的条件有限,我们可以在设计上尽量避免将textarea
放到底部来避免这个坑。
tabbar
tabbar
在说自定义之前先看看小程序自带的tabbar
可以做到什么程度。
自定义的tabbar
是在app.json
中配置的,在tabBar
下:
属性 | 类型 | 描述 |
---|---|---|
color | HexColor | tab 上的文字默认颜色,仅支持十六进制颜色 |
selectedColor | HexColor | tab 上的文字选中时的颜色,仅支持十六进制颜色 |
backgroundColor | HexColor | tab 的背景色,仅支持十六进制颜色 |
borderStyle | string | tabbar上边框的颜色, 仅支持 black / white |
list | Array | tab 的列表,详见 list 属性说明,最少2个、最多5个 tab |
position | string | tabBar的位置,仅支持 bottom / top |
custom | boolean | 自定义 tabBar,见详情 |
list
选项配置属性 | 类型 | 描述 |
---|---|---|
pagePath | string | 页面路径,必须在 pages 中先定义 |
text | string | tab 上按钮文字 |
iconPath | string | 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,不支持网络图片。当 position 为 top 时,不显示 icon。 |
selectedIconPath | string | 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,不支持网络图片。当 position 为 top 时,不显示 icon。 |
所以从上面我们可以看到,我们可以定义tabbar
的选中和未选中图标和字体颜色。没办法加入别的样式和嵌入别的自定义点击事件。
这是比较老的版本的形式。在需要tabbar
的页面嵌入tabbar
组件。这是最简单的实现方式。但是在首次切换的时候,会有很明显的闪屏。
这个比第一种要好点,也是我在项目中用到的一种模式,但是切换的时候也有稍微的闪屏
tabbar
的形势这种实现方式稍微复杂,也就是将首页的几个页面作为组件传入,通过路由控制页面切换。
之所以叫伪tabbar
的形势,是因为这个只是表面上是tabbar
。
理论上这种方式实现的在切换的时候可以做到不闪屏。但是会不会带来别的问题呢?
比如说返回的时候会不会造成页面错乱?
原本的页面生命周期和组件的生命周期略有不同,会不会造成一些坑?
还有个几乎可以肯定的问题,就是如果不使用cover-view
的话,我们就没法盖住原生组件。
不过好在小程序组件和页面之间的切换很方便,特别是在用Taro
之后,组件和页面的区分仅仅只是是否在app.tsx
中注册。所以第二和第三种实现方式切换起来并不是很麻烦。但是目前看来的话第二种实现方式体验还算满意,因此也没有必要切换到第三种方式。
额,其实我说的这三个,几乎可以总结出一个问题,那就是小程序中让人吐血的层级问题。
其实不论是弹窗还是navigation
还是tabbar
他们都有一个特点,就是定位在页面的某一个位置,还有层级要足够高,要能够覆盖住底层元素。
官方没有专门的弹窗容器(我觉得应该有一个弹窗容器)因此只能靠我们自己写了。但是因为cover-view
令人蛋疼的样式支持度,个人觉得仅仅用cover-view
和cover-image
来实现一个定制化的弹窗几乎不可能。
如果不用cover-view
你会发现很多常用的组件都是骑在你脸上,而你毫无办法的。
因此个人建议,在有原生组件的页面上,尽量避免弹层的出现。
如果是在无法妥协,那也建议弹窗组件分两块来写,一种专门用cover-view
和cover-image
来写,并且一定要写z-index
来控制层级,理论上是后面的元素会覆盖在上一个元素上面,但是还是要防止有些组件在操作的过程中重新渲染,而改变原有的层级。而对于页面中没有原生组件的,可以用view
来写,这样样式上就自由很多。
关于富文本的渲染。现在基本的做法都是先把html
解析为节点信息,然后再通过模板渲染为wxml
。但是因为小程序模板不支持递归调用。所以在很多第三方组件中都出现以下的代码:
<!--temp0-->
<template>
...
<template is="temp1"></template>
</template>
<!--temp1-->
<template>
...
<template is="temp2"></template>
</template>
<!--temp2-->
<template>
...
<template is="temp3"></template>
</template>
...
通常这种代码会出现十几到二十几个,也就是说最多支持嵌套二十多层。如果不够的话就得自己加了。我就遇到过一个富文本,足足嵌套到了两百多层。我复制到一百的时候实在受不了了,写了一个模板生成器来完成。
网上有人说这种代码看起来蠢哭了。的确,但是也很无奈。
这个不能说是小程序的坑,应该说是ios和android对new Date()
处理上的差异。我们可以用safari
浏览器和chrome
来复现这两种差异
我们公司前后端交互用的时间格式是YYYY-MM-DDTHH:mm:ss
。这种格式的时间字符串用new Date()
来处理,在safari
和chrome
的表现如下:
new Date('2019-05-29T14:00:00')
// safari Wed May 29 2019 22:00:00 GMT+0800 (CST) = $2
// chrome Wed May 29 2019 14:00:00 GMT+0800 (中国标准时间)
safari
是比chrome
要早8小时的,这是因为chrome
认为这个时间是本地时间,而safari
认为是国际标准时间,所以会有这样的8小时差异(仅限于中国)。
因此在调用new Date()
之前我们需要把YYYY-MM-DDTHH:mm:ss
格式的转换为YYYY/MM/DD HH:mm:ss
这种格式的字符串。
此外,我在处理的过程中还发现带毫秒数的事件字符串2019-05-29T14:00:00.000
,这种的还需要将毫秒数去掉变成这种格式2019/05/29 14:00:00
。然后在ios
和android
上表现也就一致了。
function getDate(date: any) {
if(typeof date === 'string') {
return new Date(date.replace('T', ' ').replace(/\-/g, '/').split('.')[0])
}
return new Date(date)
}
小程序在android
下,字体的font-weight
必须要设置到700
及以上才会变粗,或者统一使用bold
ios
android
小程序大小是有限制的,目前是主包不超过2M。可是为了实现一些功能,导致我们很容易就超过了这个限制。
好在官方提供了分包方式。具体可以参照官方文档。
就一点:对于副包内引用的,较大的包,应该包含在分包的文件夹内部。不然仍然会打包在主包内部
px
和rpx
rpx
来做适配最近在做一个需求,觉得有个点还是需要注意的,特此记录一下。
这个需求就是一个简单的消息轮播。如下图:
就是红框区域的一个向下无限滚动轮播,时间间隔为2s
。我是使用translateY
来实现的。每次translateY
的高度和消息块的高度相同。
但是在滚动的过程中,发现在某些机型上面每次滚动都会有细微的偏移,而在某些机型上面正常。
消息显示窗口的高度和每个消息的高度都是80rpx
。代码如下:
<View className="notice-pannel" style={{transform: `translateY(${curIndex * -80}rpx)`, transition}}>
{
list.map(item => (
<View className="notice-item" key={item.id}>
<Image className="notice-avatar" src={item.actor.avatar_url}></Image>
<Text className="notice-desc">{`${item.actor.login} ${item.payload.action} ${item.repo.name} at ${new DateX(item.created_at).format()}`}</Text>
</View>
))
}
</View>
最后定位问题的原因是因为部分屏幕宽度在换算rpx
的时候会有误差,导致每次translateY
的时候会有一个小误差。
rpx
根据官方文档的定义是:rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。
因此,会有些屏幕宽度在rpx转px的时候会有除不尽的时候,这时候往往会有四舍五入取整的情况。
在看这行代码${curIndex * -80}rpx)
,假设我们的设备的屏幕宽度为412
这时候80rpx
专成px
的时候是43.946666666666665px
。开发者工具上会发现,实际上是换算成了43
,也就是每个消息块的高度是43px
。
当curIndex
为2
的时候,实际上我们应该偏移43 * 2
也就是86px
,可是如果是直接用rpx
的话,我们发现会是87px
。这时候就产生了偏移了。
知道原因的话解决方案也有了,就是用px
做单位就可以了
<View className="notice-pannel" style={{transform: `translateY(${curIndex * rpx2px(-80)}rpx)`, transition}}>
{
list.map(item => (
<View className="notice-item" key={item.id}>
<Image className="notice-avatar" src={item.actor.avatar_url}></Image>
<Text className="notice-desc">{`${item.actor.login} ${item.payload.action} ${item.repo.name} at ${new DateX(item.created_at).format()}`}</Text>
</View>
))
}
</View>
下一篇