微信小程序开发实践总结

前言

近日,小程序如约而至在1月9日凌晨在微信IOS和Android最新6.5.3版本中上线了期待已久的微信小程序,用户只需要更新最新版微信并搜索自己感兴趣的就能够体验已发布的小程序了。本人有幸和另一位团队的小伙伴也一起参与了公司车管家小程序的开发,我们前端这边的开发整体进度都还算顺利没有遇到特别坑的地方,当然这里离不开小伙伴雪娇童鞋她们在这之前做了很多产品和技术上的调研以及做了Demo的实践还有分享等等工作,都确保了我们开发上没有遇到大的阻碍。现将我从小程序的学习到开发到测试的过程中遇到的问题和踩的一些坑做一个小总结。

关于整体的开发模式

小程序不像我们前端er日常开发web和H5那样在任意一个文本编辑器里编写代码在浏览器中就能实时看到页面还有本地和对外访问的链接地址,小程序是没有这些的(后面有牛人开发并开源了WEPT,集成了微信小程序组件UI可以在浏览器中查看调试)。微信为开发小程序应用提供了web开发者工具供开发者使用(实际开发中这工具开发体验真是low到可以了),建议大家实际开发时依旧使用自己的IDE,用WEPT或者web开发者工具查看页面。从小程序的开发语言中我们可以看到一些ReactAngular以及Vue的影子,也许小程序就是集大成了这三大目前最主流的前端框架(?问号脸),因此只要开发者之前有这三种框架的开发经验再认真阅读阅读小程序的API文档,那么小程序的学习成本和上手开发成本都不是很高。它的模式很闭环很严格限制,只要按照最新的官方文档去规范开发,开发完成利用开发者工具平台打包压缩上传至微信服务器,然后微信进行审核,审核通过后会发布上线,用户在客户端搜索小程序名称获取并离线缓存至微信本地文件系统,供用户离线浏览访问。

关于开发者工具

左侧边栏切换选项依次是:

编辑

编辑选项中可以实时查看页面UI并且支持切换不同主流移动设备和网络环境以供适配和测试。右侧是代码编辑区,包括项目目录结构和文件编辑区。这里建议在新建目录或是文件时选择在开发者工具的这里新建比较快捷方便。编辑代码在自己的IDE中。

调试

看着是不是眼熟?这不就是我们熟悉的Chrome的调试面板吗,是的没错,但需要注意我们看到的“Dom元素”是在wxml文件下的,这里看到的也不是标准的Dom,而是类似React微信封装的一系列自定义组件,比如viewtextbuttonmapimage等。右边是StylesDataset,Styles查看组件样式内容的,Dataset是查看绑定在UI组件上的自定义data-*属性。这里注意调试页面时要将Wxml这个Tab选项排在面板第一位才能查看组件元素。

Sources面板可以查看项目资源文件,可以打断点调试页面对应js逻辑代码及Watch我们需要观察的变量等,这和Chrome里的差不多就不多赘述了。

AppData选项面板是web开发者工具特有的一项,提供的这个辅助功能非常有用,这里会显示每个页面Page({})实例中data数据对象的所有属性和值并且所有值都是可编辑的,这样就省去了不用我们开发调试时每次都要更改代码才能看到视图页面的变化,只要在这里编辑data属性的值就可以了,方便快捷高效。这里会显示你访问浏览过的页面对应的Page({})实例下的data数据对象,当然也可以只展示当前调试页面的data对象,将启动页面设置为当前调试页面即可。这里的data对象还支持CodeTree两种切换查看模式,切换至Code模式下可以看到当前data对象下具体的所有数据。当声明的变量过多时这里还支持查询,可以快速锁定需要调试的变量属性。

项目

