🌞

小程序开发六个月总结——几个0到1

继上篇文章至今,又过去了三个月,丁妈小程序围绕开发、发布、测试等方面有了一些阶段性的进展,临近元旦,做一个梳理,为来年的进一步提升提供一些参考。以下所有全是针对原生小程序开发:写在前面这一年,小程序在

文链接在语雀:https://www.yuque.com/wumingshi/rkh1qq/

上篇文章至今,又过去了三个月,丁妈小程序围绕开发、发布、测试等方面有了一些阶段性的进展,临近元旦,做一个梳理,为来年的进一步提升提供一些参考。以下所有全是针对原生小程序开发:

写在前面

这一年,小程序在产品层面并没有取得明确的定位,主要来自于业务方的各种方式大调整以及对于小程序承担的角色没有很强的划分。导致小程序经历了好几次的分分合合,此时此刻正在经历年终的又一次大调整,关于业务前端层面内耗较大。

尽管被业务带动着野蛮生长,而我们也希望可以持续挥洒一些阳光(🔽😂)。

image.png


这三个月迎来的主要变化

从0到1有了自己脚手架

过去的开发模式是大量npm scripts的堆砌,集成的工具非常离散,比如常规校验、编译等工具散落在项目中,与项目本身耦合严重。结合之前的功能,希望构建一个业务可用且高效的脚手架,并对当前的业务进度无影响。

选型 gulp + scripts

太封闭、太离散都是耍流氓,一方面需要考虑到具体业务自身的特性,可以自由拓展,一方面对于各个项目中共识的约定可以做一些收敛,或许叫项目工具集合更合适。

项目内集成方式

通过拓展gulpfile实现

const gulpConfig = require('@dxy-mom/weapp-builder');

module.exports = gulpConfig;


目前主要包含的功能

  • gulp dev:启动开发
    • auto update
    • 拷贝自定义配置
    • 监听scss文件变动,并生成wxss
    • 监听service模块中ts文件变化并打包
  • gulp prepub 提测 将当前分支合并至test 并进行push的指令集
  • gulp generate:生成文件模板(page、component) example:
    • gulp generate -p mypage 创建名称为mypage的page文件
    • gulp generate -c mycomponent 创建名称为mycomponent的component文件
  • gulp changeAppId:切换环境(多appId环境进行切换) 配置在package.json内 例如" env": { "test": "xxx", "prod": "xxxx" },
  • gulp buildAllScss: 构建所有scss文件 将文件内容下的所有scss 编译成 wxss
  • gulp update 升级工具脚手架
  • gulp version 查看当前工具版本
  • gulp preCommitCheck 提交之前的文件校验,校验流程参考./scripts/checkfileRule.md
  • gulp jest 测试, 配置文件添加在项目__tests文件夹下


从0到1有了自己的发布系统

我们每一个小程序有两个开发账号(测试+线上),2个就是4个...2n

过去的提测流程:

打断当前的workflow

将当前的代码合并和test分支rebase,

拉取最新的代码

切换到测试环境appId

点击上传

等等等。。。

还没完,开始找测试小程序的账号

登录小程序后台

切换到自己的体验分支

同步测试,体验分支切换完成

过去的发布流程:

打断自己的workflow

将自己的代码合并到master

打tag

本地拉取master

上传

等等等。。。

还没完,开始找开发环境小程序的账号

登录到管理后台

切换到自己的体验分支

回归

提交审核

image.png


以上不仅流程相当繁琐,同一个分支每个人发布的结果还会不一致,以上就是曾经的现实,配得上野蛮不?

终于,我们迎来了一台光明之神——嗯,一个windows主机

现在我们的提测流程

  • npm run prepub(如果有冲突当然还是要自己解决)
  • 好了群机器人会吼你的

现在我们的发布流程

  • tag
  • 好了群机器人会吼你的
  • 登录后台提审还是要人肉


发布系统实现基本结构如下

image.png

目前的发布方式还是约定大于配置,新项目接入只需要增加webhook以及在fe-server中增加新项目的token配置即可,组内接入成本较低。

丁香医生也有一套类似的发布服务,后续小程序发布会被集成到统一发布系统中。

从0到1尝试了TS

小组内对于TS在小程序的实践并没有很多,一方面在于历史包袱,另一方面诱惑没有那么的大。目前小程序官方有一套完整的TS开发的方案,而我们还是以js为基础,只不过在一些方面粗略的借助一下TS的校验能力。所以有了service API模块,为什么叫API service?

举个🌰:


return new Promise((resolve, reject) => {
    const combineCommodity = (list, commodityDic) => list.map(item => ({
      // 此处省略N行
            limitCount: item.limitPerUserBuy ? item.limitPerUserBuy - item.limitPerUserBoughtCount : item.stock,
    }));
    API[201020012]({ cartType: 1 }).then(async res => {
      // 此处省略N行
          try {
            // 此处省略N行
            const res = await getSkuDetailBySkuIds(idArray.join(','));
            // 此处省略N行
          } catch (error) {
            console.error(error);
          }
        }
      }
      resolve({
        // 此处省略N行
        crossBorderList: combineCommodity(crossBorderList, global_cache_commodityDic),
      })
    }, reject)
  })

