武装你的小程序——开发流程指南

10,006 阅读11分钟

前端工程本质上是软件工程的一种。软件工程化关注的是性能、稳定性、可用性、可维护性等方面,注重基本的开发效率、运行效率的同时,思考维护效率。一切以这些为目标的工作都是"前端工程化"。工程化是一种思想而不是某种技术。

本篇文章你可以学到:

  • 如何使小程序支持scss;
  • 怎样通过gulp编译你的项目;
  • 项目常用的模块封装;
  • 小程序同webview之间如何优雅的进行交互;
  • 集中式管理你的项目提高可维护性;
  • 提升开发效率的小工具编写;

全文全部基于原生的小程序开发所阐述,各种第三方框架开发不在此列。并不会将整个项目的搭建流程细致的写出来,而是挑其中我认为在开发过程中存在的一些很重要的点进行详细陈述。

普通小程序开发流程有可能会遇到的坑

列举部分常见的

  • 小程序本身不支持常用的css预编译器,导致样式规范随意散落在各个文件,无法统一进行管理,而现代前端开发中不论是less,sass,stylus 都可以提升css效率;
  • 缺少统一的request拦截请求;
  • 缺少统一的路由管理;
  • 缺少集中式的API地址和ENV环境变量管理;
  • 缺少统一的本地缓存读取管理;
  • 重复的webview页面;
  • 不支持ES7以上的高级语法,如async await等特性;
  • 不管是体验版还是开发版只能存在一种环境,一旦发布预览测试环境切换繁琐;
  • 上线前需要手动修改线上环境,容易出错......

如何解决?

要解决工程化的问题,需要从两个角度入手:开发 && 部署

开发

Question

  • 如何提高开发生产效率?
  • 如何降代码维护难度?

Program

  • 制定开发规范,提高团队协作能力;
  • 使用自动化编译工具使项目支持各种插件和提高效率的工具;
  • 模块/组件化开发;
  • 所有需要集中管理的地方进行统一封装;

部署

  • 环境切换;
  • 压缩打包;

项目搭建

完整目录结构

开发流程

工程化方案选型

对于目前常用的工程化方案,webpack,rollup,parcel等来看,都常用与单页应用的打包和处理,而小程序天生是 “多页应用” 并且存在一些特定的配置。根据要解决的问题来看,无非是文件的编译,修改,拷贝这些处理,对于这些需求,我们想到基于流的 gulp非常的适合处理,并且相对于webpack配置多页应用更加简单。所以小程序工程化方案推荐使用 gulp

Start

初始化一个项目,结构如下

  • src 为开发目录
  • dist(开启编译后可见)为预览/上传目录
  • .gitignore git上传忽略文件
  • gulpfile.js 编译配置文件
  • CHANGELOG.md 版本更新日志
  • README.md 项目说明文件
  • package.json 项目配置文件

编译用到的插件 使用npm或yarn自行安装,安装过程不过多赘述,不会请自行搜索。
"gulp": "^3.9.1"
"gulp-sass": "^4.0.2" scss编译插件
"gulp-postcss": "^6.4.0" 强大的css处理插件
"gulp-rename": "^1.2.2" 更改文件名
"gulp-replace": "^1.0.0" 替换内容
"gulp-changed": "^3.2.0" 检测改动
"autoprefixer": "^6.5.1" 自动添加前缀

如何使用scss?

gulp配置打包sass非常简单,唯一需要注意的是@import的使用,wxss是支持样式导入的,但上面说到过小程序是天生的多页面应用,每一个页面都对应一个wxss,因此sass打包会把import的文件打包到当前文件,从而导致当前文件的体积变大。由于微信限制单包代码不能超过2M,因此当css越写越多的时候,这种打包方式势必会使样式文件越来越大。

解决import导入问题

那如何解决import的导入问题呢,其实也比较简单,说白了就是sass处理的时候,让其不处理import部分的语句就可以了。有两种方式可以做到,第一种是改写sass处理的源码,当遇到import语句时跳过。第二种是,在把文件交给sass处理前,我们先把import语句部分注释掉,这样sass处理的时候就会忽略了,当sass处理完成后,再把注释掉的语句打开即可。显然第一种成本比较高,也不好维护,所以我们采用第二种。
在处理import的时候,还有个地方是需要注意的。在sass中,import除了能引入css外,也可以引入变量,函数。因此,我们在处理的时候也需要注意区分,变量和函数最好有一个独立的文件目录存放,并且在import的时候,对于变量和函数,是必须交给sass处理的,也就是不能注释掉。因此我们单独配置了sass变量和函数存放的位置,这样我们在打包的时候,遇到这样的import语句,我们就跳过,交给sass处理,否则就代表其是引入了共用的样式文件,这样我们交给sass处理前,就先将其注释掉,sass处理完成后再把注释打开。

完整实现支持scss思路如下:

  • 指定文件处理目录
  • gulp-replace通过正则匹配@import语句将其注释
  • 判断当前@import语句是否存在于变量和函数文件的配置路径中
  • 不存在就注释,存在就跳过
  • 启用gulp-sass编译scss文件,
  • 通过postcss对低版本ios和安卓进行兼容样式处理
  • gulp-rename更改文件后缀为.wxss
  • gulp-replace通过正则匹配@import语句打开注释
  • 最后输入到dist目录

代码如下

拷贝其余页面,注意要排除scss文件,或者使用gulp-clean清理无用文件

建立监听任务

创建默认执行任务