在项目面板中有以下几点小细节可以关注下:

  • 点击预览会生成一个二维码,开发者可以用手机设备扫二维码进行真机调试,如果在app.json中配置了debug: true,则如果在真机中打开调试就会出现vconsole控制台,可以进行一些log的输出及设备信息的查看。
  • 如果只想单独调试某一个page,则可以点击预览右侧的小箭头进行自定义预览的页面设置。
  • 下面有几个工具支持的功能,如果项目统一用ES6则可以勾选开启ES6转ES5;可以灵活的选择勾选监听文件变化自动刷新开发者工具,本人实际开发时是写页面的时候不勾选(因为每次习惯性的按Ctrl+SCommond+S开发工具都会reload一遍,作为有轻微强迫症的我还是有点心疼自己mac的…我一般是手动刷新),当页面写完调试js逻辑功能时可以勾选。
  • 最后一项开发环境不校验请求域名以及TLS版本,这是一个挺重要的功能,在开发时我们用的更多都是测试环境的接口,可能这些接口服务器域名不是https的或者不在事先管理后台配置的合法域名内,此时勾选这一项就可以正常使用测试环境的接口以及一些第三方平台提供的Mock数据接口,因为开发者工具帮你做了请求代理。
  • 最下面的删除项目按钮可以将当前小程序项目从开发者工具中删除。

关于开发语言及小技巧

  • wxml:基本上和写html是一样的,但一定要用小程序API提供的组件标记,不可随意使用HTML的一些标签元素!使用这些组件标记就会有默认的组件样式;可以像AngularVue一样在这些组件标记上绑定事件、条件判断指令、循环指令等。
  • wxss:小程序的页面样式表文件,官方推荐使用Flex弹性盒方式进行页面布局,以及提供了响应式单位rpx(设计图最好给成750的,开发时设计图是多少px对应样式属性就是多少rpx!),小程序提供了全局样式app.wxss和页面局部样式page的wxss文件,我们可以将项目的所有公共样式放在app.wxss中,这样可以复用到任何一个页面中。
  • 特殊标记block:这不是一个UI组件,是一个包装元素,可以利用它很好的进行对页面结构进行判断循环展示等。
  • bindcatch开头再加上事件名称就组成了小程序的绑定事件,还可以在标记元素上绑定自定义属性供逻辑层js获取。
1
2
3
<block wx:if="{{item.status === 1}}">
<button type="default" hover-class="button-hover" hover-stay-time="100" class="btn-pay" data-ordernum="{{item.ordernum}}" bindtap="bindOrderPayTap">立即支付</button>
</block>
1
2
3
4
5
6
7
bindOrderPayTap: function(e) {
// 微信支付
Payment.requestPayment({
ordernum: e.target.dataset.ordernum ? e.target.dataset.ordernum : '',
openId: App.globalData.openId ? App.globalData.openId : ''
});
}
  • 页面和路由跳转传参
1
<navigator url="/pages/wz-records/wz-records?plateNum=京A12345"></navigator>
1
2
3
wx.navigateTo({
url: '../wz-records/wz-records?plateNum=' + params.plateNum
});

目标页面接收参数:

1
2
3
4
5
6
7
8
9
10
Page({
data: {

},
onLoad(option) {
wx.setNavigationBarTitle({
title: option.plateNum
});
}
})
  • 在使用wx:for循环渲染时要添加wx:key否则会报warning警告。
1
2
3
<view class="dj-group mb30" wx:for="{{orderList}}" wx:for-item="item" wx:key="*this">
<!-- ... -->
</view>
  • 小程序虽然提供了ES6转化ES5,但是新的API比如说Promise它并不支持,那么恶心的异步回调地狱问题就不可避免了,我的做法是除过第一次的request外之后需要进行异步request的都单另封装在一个方法中,这样在请求的回调中调用方法即可不用层层request请求嵌套下去。
  • 注意在request请求中,method不同对应的headercontent-type也是不同的(这点很重要!)
1
2
3
4
5
6
7
8
9
10
11
12
13
wx.request({
url: '',
method: 'GET',
header: {
'content-type': 'application/json'
},
success: (res) => {

},
fail: (res) => {

}
});

methodGET方式时:'content-type': 'application/json'

methodPOST方式(或者表单提交)时:'content-type': 'application/x-www-form-urlencoded'

  • 注意event.target.dataset与event.currentTarget.dataset的区别:target.dataset是取源组件上设置的数据集合,currentTarget.dataset是取当前组件上设置的数据集合。

  • 微信官方给出的page的生命周期的图片:

