关于微信小程序的那些事
小程序安装后避免跑路的功能自发布以来一直很受欢迎,甚至到现在还很火爆。小程序虽然是一个HTML5,但是通过限制开发人员的编写方法,提供了一套自定义的组件和编写方法,使用客户端渲染,大大提高了网页的性能。
前段时间,我们也开发了小程序。因为是接管项目,所以这个项目之前没有使用框架,直接用原生小程序开发,在开发过程中发现很多不便。例如NPM、Promise组件化等。最后考察了WEPy、tinajs、Labrador、MPVue等几个框架,最终决定用Tina JS来重构项目。选择tinajs的原因是基于小程序的语法,没有太多的修改,学习成本很低。同时,它的递进概念让我可以一步一步的访问框架,而不是一下子把它所有的概念都学完,这在工期紧张的时候对我来说更重要。当然,另一个更重要的原因是它的源代码非常简单,即使出了问题,我也能快速定位。
但是,即使选择了正确的框架,也难免会遇到坑,尤其是对于小程序。我来说说这段时间遇到的一些经典问题。
数据传输长度超过最大长度
我们是新闻流项目。用户可以无限期地下拉和加载数据,内部将使用一个数组来存储列表中的数据。当我在使用tinajs重构项目后准备尝试时,发现当我加载超过一定数量的数据(约200条数据)时,控制台会报告“输出传输长度超过最大长度”的错误。
最后,检查后发现小程序为了性能限制了一次渲染传输的数据量。一开始我以为我的数据超出了限制,所以采用精简不必要字段的方法来减少数据量。然而,当我不能再减少它时,我仍然觉得它没有用。后来觉得我的数据真的有那么大,所以之前做了原始测试,发现刷了700多条数据的时候,也会提示这个错误。那么就可以证明我之前的200条数据没有超出限制。同时我对数据进行了JSON并保存在本地文件中进行检查,发现只有150KB左右,离上限相差甚远。
最后通过tinajs和我一个一个的分析,发现可能是一盆定制组件。因为我的列表元素有不同的样式,所以我用自定义组件来定义不同样式类型的组件,有些组件有共同的部分,所以它们必须被拉出来成为组件,也就是说,实际上我的列表是由一个多层嵌套的自定义组件循环渲染的。我们猜测最后渲染小程序的时候,每个自定义组件的传入数据都会复制一次,导致我原来的150K数据,瞬间超过了它们的限制。
最终的解决方案也很简单。因为我的大多数组件都是纯呈现组件,所以我总是使用模板模板来呈现组件内部的自定义组件。在这种情况下,我不会触发数据复制,尝试后也不会有问题。当然,除了减少数据量,用自定义模板代替自定义组件降低数据复制级别外,我们还可以对数据进行分页,减少一次数据渲染的量。
滚动
我们的小程序有一个下拉刷新功能,小程序本身正式封装了onPullDownRefresh接口来帮助我们做到这一点。但是因为我们的下拉刷新有自定义风格,所以不能使用官方界面。最终结果如下:
首先,我使用了scroll-view组件来滚动,同时,我使用了scrolltoupper来触发下拉事件。在内部,翻译操作用于展开下拉卡。吃完饭,我觉得很完美,但后来我突然发现官方提示:
请勿在滚动视图中使用文本区域、地图、画布和视频组件。
因为这些组件都是由Native实现的,只能固定在屏幕上,所以不能在滚动视图中使用。因为视频数据以后可能会加到里面,我对这个组件有点气馁。同时,使用这个组件后,其他外部组件修改scrllTop会非常麻烦,都需要自己维护一组事件,增加了业务复杂度。
最后,我返回到正常视图来监控touchstart、touchmove和touchend事件,并根据移动距离判断下拉百分比来实现这个功能。最终的实现可以说是极其困难的。然而,在实现之后,又出现了另一个问题。在iOS中会有阻尼效应,也就是你往下拉的时候,滚动条会有一种反弹的特殊效果,导致你虽然往下拉,但是触摸事件却无法有效执行。目前这个问题没有更好的解决方案,这里有用户建议需要提供参数来禁止页面阻尼效应。
不过目前还没有官方回应。除了阻尼问题之外,还有一个问题是wx.pageScrollTo()方法提供了duration参数,让滚动能够有动画效果。你可以在开发者工具中看到,实际上小程序的这个动画是使用transform属性来做的。正常情况来说这个是没有问题的,但是对于页面内存在position: fixed的元素来说,这个是有问题的。为什么这么说呢,大家可以看看 MDN 上fixed的描述:
It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has atransform,perspective, orfilterproperty set to something other than none (see the CSS Transforms Spec), in which case that ancestor behaves as the containing block.
via:https://developer.mozilla.org/en-US/docs/Web/CSS/position
文档上说 fixed 默认是相对于视口的,除非说父级元素设置了transform,perspective或者filter数值的值为非none,那么就会相对于这个祖先元素。这样就造成了如果我最开始相对于窗口设置了一个元素 fixed 在右下角,当我wx.pageScrollTo()操作的时候本来相对于窗口的元素就会突然相对于<page>定位,当滚动结束之后因为transform属性已经消失,所以元素又会闪现回来。有做一个代码片段,大家感兴趣的也可以试试:wechatide://minicode/Q7zHi6m96eYV 。滚到底部之后点击下方的蓝色色块,会发现蓝色色块出现闪动,原因就是刚才描述的。解决办法就是去掉wx.pageScrollTo()的duration参数,或者是将滚动内容使用<scroll-view>包裹与 fixed 元素分离。
Canvas
除了以上两个问题之外,还有一个就是 Canvas 画布的问题。这个 Canvas 画布最大的问题在于小程序内部是使用客户端组件实现的,但是在开发者工具中由于是网页预览所以这里的是 HTML 中的<canvas>。虽然微信将 HTML 原生的 canvas 大部分接口都实现了,但是我要说...还是有!很多!不!一!样!敲黑板,划重点!所以这就导致了开发者工具上看到的效果和客户端实际看到的效果有可能会完全不一样,给我们开发过程带来了无尽的阻挠!而且我这边不知道为什么,即使是同样身为客户端版本的调试版,预览版,线上版,这三者有时候表现出来的行为也可能造成不一样,我当时的内心真的是万马奔腾!
由于是客户端渲染的画布,所以小程序的画布有以下几个比较明显的特点:
- 小程序的画布是无限大的,使用 CSS 的宽高设置只是影响它的显示区域,并不会影像绘制。也就是说你发现元素绘制超出画布返回之后,只要设置画布的 CSS 宽高即可让超出区域显示出来。如果是 HTML 中的画布的话应该是没有这个效果的。这样就导致了我们没办法同时设置绘制区域和画布大小这两个概念。
- 小程序的画布因为是客户端渲染的,所以它永远是置顶的,z-index属性是无效的。这个理解起来也非常简单,因为网页是使用客户端 WebView 组件加载的,画布又是另外一个客户端组件,两个客户端组件叠加只能是以层级的关系叠加,没办法做成嵌入式的。
- 小程序的ctx.draw()方法是异步的,而且默认是清屏重绘的。这个和原生的画布是有区别的,之前我没有注意清屏重绘的问题导致调试了很久。
- 小程序的ctx.drawImage()方法只能获取本地资源的图片。当你想要使用远程资源的时候必须先使用wx.getImageInfo()方法下载下来获取到路径之后才能使用ctx.drawImage()进行绘制。
另外还有一个问题在于,小程序的画布必须可视才能绘制成功,也就是说如果你给这个画布设置display:none然后等它绘制成功之后再显示出来是不可以的。目前我的解决办法是在页面用户不可视区域内先绘制然后再获取图片内容。
后记
目前接触到的小程序的一些问题大概是这么多,有些可能在之后的版本中会解决(例如阻尼效果),而有些真的就是特性必须去适应(例如画布)。希望我总结的一些经验能帮助到大家。
版权声明:关于微信小程序的那些事是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。