一方面,我们需要的数据经常需要几个接口的拼凑,另一面还需要对数据做一些逻辑操作才是最终我们想要的,而我需要的仅仅是一个列表数据。

后端也在规划接口聚合的能力,目前处于beta测试阶段

所以对于这部分决定用TS进行一定的抽象,最终形成了以下services模块:

image.png

  • folder api 定义各种接口,例如
200722006': (data: {activityIds: string,newVersion: boolean,sellChannel: number}, showConfig?: RequestConfig) => {return wrapService({url: 200722006,method: 'GET',needLogin: true,})(data, showConfig)}


  • folder services定义各个页面需要的真实数据,通过逻辑聚合所得即所要
  • index.js 为ts打包产物,供js型小程序调用
  • config.js 为一些基本打包配置


对于api和service都可以是一个通用模块,当前丁妈前端的小程序技术栈还是以js为主,后续什么时候切换到TS还需要看时机。

image.png😂


从0到1有了一些公共库

之前长期处于单仓库状态,导致大量的逻辑都耦合在项目仓库。对于公共逻辑的抽离还局限在仓库里,完全无法复用,随着新项目启动,针对一些通用高且稳定的公共模块进行抽离,例如weapp-request、weapp-utils(内网访问)。

除了可以彻底剥离的部分,对于项目可以共享的部分,尝试使用bit进行共享,并搭建了内网服务,但是当前并没有真实有效的利用起来:

  • bit内网服务服务无法提供有效的可视化界面
  • bit本身需要有额外的学习成本,并且需要和npm包区分
  • bit在小程序上只能使用其最基本的源码管理功能,具有比较强的局限性
  • 最重要的不如ctl cv快 ̄□ ̄||
  • 对于可复用的部分并不明确,从设计层面就开始不明确...


从0到1提供了e2e测试能力

借助小程序自动化api,在脚手架中集成了自动化测试的能力,正在考虑接入到发布流程中

一个简单例子如下:


const automator = require('miniprogram-automator');

describe('main route', () => {
  let miniProgram;
  let page;
  beforeAll(async () => {
    miniProgram = await automator.launch({
      projectPath: process.cwd(),
    });
    page = await miniProgram.reLaunch('/pages/home/index');
    await page.waitFor(500);
  });
  afterAll(async () => {
    await miniProgram.close();
  });
  it('每个tab走查', async () => {
    let error;
    try {
      await miniProgram.switchTab('/pages/tab1/index');
      await miniProgram.switchTab('/pages/tab2/index');
    } catch (e) {
      error = e;
      console.log(e.message);
    }
    expect(error).toBe(null);
  });
});

image.png

编写测试用例本身成本并不小,实际生产中主要还是以检测白屏为主,对主页面进行走查。

从0到1有了组件站

之前的组件是否存在以及和设计沟通过程中,全凭一张嘴和记忆。为了以最小成本结束这种局面,一个精简版的组建站诞生,大概长这个样子:

image.png

通过在组件内定义一个md文档,包含标题以及当前组件的一个截图(链接到蓝湖设计链接),利用jsdoc解析js文件内参数说明拼接为一个静态站点,内网可以前往216:3123查看。

曾经以为可以以此站点为基础,拓展各类组件,并最终归为一个统一的组件库,然而想太多了,产品差异过大,并不一定能在各个实际产品中共用。所以很可能最终会每个仓库会有一个类似的东西

正式实践了一次状态管理

之前也提到过小程序的状态管理,当时的正式的状态管理基本为0,终于还是等到了官方靠谱解决方案(mobx-miniprogram),新的项目中针对商详、购物车、下单进行了重构,整个页面的状态和逻辑统一管理,大大简化了小程序组件本身的交互带来了复杂性,并会”无意“中降低页面本身setData次数,更多数据变动的通信下沉到具体到组件内,对于复杂页面的优化还是比较明显。

小程序完整依赖mobx 4.0,只使用了其中部分特性,利用runInAction+wx.nextTick(setData)实现数据变更监听与改变。


写在最后

以上是最近三个月小程序再项目迭代进行中同步进行的一些建设,很多设计和实现也比较粗糙,面临的挑战还有很多,例如:

  • 业务不确定
    • 如果再过一年,目前的小程序项目该发展成什么样,其实是不太清楚,比如运行时要不要关注、多端编译是否有必要
  • 组件
    • 虽然有了一个简版的组建站,但是组件到底该去往哪个方向,并不是太明确,一方面业务不确定性给组件的前进造成了很大的阻力,功能组件为了满足不同的需求会变得越来越臃肿,对于组件的设计要求较高,总之组件这个轮子在业务快速迭代过程中,落地困难。
  • 监控
    • 当前基于sentry会做错误日志收集,针对错误的梳理和环境的区分,当前线上环境的错误相对较少,由原先三位数降为现在的两位数,之前主要是由于环境以及误报问题导致,尽管做了每日的错误报告以及一个简单的错误统计页面,但错误的发现还是需要主动发现,错误的实时报警相对较薄弱。

            image.png

    • 对于性能的监控还是空白











5gfs4McCLykdW3S.gif

updatedupdated2023-10-312023-10-31