我的第一次小程序制作 — 总结与思考

@JChehe 2019-03-17 13:10:14发表于 JChehe/blog

cover

微信小程序是赋能开发者微信生态能力、改善 Web 用户体验、对 Web 开发者友好的特定应用。

前言

最近参与了一个新项目,涉及到微信小程序。鉴于第一次接触小程序和对小程序的期望,借此谈谈本次项目的总结与思考。

笔者之前也编写了《我的第一次移动端页面制作 — 总结与思考》

小程序的生态

在微信的《2018 年数据报告》中提到:

  1. 截止 2018 年 9 月,微信总体活跃账户数达到 10.82 亿。
  2. 小程序目前已经覆盖了 200+ 个行业,服务用户达到 1000 亿+ 人次,年交易增长超过 600%+,创造超过 5000 亿的价值。

在中国市场有如此高的占用率,无疑是一个不可忽视的场景。

尽管在浏览器不断提高性能和赋予网页更多原生应用权限的今天,多年被诟病的浏览器兼容性和差异性等依然是开发者难以跨越的鸿沟。

而微信正是看到了网页体验的种种痛点,于 2016 年正式推出了小程序。

小程序不仅改善了“触摸体验”,更融合了微信的整体生态。如更早前为普通网页提供微信原生能力的 JS-SDK(涵盖拍摄、录音、语音识别、二维码、地图、支付、分享、卡券等几十个 API)。

小程序带来了贴近原生应用的特性和微信的生态能力,使得开发者能相对轻松地拥有“完整”的应用。

小程序の知识点

网页开发者需要面对的环境是各式各样的浏览器,PC 端需要面对 IE、Chrome、QQ 浏览器等,在移动端需要面对 Safari、Chrome 以及 iOS、Android 系统中的各式 WebView 。而小程序需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具,小程序中三大运行环境也是有所区别的,如下表所示:

运行环境 逻辑层 渲染层
iOS JavaScriptCore WKWebView
安卓 X5 JSCore X5 浏览器
小程序开发者工具 NWJS Chrome WebView

关于小程序更多知识,更推荐官方文档——《小程序开发指南》。笔者阅读后整理了以下一幅思维导图。当然,这是基于笔者个人理解和需求整理而成。理解实现原理,才能更好地应对开发中遇到的疑问和编写出更合适的代码。

小程序思维导图
思维导图的下载地址>>

开发体验

笔者作为后来者,更像是站在巨人肩膀上。而老东家则是微信小程序的第一批尝鲜者,躺过无数坑。

现在开发框架百花争放,让开发体验不断往相对完善的 Web 靠拢。WePYTaro 等便是其中的佼佼者。基于团队的技术栈,我们此次选择了更像 Vue 的 WePY。

尽管 WePY 的风格像 Vue,但在开发的过程仍有部分不完善的地方,需要通过 Hack 的方式规避一些问题。至于具体哪些问题,本文也不会列举太多。毕竟这不是技术原理/本质的东西,说不定过一阵子就会被抹掉(WePY 2.0 正在路上)。

