还有两三天就跨年了,今天总结下开发小程序的心得与体会
开发前准备
本文首先假定开发者已经粗略阅读过微信小程序的开发文档,所以注册小程序的流程就不介绍了。不过需要注意,小程序现在只允许企业用户注册,所以认证需要企业营业执照复印件和加盖公章的小程序申请公函。如果是选择对公账户认证,则不需要公函。另外,如果需要使用微信支付接口,则需要另外进行一次微信认证,这个就必须使用300块的方式了,如果要进行第三方平台开发小程序,建议使用对公账户,一般几毛钱就能认证一个小程序。
开发工具介绍
我调试用的开发工具就是微信官方提供的IDE,编写代码还是使用Atom编辑器,毕竟代码高亮提示等等是必须的啊,现在也有了很多第三方的IDE或者插件,但是用起来感觉整合度不如官方版,索性不换了。新建项目需要输入注册小程序时获取的AppId。需要注意的是设置页面:其中有一项是开发环境不校验请求域名以及TLS版本。这一项需要勾选上,因为微信只支持HTTPS的协议而且必须是指定域名,这让开发者使用localhost调试变的很麻烦,勾选此项之后就没有这种限制了,但只有在开发环境才可以,
开发者工具调试界面和Chrome浏览的开发者工具类似,调试工具分为 7 大功能模块:Wxml、Console、Sources、Network、Appdata、Storage、Sensor、Trace。
语言
首先,小程序类Web,但不同于我所认识的HTML,他有属于自己的开发语言:
JavaScript: 微信小程序的 JavaScript 运行环境即不是 Browser 也不是 Node.js。它运行在微信 App 的上下文中,不能操作 Browser context 下的 DOM,也不能通过 Node.js 相关接口访问操作系统 API。
WXML: 作为微信小程序的展示层,并不是使用 Html,而是自己发明的基于 XML 语法。
WXSS: 用来修饰展示层的样式。官方的描述是 “ WXSS (WeiXin Style Sheets) 是一套样式语言,用于描述 WXML 的组件样式。WXSS 用来决定 WXML 的组件应该怎么显示。”
入口文件
小程序主要包含以下三个入口文件:app.js 这个文件是整个小程序的入口文件,我们主要做了网络检测、用户信息获取等;当让也可以注入公用的方法在其他页面中去通过getApp()调用(注:页面中调用app.js中的方法时不需要通过require或者import引入)
app.json 这个文件可以对小程序进行全局配置,决定页面文件的路径、整体窗口表现、设置网络超时时间、设置多tab等.
app.wxss 是小程序的公共样式表
一、准备工作
注册一个小程序账号,得用一个没注册过公众号的邮箱注册。
注册完账号,登录,在主页面左边列表中点击设置,然后再设置页面中选开发设置就可以看到AppID,用于登录开发工具。
二、开始项目
- 项目目录需要是空目录
- AppID如果不着急发布可先不填写,如果需要是直接上线的项目就必填AppID
-
项目名称可为中文和英文
勾选快速启动模版
启动后
这是官方提供的示例模版
1、框架
先看下一目录:app.js: 小程序逻辑,生命周期,,全局变量app.json: 小程序公共设置,导航栏颜色等,不可以注释app.wxss :小程序公共样式,类CSS 。
小程序页面构成:
每一个小程序页面是由同路径下同名的四个不同后缀文件的组成,如:index.js、index.wxml、index.wxss、index.json。
微信小程序中的每一个页面的【路径+页面名】都需要写在 app.json 的 pages 中,且 pages 中的第一个页面是小程序的首页。
每个基础页面组成
这四个文件按照功能可以分成三个部分:
配置:json 文件
逻辑层:js文件
视图层:wxss.wxml文件
在 iOS 上,小程序的 javaScript 代码是运行在 JavaScriptCore 中,在 Android上,小程序的 javaScript 代码是通过 X5 内核来解析,在开发工具上,小程序的 javaScript 代码是运行在 NW.js(chrome内核)中,所以开发工具上的效果跟实际效果有所出入。
2、组件
微信提供了许多组件,主要分为八种:
视图容器、基础内容、表单组件、操作反馈、导航、媒体组件、地图、画布
包含view、scroll-view、button、form等普通常用的组件,也提供了地图map
、画布canvas
。
组件主要属于视图层,通过wxml来进行结构布局,类似于html。通过wxss修改样式,类似于css。组件使用语法实例:
<!--普通视图-->
<view>这是一个普通视图</view>
<!--wxss样式修改-->
<view class="mainView">样式修改过的视图</view>
更多的组件以及相关使用方法可以到官方文档-组件查看
3、API
网络媒体数据位置设备界面开发接口
其中网络请求
的使用必须先到公众平台登录小程序账号,在设置页面那里,设置允许访问的域名,网络请求包含了普通的http请求、支持上传、下载、socket。基本上满足了我们开发中所需要的网络需求。
这些API属于逻辑层,写在js文件中,使用示例:
//获取当前定位,返回经纬度等具体信息
wx.getLocation({
type: 'wgs84',
success: function(res) {
var latitude = res.latitude
var longitude = res.longitude
var speed = res.speed
var accuracy = res.accuracy
}
})
可以到官方文档-API查看其它API的使用方法。
生命周期
当然,微信小程序和其他前端框架类似也是有生命周期的:
Page({
/**
* 页面的初始数据
*/
data: {},
/**
* 生命周期函数--监听页面加载(像首页数据请求可以放在这里)
*/
onLoad: function (options) {},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作(这里添加了下拉刷新的功能)
*/
onPullDownRefresh: function () {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {}
})
当我们在data中初始化的值需要修改时,可在各生命周期及方法中通过setData()修改。由于小程序的入口页面就是首页,在首页添加了用户登陆和网络状态的检测在onLoad中。
tabBar
tabBar即小程序的底部导航栏,由于微信的限制,最少2个最多5个导航栏,只可设置文案、图标。(知识点:图的大小为81px边缘要留透明的白边,不然图会很大)
样式
小程序样式采用WXSS语言(具有CSS大部分特性)。他也提供了一种新的单位rpx(可根据屏幕宽度自行适应)。官方规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素,既然这样我们也推荐了我们设计师采用iPhone6作为设计标准,即750px宽度。
但是在实际的开发过程中如果字体大小也使用rpx做单位的话,在iPhone6 Puls上显示文字过大,影响美观。后经过测试采用了px做单位,即原设计稿尺寸的一半+px,这样可以保证文字大小在各设备中保持一致。
网络状态
在官方文档上有明确规定,本地资源是无法通过CSS获取的,图片的话只能使用网络资源或base64方式。首页有个需要判断网络状态的需求,由于断网情况下无法获取网络资源,最后就使用了base64的方式。
官方获取网络状态的API是getNetworkType为异步接口,通过它的返回结果再进行下一步(是显示无网络还是调用数据列表接口),说到这里大家都知该怎么办了——Promise,具体封装如下
new Promise((resolve, reject) => {
let req = wx.getNetworkType({
success: function (res) {
var networkType = res.networkType;
if (networkType === 'none') {
resolve(false)
} else {
resolve(true)
}
},
fail() {
reject(false)
}
});
})
调试
调试的时候最大的问题呢是:无论是开发者工具上还是手机上,记得先把缓存删干净再测。特别是开发者工具每次切换host都要清理缓存,再重新打开,而且出现bug的时候尽量多测几次,进行反复确定。不然的话,你可能会发现,本来测好的功能又出现问题了,或者是本来有问题的部分又没有问题了。
页面编写 (跟vue类似)
知识点1:
<text wx:for="{{titles}}" wx:key="{{item}}" class="home-title {{index == activeIndex ? 'active' : ''}}" bindtap='changeClassify'>
{{item.name}}
</text>
// index == activeIndex class为 "home-title active" 否则为 "home-title "
知识点2:
// 普通的单次循环
<text wx:for="{{titles}}" wx:key="{{index}}">{{item.name}}</text>
//循环嵌套时使用 wx:for-item="XXX"
<view wx:for="{{hotArr}}">
<view class="classify-title" bindtap="goClassifyPage">
<text>{{item.name}}</text>
</view>
<view class="classify-items">
<view class="classify-item" wx:for="{{item.data}}" wx:for-item="cell" wx:key="index">
<view>
<text class="classify-item_name">{{cell.name}}</text>
</view>
</view>
</view>
</view>
知识点3:
//router 跳转传参及参数获取
<text wx:for="{{titles}}" wx:key="{{index}}" bindtap='changeClassify' data-id="{{index}}">{{item.name}}</text>
//js
function changeClassify(e) {
//
let id = e.currentTarget.dataset.id;
//跳转到classify 页面
wx.navigateTo({
url: '../classify/classify?id=' + id
})
}
//classify 页面 获取参数
onLoad: function (opts) {
console.log(opts.id)
console.log(this.options.id)
}
知识点:4
上拉加载更多, 下拉刷新
- 直接使用小城程序自带方法onReachBottom 、 onPullDownRefresh
- 如果使用scroll-view组件还可以监听 bindscrolltoupper 、 bindscrolltolower
// 上拉加载更多
onReachBottom() {
if (this.data.next) {
this.setData({ isHideLoadMore: false })
this.getNextPage()
}
}
// 下拉刷新
onPullDownRefresh: function() {
this.refreshData()
}
//this.refreshDate里一定要使用停止下拉刷新的API
知识点:5
对于通用的组件可以抽出一个template
/**
* 1. 给template 设置name
* 2. 组件传过来的值可以直接使用 hidden="{{!isloading}}"
*/
<template name="loading">
<view class="qtshe-loadmore" hidden="{{!isloading}}">
<view class="qtshe-loading"></view>
<view class="qtshe-loadmore-tips">正在加载</view>
</view>
</template>
//
/**
* 使用通用的template
* 1. 按路径引入
* 2. 设置 is 等于 template的name data="{{isloading}}" 给template的数据
*/
<import src="../template/loading.wxml"/>
<template is="loading" data="{{isloading}}"></template>
知识点:6
如果你的页面需要加载一段html
- 官方没有提供相关API让小程序内能加载html
- 但是民间有偏方啊wxParse-微信小程序富文本解析组件
- 具体效果图可以看下青团社兼职实习小程序的精品兼职详情页
踩坑(详细问题看精品兼职详情页)
- 官方textarea组件的placeholder会跟着屏幕滚动而滚动,设置fixed=true即可,但是,如果你的textarea是在一个弹窗里面的组件。跟着屏幕滑动到底部后再使用会发现,textarea滑动到底部去了,textarea输入不了,最后解决办法是页面最外部套一个view设置overflow:hidden,position:relative,让页面自动滚动到顶部,虽然交互不太好,但是是暂时唯一能解决这个问题的办法。
- 兼职详情页在当初小程序官方没出自定义组件的时候引入了摩拜小程序的wx-component插件(该插件可以自定义组件方案)。在小程序更新自定义组件后,详情页加载会偶现空白八九秒然后才会加载onLoad里面的方法,解决办法是暂时去掉插件,使用小程序官方的自定义组件(已解决)。
- 页面弹出自定义弹窗,底部内容会跟着滚动,首先想法是点击按钮显示自定义弹窗的时候设置page的wxss的定位为fixed,然后关掉的时候设为初始化。但是后来发现官方并没有给相关API直接操作Page。最后实现方法是:catchtouchmove="preventTouchMove" preventTouchMove为一个空方法。catchtouchmove是阻止事件滑动穿透。
- scroll-view隐藏滚动条(详见首页趣味兼职滑动)
实现代码
scroll-view.wxml:
<scroll-view class="recommend_scroll_x_box" scroll-x="true">
<view class="recommend_hot_box" wx:for="{{hotList}}">
<image src="{{item.pic}}" class="recommend_hot_image"></image>
</view>
</scroll-view>
scroll-view.wxss
.recommend_scroll_x_box {
height: 245rpx;
white-space: nowrap;
display: flex;
}
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.recommend_hot_box {
width: 230rpx;
height: 245rpx;
margin-right: 24rpx;
display: inline-block;
}
.recommend_hot_image {
width: 230rpx;
height: 143rpx;
}
scroll-view.js
Page({
data: {
hotList: [
{
pic: 'https://ojlf2aayk.qnssl.com//act/1501760559/banner.png'
}, {
pic: 'https://ojlf2aayk.qnssl.com//act/1501760559/banner.png'
}, {
pic: 'https://ojlf2aayk.qnssl.com//act/1501760559/banner.png'
}, {
pic: 'https://ojlf2aayk.qnssl.com//act/1501760559/banner.png'
}, {
pic: 'https://ojlf2aayk.qnssl.com//act/1501760559/banner.png'
}
]
}
完美解决
微信小程序下拉刷新在苹果机上 会上弹
原因是:onPullDownRefresh和showLoading或者showModel同时使用
然而很多需求是需要下拉刷新的时候顺便加载个showLoading。
现在的解决方法只能避免两个API同时出现,使用onPullDownRefresh的时候不使用showLoading或者showModel小程序获取二维码的问题
因为前端通过小程序官方提供的接口获取到的是个二进制的流,使用小程序自带的image标签是不能进行展示的,并且前端暴露AppID和AppSecret密钥不安全,所以这里的操作都是需要后端来进行的,此时交给后端进行转换并返回一张小程序码的图片给你。
前端只需要拿到后端返回图片的图片展示即可。现与后端约定:
我给页面page(第一,第三个接口是path)路径,以及需要的scene字段。
期间遇到的问题:scene字段是什么?我怎么拿到?
期间设想scene是扫码后才能拿到的场景值,注意是扫码后才会有,那我怎么生成小程序码,这里陷入一个误区了。
其实scene在这里你可以理解为传参数的一个字段。
⚠️第一个第三个接口是不需要传入scene字段的且字段为path
path: 'pages/index/index?jobId=111111' //此时字段是path
⚠️第二个接口是需要的且字段为page
scene: 'jobId=111111', //(string类型)
page: 'pages/index/index' //此时字段是page注意前面不要加'/'
⚠️生成的小程序码都是只能跳转线上的小程序页面,如果线上没有页面是跳转不了的,当然跳转后需要在我要跳转的页面onLoad里拿到scene字段然后进行逻辑操作
使用如下代码可以获取到二维码中的 scene 字段的值。调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 encodeUrl
Page({
onLoad: function(options) {
// options 中的 scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
var scene = decodeURIComponent(options.scene)
}
})
- 最坑的问题:
业务功能:详情页面 生成菊花码,并且带信息。(使用canvas画图)
canvas画图生成一张图片。把当前页面的信息拿到画到图上,然后使用wx.canvasToTempFilePath保存为图片,最后使用wx.saveImageToPhotosAlbum保存为图片
<view class="canvas-box" wx:if="{{isCanvas}}">
<canvas style="width: {{canvasWidth}}px; height: {{canvasHeight}}px; margin:0 auto;" canvas-id="mycanvas" />
</view>
//将title绘制到canvas的固定位置
setName(context) {
var unit = this.data.screenWidth / 375
var title = this.data.partJobVo.title
context.setFontSize(18)
context.setFillStyle('#333333')
context.save()
context.translate(24 * unit, 40 * unit) //必须先将旋转原点平移到文字位置
context.fillText(title, 0, 0) //必须为(0,0)原点
context.restore()
context.stroke()
},
//将金额绘制到canvas的固定
setMoney(context) {
var unit = this.data.screenWidth / 375
var money = this.data.partJobVo.salary
context.setFontSize(22)
context.setFillStyle('#FF2A52')
context.fillText(money, 24 * unit, 80 * unit)
context.stroke()
},
setQrcode(context) {
var unit = this.data.screenWidth / 375
let path = this.data.qtPath
context.drawImage(path, 200 * unit, 50 * unit, 120 * unit, 120 * unit)
},
//将canvas转换为图片保存到本地,然后将图片路径传给image图片的src
createNewImg() {
var that = this
var unit = this.data.screenWidth / 375
var context = wx.createCanvasContext('mycanvas')
var path = that.data.qtPaths
context.drawImage(path, 0, 0, 343 * unit, 220 * unit)
that.setMoney(context)
that.setName(context)
that.setQrcode(context)
//绘制图片
context.draw()
context.save()
//将生成好的图片保存到本地,需要延迟一会,绘制期间耗时
setTimeout(() => {
wx.canvasToTempFilePath({
canvasId: 'mycanvas',
success: function(res) {
wx.hideLoading()
that.setData({
imagePath: res.tempFilePath
})
},
fail: function(res) {
console.log(res.errMsg)
}
})
}, 2000)
},
savePhoto() {
var that = this
wx.showLoading({
title: '正在保存...',
mask: true
})
setTimeout(() => {
wx.saveImageToPhotosAlbum({
filePath: that.data.imagePath,
success(res) {
that.wetoast.toast({
title: '保存成功',
duration: 1500
})
setTimeout(() => {
wx.hideLoading()
that.setData({
isCanvas: false,
isXcxCode: false
})
}, 300)
},
fail() {
that.wetoast.toast({
title: '保存失败,请重试',
duration: 1500
})
setTimeout(() => {
wx.hideLoading()
that.setData({
isCanvas: false,
isXcxCode: false
})
}, 300)
}
})
}, 2000)
}
测试(IOS6.5.22有问题,安卓6.5.23有问题)其他版本都是好的,微信客户端的问题,后端也是可以截图的,但是因为暂时没有资源,最后导致砍掉这个功能。
其他坑,也有很多小的坑,就不一一说了。遇到了你可以来问下我,万一我早就遇到并解决了呢。(联系方式就留在最下边吧,毕竟还要打一波广告)
4、编译运行
1、模拟器可以在模拟器上看效果,上面讲到了运行底层不同,效果跟在手机上运行有些差异(感谢运营小姐姐的美图)
2.真机预览,点预览会生产一个二维码,用管理员微信号扫一扫就可以在真机上看实际效果
真机预览效果图
坑挺多的,不过业务前景还是挺不错的,小程序最近出了小游戏。看了一下源代码,头疼ing。
再推荐一波开源的demo:
微信小程序 - 从零开始制作一个跑步微信小程序 - 前端 - 掘金
微信小程序电影推荐 demo 实战开发小结(附源码及思维导图) - 前端 - 掘金
微信小程序 - 仿淘宝 - 前端 - 掘金
微信小程序 Demo - 仿知乎 - 前端 - 掘金
仿 「ONE · 一个」 微信小程序 - 前端 - 掘金
比较完整的微信小程序示例 - 知乎日报 - 前端 - 掘金
微信小程序实现 1024 小游戏 - 前端 - 掘金
一个干净的简单的微信小程序 Demo - 前端 - 掘金
微信小程序实战之 Gank 客户端 - 前端 - 掘金
微信小程序 cnode 社区版 - 前端 - 掘金
微信小程序 音乐播放器 真机测试 - 前端 - 掘金
最后重磅来了
青团社招聘:
招聘岗位:高级前端开发工程师P5及以上
坐标杭州市余杭区文一西路1380号金之源大厦11层
简历投递到:hr@qtshe.com||haochen@qtshe.com
职位描述:
1、建设工具、提炼组件、抽象框架,促进前端工程化、服务化,持续提升研发效率,保障线上产品质量
2、构建H5/PC应用基础设施,主导建设前端各种发布/监控等平台,指导落实解决方案
3、持续优化前端页面性能,维护前端代码规范,钻研各种前沿技术和创新交互,增强用户体验、开拓前端能力边界