写在前面
笔者专注于前端,也写过php,node,对其它语言也有关注,认为计算机语言存在互通性,学习过程要学会举一反三,了解其中底层原理更为重要。在工作外,也尝试开发过几款个人小程序(个人充当设计,前端,后端的角色)。之所以用《人人都会小程序开发》为题,原因有三:
一是,小程序生态如一场细雨,2年多时间,已经润物细无声,人们已经在生活中无时无刻离不开了小程序(出行、点餐、电影、快递、机票等各个场景)。这里小程序生态涵盖了(微信小程序,支付宝小程序,百度小程序,头条小程序,快应用等一系列小程序),其中微信小程序市场占比最为明显,触达的用户更多,本文主要以微信小程序为例,介绍如何快速上手小程序开发(其余小程序可以举一反三,底层原理大同小异,了解语法即可快速上手)。
二是,小程序较传统web而言,学习成本要低很多,其封装了常用的组件和API,只要了解其基本语法和运行原理之后,便可快速上手(在此之前对Javascript、css、html稍作了解即可)。
三是,笔者从小程序发布内测消息开始,便作为第一批内测用户,参与了小程序开发,深深被其简洁的语法,高效的开发方式,良好的使用体验所吸引(虽然当时的生态和基础建设和现在相比较,还存在着很大的优化空间)。两年多时间,开发过数十款大大小小的小程序,积累过不少开发经验,也踩过不少的坑,把这些经验记录下来,以供后来学习者作参考,也欢迎一起交流进步。
何为小程序
2016年9月前后,业界流传了一张微信之父张小龙对于小程序描述的截图,原文如下:
什么是小程序:小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用。也体现了“用完即走”的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。
在小程序开发文档的第一篇,小程序简介中,这样描述:
小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。
在小程序技术发展史上,小程序并非一个全新的概念,它是当微信内WebView成为其移动Web的一个重要入口时,解决移动网页通用问题的一种更优解决方案(个人理解Google提出的PWA也是为了解决类似问题)。
曾经为了解决这样的问题,也尝试过“微信Web资源离线存储”,与预期体验还是存在差距,这里不作深入延伸。
于是期望有一个全新的系统,来让所有的开发者或使用者拥有以下特性:
快速的加载
更强大的能力
原生的体验
易用且安全的微信数据开放
高效和简单的开发
这时,小程序应运而生。
如何快速上手小程序
开发前须知
由于标题为《人人都会小程序开发》,所以文章的写作顺序是由浅入深,同时也期望读者的学习过程也能够是渐进式的,即先了解到如何开发,再有需求对性能优化,运行原理作深入了解时,再去探究更深层次的原理,这样会让学习兴趣和学习效率大大提升。文章是结合笔者开发经验,对快速掌握小程序开发,对官方文档进行了提炼,其中会加入一些自己的看法。之后还是希望读者熟读小程序文档。
以下假使你已经申请好了小程序开发账号,并下载好了开发者工具。让我们开始了解如何开发小程序。初次开发小程序,不太建议使用市场上任何一款小程序框架。原因有四:
框架新特性滞后性:小程序迭代速度非常之快,小程序团队正以小步快跑的方式优化其能力和体验,相信市场上任何一款框架也没法做到及时更新,以满足新能力。这里可以打个比方,小程序官方比做ECMAScript 制定者,而小程序框架维护团队比作浏览器对ECMA规范的实现,理所当然是存在一定滞后性的。
风险不可控:引用第三方的框架,在软件开发中的一个通病是,在不了解其底层机制和原理时,出现了预期外的问题,可能只能买个彩票,等待开奖。
团队学习成本:意味着团队交接或者新成员加入,必须学习框架的独有特性和语法(这里是肯定存在的,若和官方使用没有差异的话,其实不应该独立称为框架),免不了增加学习成本。
小程序生态日益丰富:市面上几大小程序框架,最初无非是为了解决小程序开发以下几个问题:
开发低效。框架提供了当前流行的Vue,React等类似的语法糖,开发效率更加高效。这里是相对而言,毕竟互联网公司追求的是效率是第一生产力,自然是越快越好。其实目前来说小程序相比传统网页的开发效率已经高了很多。
某些能力不满足。比如npm,组件化开发,这里会存在引入外部库不方便、复用效率低,代码维护分散等问题。
但目前的小程序生态已经日益丰富,以上的很多诟病已经不是问题。
综上考虑,建议直接使用原生语法进行开发,若团队对代码质量、格式有一定规范,可考虑结合Webpack、gulp等搭建一套脚手架。
代码结构
.json 后缀的 JSON 配置文件
.wxml 后缀的 WXML 模板文件
.wxss 后缀的 WXSS 样式文件
.js 后缀的 JS 脚本逻辑文件
JSON 配置: 小程序或页面的静态配置,具体参见。
app.json:当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。大致如下:
{
"pages": ["pages/index/index", "pages/logs/logs"],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
}
}
工具配置 project.config.json:个性化配置,例如界面颜色、编译配置等等。
页面配置 page.json:独立定义每个页面的一些属性。
需要注意的是 JSON 文件中无法使用注释,试图添加注释将会引发报错。
WXML 模板
可以理解为传统Web的HTML,描述页面的结构。WXML也由标签、属性等等构成。需要注意以下几点:
标签名不完全一样。如HTML的div->WXML的view等,这里按照文档去写就行。
多了一些语法糖,可快速实现传统HTML元素隐藏,文本展示等。
小程序整体设计模式采用MVVM(与当代前端三大MVVM框架大同小异):
M: model(模型)
V:view(视图)
MV: model-view(模型-视图驱动器)
WXSS 样式
WXSS 具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改。
新增了尺寸单位rpx。自适应各种机型设备,开发者无需关心适配问题。
提供了全局的样式和局部样式。app.wxss为全局生效的样式,可以理解为传统网页的common.css。每个页面结构下的page.wxss会覆盖app.wxss的样式。
wxss只是css的子集。部分高级特性可能不支持。
JS 逻辑交互
和传统web事件响应机制类似,产品的交互行为由.js文件承载,并需要在page()里定义,并且小程序底层提供了丰富的API,可以很方便调起微信提供的能力。
天生的异步机制
依赖于微信客户端宿主环境和小程序运行机制,JS逻辑运行在客户端的JsCore,页面渲染运行在Webview,之间通信需要经过客户端Native的转发,所以多数 API 的回调都是异步。
注册小程序和页面
注册小程序:在根目录下的app.js下定义App实例,小程序启动之后App中的定义生命周期函数会在对应生命周期被执行。注册页面:与App实例类似,需再每个页面下定义Page实例,Page也拥有其data属性、生命周期函数、事件等。
组件
官方基于常见的交互,封装了许多常用的UI组件,可以让开发者更聚焦于业务和功能开发。组件提供了一些常用属性,以应对不同的业务场景,如在页面内引入日期选择组件,只需如下代码:
mode="date"
value="{{date}}"
start="2015-09-01"
end="2017-09-01"
bindchange="bindDateChange"
>
class="picker">
当前选择: {{date}}
API
为了让开发者可以很方便的调起微信提供的能力,例如获取用户信息、微信支付等等,小程序提供了很多 API 给开发者去使用。如调用微信扫一扫功能:
wx.scanCode({
success: (res) => {
console.log(res)
}
})
以上只是对小程序基础特性的一个介绍,还有一些如运行环境、更新机制、运行机制可以参照官方文档,不再此赘述。
开始开发
在此之前普及下一些基本概念,日常小程序开发中可能用到:
自定义组件:复用性高的交互模块,可以考虑以组件的形式封装,提高复用性和开发效率,但同时也需注意低版本兼容问题.
低版本兼容:某些API是在迭代中新增的,故和小程序版本号与客户端版本有关,故在使用前需进行兼容性判断(如VUE不兼容IE8以下,因为VUE基于Object.defineProperty)。
插件:程序基础库版本 1.9.6 开始支持。适合用来封装自己的功能或服务,提供给第三方小程序进行展示和使用,之前有沉淀过相关文章,点击查看。
场景值:可以区分小程序是从哪个场景打开的,如群聊会话卡片,发现栏小程序tab入口,以便做精细化运营或数据区分。
WXS:WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。比如对金额封装展示函数等场景。
从构建Demo开始
打开开发工具,选择一个空的目录,勾选"创建 QuickStart 项目",为项目取一个名字,填写申请到的AppID(若还没申请到),可以暂时选择用官方提供的测试号。点击确定,即可打开小程序开发界面。
可以看到项目结构如下:
打开app.json,可以看到代码结构类似以下:
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json"
}
pages定义了小程序的所有页面路径,window定义了导航栏颜色,文字等属性。
打开index.js,类似如下代码:
//index.js
//获取应用实例
const app = getApp()
Page({
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
//事件处理函数
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad: function () {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse){
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
},
getUserInfo: function(e) {
console.log(e)
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
}
})
常见页面分为三个组成部分,详见:
data: 页面数据,可由MVVM驱动
事件处理函数:如这里的bindViewTap,getUserInfo,可以相应页面的交互,也可以在Page定义的函数中互相调用。
生命周期函数:常用有——onLoad(页面加载时触发,可以从query字段中拿到页面参数)、onShow(页面显示时触发)、onShareAppMessage(用户点击转发或分享时触发)
打开index.wxml,可看到类似如下代码:
class="container">
class="userinfo">
wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称
wx:else>
bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover">
class="userinfo-nickname">{{userInfo.nickName}}
class="usermotto">
class="user-motto">{{motto}}
这里的view、button、image组件都是官方提供的组件,view可以理解为一个盒子模型用于承载内容,button是一个按钮。
这里的button的open-type表明了这是一个获取用户属性的行为,可以在bindgetuserinfo里触发getUserInfo函数。这里的image绑定了一个bindViewTap点击事件,当用户点击这个图片时则会触发bindViewTap事件。定义在index.js里的bindViewTap方法会触发对应的逻辑。
这里的wx:if是官方提供的语法糖,用来动态展示或者隐藏模块,比如这里的wx:if="{{!hasUserInfo && canIUse}}"是当用户数据不为空且canIUse为true时才展示。这里的{{motto}}则会把data中定义的motto字段显示出来,且跟随motto值的变更,动态更新视图(这里小程序底层做了由数据驱动视图更新的事情)。
setData是官方提供的更新数据的方法,不要视图直接用this.motto = 'xxx'去更新,这样视图不会得到同步更新。而应该采用
this.setData({
motto: 'xxx'
})
这里需注意几点:
避免过频过大调用setData(因为底层需要通信)
不要对data设置undefined
wx:for,是用于渲染重复模块的语法糖,适合如列表渲染等场景,详情参见
以上就是一个小型页面的渲染和构建过程。
尝试使用官方提供的API
在不同业务场景下,往往需要使用底层的一些能力,如地理位置、系统信息、绘图等,详见。以下为地理位置获取(某些API需要向用户授权):
wx.getLocation({
type: 'wgs84',
success(res) {
const latitude = res.latitude
const longitude = res.longitude
const speed = res.speed
const accuracy = res.accuracy
}
})
某些时候还需要将用户某些数据(非敏感数据)存在本地缓存(因为web是无状态的),便可以利用本地缓存API:
try {
wx.setStorageSync('key', 'value')
} catch (e) { }
又如绘图的场景,需要用到canvas能力,可参见写的这篇文章小程序canvas的那些事。
往往的交互还需要通过网络从服务端获取数据,则需要发起网络请求,大多时候业务是需要检验用户身份的(即发请求时需要带登录态),这里可以考虑用同事封装的请求库(比较优雅的封装了登录模块),业务开发者可以聚焦于业务开发,而不用花太多精力在登录上,github传送门。
复杂的业务场景
服务端支持
更复杂的业务场景就需要后端服务,包括登录服务,用户信息,客服消息等可以参见文档。
云开发
若你对后端开发不熟悉,还可以利用小程序团队提供的云开发,让你不用了解太多后端知识,只需掌握JS则可以搭建自己后台服务。
总结
以上只是对如何快速上手开发小程序的一个缩影,具体深入的开发还需要结合文档进行实践,可以参考之前个人尝试的小程序经验沉淀,文章链接。后续会从小程序的设计、后端等多方面进行介绍。