将生成的dist目录作为根目录丢进小程序开发工具即可实时刷新预览小程序,至此你的项目已经完全支持scss了,尽情的去浪吧。

提高你的代码维护性——封装

request请求拦截器

wx.request是小程序中最常用的api,在实际项目中会涉及到很多需要统一拦截/发送/处理,所以我们需要对wx.request进行二次封装用来支持各类需求,以实现代码的可维护性。小程序本身已经支持promise语法, 在此用promise将其封装成常用的.then的形式

要做什么?

  • 可以通过header和data统一发送公共参数,如请求验证的token,用户id等信息...
  • 可以统一进行错误拦截处理,如全局登录状态判断,特殊code码的处理...
  • 可以根据配置自适应请求环境,如Mock,Dev,Test, Slave,Prod...

需要支持的功能

  • 请求方式
  • 参数传递
  • 成功回调
  • 失败回调
  • 是否开启mock数据
  • 请求时是否展示loading
  • 请求错误时是否展示toast

代码实现

调用方式

app.js入口文件内引入,并将其挂载到App对象上,需要调用时可通过getApp()的方式调用

index.js为例,关于API的引入会在后文介绍:

Router路由

路由的封装主要是为了防止路由地址各个文件散落,无法集中管理的问题。

需要支持的功能

  • 无参路由和有参路由
  • 路由地址缩写
  • 参数传递
  • 跳转延时
  • 跳转类型

代码实现 定义出存路由地址的对象,使用时直接通过key值匹配

实现一个parse方法解析参数为query拼接方式
定义一个push对象接收普通无参数path地址和有参数的option对象,option对象包含path(路由地址)、query(参数),duration(跳转延时),openType(跳转方式)
通过openType结合原生api实现路由的几种跳转方法
调用方式 还是老规矩,直接挂载App对象通过getApp()获取直接调用

Stroage存储

合理的Stroage方法封装可以使你更优雅的管理你的本地缓存。此功能需要支持三种常用的setItem(设置缓存)、getItem(读取缓存)、clear(清除缓存)方法,并且在你的团队内部最好整理一套写入缓存的规范,不要一股脑丢在全局,应该按一些模块进行划分存取,这样才能更好的维护你的本地缓存信息。

代码实现

写入和读取均支持key ---> value的普通方式也兼容key--->value--->module的模块方式,默认使用同步的方式设置,之所以加catch是为了防止在特殊情况下小程序会报警设置缓存错误,如同步报错则采用异步容错。 setItem写入缓存

getItem读取缓存
clear清除缓存

调用方式 同上挂载App,使用方法如下:
写入
读取
清除

API地址和ENV环境变量管理

API地址和ENV环境变量可以做为两个单独配置的文件进行配置,API文件只存接口路径,ENV存储多个环境变量,环境对象内配置当前环境各种域名,然后在app.js配置当前环境变量,作为key值匹配ENV内的环境,将匹配的环境挂载App对象,配合前面封装的fetchApi以env+url的方式实现自动环境适配。

API

ENV
app.js

统一的webview

微信小程序提供了在小程序中内嵌HTML页面的能力,从微信小程序基础库1.6.4开始,可以在小程序内放置一个组件来链接HTML页面。有了就可以方便的将几端共用的h5页面集成到小程序内部,为我们减少了可观的工作量。

如果你有多个需要集成的webview页面实际上无需为每个页面都单独建一个文件,只需对一个公共的webview页面进行简单封装配合路由即可集中管理你的webview页面。

代码实现

wxml引入webview组件和加载中动画,webview组件接收地址,和加载成功回调。

js中在data内定义webview页面的地址列表,key用type代表指定路径,value用page代表页面链接,通过onload接收一个formpage参数对应type,动态加载组件上的src即可。需要注意的一点是,如果需要在webvie链接拼接获取的参数,在某些安卓机型会因为提前渲染webview而src地址没有初始化而产生白屏,所以最好的方式是通过一个变量控制组件的展示隐藏,确保需要渲染组件时数据已经初始化完成以保证页面正常展示。

如何解决多环境切换问题

小程序不像h5网页只要部署到对应环境,就可以随意输入指定的环境域名进行测试,而小程序像app一样没有网址这一说,它本身只会存在一个预览版本,普通的流程是每次当测试同学需要在不同环境中测试时就需要找到开发同学手动更改环境并重新发布体验版。这样的流程是很不灵活的,所以我们需要想一个办法,让一个小程序版本自由的切换多个环境,而无需手动改代码配置发布。
如何实现? 实现的思路有很多种,主要需要解决的就是环境如何进行切换的问题,在这里我是通过利用小程序的重力感应api模拟摇一摇,将切换环境搓成一个一个隐藏的小彩蛋,测试人员只需要摇一摇弹出环境选项列表点击对应选项更改App对象的config内的env环境属性即可成功切换环境。

代码实现

app.js也要进行处理,因为不能将该功能带到线上所以需要进行逻辑判断。

如何自动打包部署环境,防止手动配置易出错的问题

在这里其实还是要用到gulp这个神器,来实现不同环境的代码打包,配置起来很容易,无非就是通过gulp-replace在打包的时候对app.js的环境变量进行配置,但要配合微信开发工具的自定义处理命令每次在发版本审核的时候只需要开启该功能即可。

gulpfile.js

project.config.json
开发者工具

以上列举了开发流程中常用的工程化解决方案,再此希望对大家有一个参考作用,如有问题欢迎指正。