关于开发建议

  • 在项目中我们可以将公共的工具方法、公共的配置项(如接口URL等)、公用的业务逻辑(如调起微信支付等)抽离当前业务成公共方法,这样满足模块化开发的思想也提高了代码的高度复用性。
  • 如果团队统一使用ES6语法开发切记一定要在开发者工具、IOS手机设备、Android手机设备上运行都是不报错没有问题的!!我在一处使用了for (let item of arr) {}语法循环数据时就踩坑了,一直以为没问题直到最后提测时才发现在安卓上这ES6语法报错不识别,最后改成了.forEach(function(item, index) {})
  • 写页面布局结构时最好使用Flex弹性盒布局。
  • 使用navigateTo路由跳转时,小程序会缓存上一个页面到内存栈中(最多5个!)所以一些加载页面时做的一些逻辑判断一定写在onLoad或者onReady onShow中。
  • 所有图片资源都先在本地压缩(小图标图做sprit合并)然后再统一上传到CDN服务器上,页面图片地址直接走https服务器(因为小程序最大限制1M所以最好别把图片放本地了)。
  • 微信登陆通过code获取openId作为小程序用户的唯一标识,小程序提供了微信用户登录的接口API以及获取用户信息的API,不过并不会反回用户的openId,此时需要我们自己的服务端提供这样的接口来获取用户的openId。具体实现方法是先调用登陆请求方法wx.login({})后返回code,再通过code去请求我们自己服务器的接口用来返回openId。具体代码实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 用户登录
wx.login({
success: (res) => {
// 获取openId
if (res.code) {
wx.request({
url: '',
method: 'POST',
data: {
code: res.code
},
header: {
'content-type': 'application/x-www-form-urlencoded'
},
success: (res) => {
if (res.data && res.data.errcode === 0) {
if (res.data.openid) {
that.globalData.openId = res.data.openid;
}
} else {
console.log('获取openId失败:' + res.data.errcode + '=====>' + res.data.errmsg)
}
}
});
}
},
});

成功获取到openId后我们可以将其绑定在全局变量globalData上,这样就可以在任意page中访问到globeData下的openId

  • 微信支付:小程序提供了调用微信支付的API方法
1
2
3
4
5
6
7
8
9
10
11
12
13
wx.requestPayment({
'timeStamp': '',
'nonceStr': '',
'package': '',
'signType': 'MD5',
'paySign': '',
'success':function(res){

},
'fail':function(res){

}
})

一开始看文档以为需要传的这5个参数值是前端要做的,后面仔细看了关于支付方面的文档和技术原理示意图,发现这几个参数不需要前端处理(也不安全…),这依旧需要我们自己的服务端提供接口给返回,最后沟通了公司负责支付相关的部门他们给予了支持,所有所需参数都由负责支付部门的提供返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
let requestPayment = (obj) => {
return wx.request({
url: '', // 开发者自己业务的服务端接口
method: 'GET',
data: {
ordernum: obj.ordernum,
openid: obj.openId
},
header: {
'content-type': 'application/json'
},
success: (res) => {
if (res.data && res.data.returncode === 1) {
const result = res.data.result ? res.data.result : {};

// 微信支付
wx.requestPayment({
'timeStamp': result.timeStamp, // 当前时间戳
'nonceStr': result.nonceStr, // 随机字符串
'package': result.package, // 统一下单接口
'signType': result.signType, // 签名算法
'paySign': result.paySign, // 签名
'success': (res) => {
if (res.errMsg === 'requestPayment:ok') {
// 成功
} else {
// TODO
}
},
'fail': (res) => {
// 用户取消支付
}
});
} else {
// TODO
}
},
fail: (res) => {
// TODO
},
complete: (res) => {
// TODO
}
});
}

思考与总结

优势?

  • 低门槛下载,作为微信的一环,可以通过微信直接进入,即可使用。

  • 跨平台,微信客户端底层封装,支持小程序跨平台。

  • 开发成本低,通过之前的开发对比,小程序的开发比web app 的开发成本还低,并且前端的资源存放、发布运维都集成在微信中。

  • 页面仿原生,体验更流畅。

  • 小程序可以使用微信的支付功能。

局限?

  • 开发基于微信框架,部分功能受限,不支持现有的其他第三方插件。

  • 小程序页面只能同时打开5个,如果交互流程较长难以支持。

  • 小程序包大小限制为1M(目前),所有只适合轻量级。

最后安利一段最近看到的不错的语录:

“所有的成长过程都是从提出一个问题开始,并找到了答案,最后融入自身的价值观,完成下一次更好的选择,周而复始。”

坚持原创技术分享,您的支持将鼓励我继续创作!