WePY(1.7.x)版本存在以下问题:

  • 页面(或组件)中非 data 内的属性需要在 onUnload 重置,否则下次打开该页面仍是上一次的值。

    // 本文称该区域的变量为:页面局部变量
    const height = 100
    
    export default Class Index extends wepy.page {
        data = {
            name: 'Jack'
        }
        
        methods = {
            changeHandle () {
                height = 120
                this.name = 'Amy'
                this.age = 18
            }
        }
        
        // 本文称该区域的变量为:页面属性
        age = 10
        
        onLoad () {
            // 假设第一次进来触发了 changeHandle,那么第二次进来会分别输出
            console.log(this.age) // 18
            console.log(this.name) // 'Jack'
            console.log(height) // 120
        }
    }

    与 Vue 相比,有以下异同:

    • :Vue 会忽略页面属性(age);
    • :页面局部变量若未在组件销毁时重置,也会在保留至下次(height)。
    • data 内属性会在组件销毁时重置。

    对此,笔者有以下建议:

    • 页面局部变量用于声明为常量;
    • 对于非 data 内属性,要在 onUnload 内进行重置;
  • $apply() 的触发都会导致 computed 属性内所有值都运行一次,即使 computed 属性所依赖的属性未变更。

    与 Vue 相比,有以下不同:

    • Vue 中,computed 属性仅在其所依赖的属性发生变化时才重新计算

    优化方法:

    • 减少不必要的调用 - $apply()
    • 对于频繁触发的方法,尽可能 debounce、throttle 化。
    • 不需要在页面模板渲染的变量可声明为页面局部变量或页面属性,这些属性的变更无用在异步回调中显示调用 $apply() 触发脏检测。
    • 对于计算量繁重的 computed 属性,可声明为 data 属性,然后 watch 其所依赖的属性,这样就能仅在所依赖的属性变化时才会重新计算。
  • methods 属性提供的 onPageScroll 方法是 WePY 封装过的,其内置 $apply() 会导致频繁触发脏检测问题。

    ...
    methods = {
        onPageScroll () {} // 即使函数体为空,只要定义了该方法,页面滚动就会触发该方法,从而导致频繁调用 $apply()
    }
    ...

    其实在页面属性定义 onPageScroll 即可使用微信小程序提供的原生方法,这样就可将其 throttle/debounce 化。

    onPgaeScroll () {
        this.pageScrollThrottled()
    }
    
    onLoad () {
        this.pageScrollThrottled = throttle(() => {
            this.isScrolling = true
            this.$apply()
        }, 3000)
    }
  • WePY 1.7.x 对于自定义组件是静态编译的,所以对于 repeat 里放置自定义组件是存在一些问题的:

    <repeat for="{{list}}">
        /* 向自定义组件的 anyProp 属性传 item.someKey,并在 customComponent 里对 anyProp 属性衍生出 computed 属性,结果该 computed 属性输出却是 undefined */
        <customComponent :anyProp.sync="item.someKey" />
        /* 直接将 item 传入,但该 repeat 内所有 customComponent 的 anyProp 都指向 list 的第一个 item */
        <customComponent :anyProp.sync="item" />
    </repeat>
    

    除了上述两个问题,还存在 repeat 嵌套 wx:for 循环等其他问题。因此,在 WePY 解决这些问题前,暂时可通过以下方法规避以上问题:

    1. 对于 repeat 内的自定义组件,只支持直接渲染 props 属性,不支持其他诸如 computed 后处理。
    2. 如官方文档所说,将 repeat 封装为一个组件,而不是将 item 封装为一个组件。
    3. repeat 列表的自定义子组件存在列表渲染时,使用 repeat 而不是 wx:for

    以上自定义组件的问题相信在即将到来的 WePY 2.0 会得到有效解决。

性能优化

除了耳熟能详的 《雅虎前端优化 35 条规则》 和《小程序开发指南》的第七章 性能优化,针对 WePY 也有一些需要注意的地方。

分包

小程序支持“分包”,即小程序的代码包可以被划分为几个:一个是“主包”,包含小程序启动时会马上打开的页面代码和相关资源;其余是“分包”,包含剩余模块的代码和资源。这样,小程序启动时,只需要先将主包下载完成,就可以立刻启动小程序,从而显著降低小程序代码包的下载时间。

在 WePY 中,当项目结构是将页面的业务组件放在 src/components/{pageName} 下,那么 WePY 在构建时会把所有页面的业务组件均放置在主包内,导致主包冗余,影响小程序首次加载时间。

├── app.wpy
├── components
│   ├── article
│   │   └── comA.wpy
│   ├── common
│   │   ├── table.wpy
│   │   └── tabs.wpy
│   └── index
│       ├── vfooter.wpy
│       └── vheader.wpy
├── pages
│   ├── article
│   │   ├── article.wpy
│   │   └── list.wpy
│   └── index.wpy

为了避免主包含有其他分包的组件,需要将页面的业务组件放置在 src/pages/{pageName}/components 下:

├── app.wpy
├── components
│   └── common
│       ├── table.wpy
│       └── tabs.wpy
├── pages
│   ├── article
│   │   ├── article.wpy
│   │   ├── components
│   │   │   └── comA.wpy
│   │   └── list.wpy
│   └── index.wpy

另外,微信开发者工作中提供了“体验评分”插件,它是一项给小程序的体验好坏打分的功能,会在小程序运行过程中实时检查,分析出一些可能导致体验不好的地方,并且定位出哪里有问题,以及给出一些优化建议。

思考

关于对小程序的思考,我想脱离开发者的角度,以产品/运营角度看。所以待笔者有属于自己的产品时再回头补充。

最后

任何新生事物的发展都不会是一帆风顺的,都要经历一个从小到大、由弱到强的曲折发展过程。

微信小程序的出现,为大家带来了比 Web 更好的使用体验。而小程序的火爆,也促使社区诞生出对开发者更友好的开发框架和工具。

显然,小程序仍在快速迭代中,难免出现一些问题或不足的地方。希望大家在享受小程序红利的同时,也耐心地向微信官方反馈遇到的问题,共同成长。

最后,希望属于自己的“小程序”也早日落地。

参考资料