微信小程序,简称小程序,英文名Mini Program,是一种“不需要下载安装”即可使用的应用(实际上是需要下载安装的,只是整个过程被简化到可以让用户忽略的地步),它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。
小程序基于HTML5标记语言发展而来,微信团队将HTML、CSS、JS等语言的大量常用组件进行封装,形成了一套全新的开发语言。小程序已然形成了一个新的生态系统,官方公布了大量组件和开发API,如要系统的学习一遍必然费时费力,在实际的项目开发中,应该找出项目的基本要素,从这些基本要素入手,加快开发速度。比如最近公司有一个需求,写一个可以访问后台数据库数据的微信小程序,主要任务是用来浏览数据,基于安全原因写的操作只有在PC端才能执行。那么要实现这个一个主要是展示数据作用的小程序,对于从来没了解过小程序的小白来说,我总结了以下几点必然要实现的基本要素:
- 开发者认证以及开发者工具
- 小程序基本架构
- 常用组件如 Text、Butoon
- Page
- 数据库交互
要开发小程序,必须先进入小程序官网通注册成为小程序开发者,网址:
https://developers.weixin.qq.com/miniprogram/dev/
认证成功证明可以进行小程序的开发了,在菜单 “设置”-“开发设置” 看到小程序的 AppID。这个AppID非常重要,代表了一个开发者的ID,一个开发者只能有一个AppID,该开发者每发布、修改、升级一个小程序,都需要此APPID,见下图:
上图中还有一处配置服务器域名需要特别注意,任何与后台服务器的交互操作都需要经过微信的认证,比如我们公司的域名 http://www.action-prowave.com,我们在小程序中如果要对该域名的数据进行访问,则必须将该域名在上图地方进行申请,否则在访问中将会报错,提示“不信任的域名”。并且,小程序只支持https开头的域名,像我公司这种http开头的域名必须升级为https域名,否则也会报错。
完成小程序开发者认证后,下载小程序官方推出的开发工具既可进行小程序的开发,下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html?t=18112122
新建一个小程序,输入上文中申请的AppID(如不输入AppID也可以开发,但是该程序最终无法发布上线),创建一个QuickStart项目既可。见下图:
图中有四种格式的文件,分别是JSON、WXML、WXSS、JS。pages文件夹包含index和logs两个文件夹,pages表示页面,所有的页面都应该放到这里,比如index和logs,是两个不同的页面。
JSON 文件为小程序配置文件,可以配置小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。最外层的app.json为全局配置文件,内容如下:
{
"pages":[
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
}
}
pages:用于描述当前小程序所有页面路径,这是为了让微信客户端知道当前你的小程序页面定义在哪个目录,当前示例小程序有两个页面,index和logs。
window:定义小程序所有页面的顶部背景颜色,文字颜色定义等。
具体页面中可以复写这些默认设置,比如logs页面的logs.json文件:
{
"navigationBarTitleText": "查看启动日志"
}
那么index界面显示的头部标题为默认的 WeChat,而logs界面显示的头部标题为 查看启动日志。
WXML 是小程序的布局文件,所有的控件都在这里申明、绑定事件(事件的处理逻辑在js中)。类似网页编程中的HTML文件,而wxml原本就是从HTML演化而来,只是WXML封装了很多HTML标签的用法,使得开发更加的简洁高效。
比如定义一个布局,包含一个button组件和一个text组件:
{{motto}}
其中class表示布局及控件的属性定义,比如布局的宽、高、颜色、大小等等。class在WXSS文件中定义。bindtap表示绑定的事件,{{}}表示定义把一个变量绑定到界面上,也就是数据绑定,这些数据处理逻辑都在JS 文件中定义。
WXSS 样式文件由 CSS演化而来,WXSS 具有 CSS 大部分的特性,比如上文中的最外层布局 ,其定义在全局样式app.wxss文件中:
/**app.wxss**/
.container {
// 布局高度占满屏幕
height: 100%;
// 弹性布局
display: flex;
// 对不确定的宽和高,我们都可以让他垂直居中对齐
flex-direction: column;
align-items: center;
justify-content: space-between;
// 设置内边距,距离上边距200rpx,右边距0,下边距0,左边距0
padding: 200rpx 0;
// 允许以特定的方式定义匹配某个区域的特定元素
box-sizing: border-box;
}
也可以在当前所属页面的文件夹中(作用域)的wxss文件中的样式文件中定义.container样式,用以覆盖全局app.wxss中的样式。
wxml 定义了需要展示的控件,wxss 定义了布局该怎样展示,布局有了,还需要和用户的交互,这些逻辑处理都定义在 JS 文件中。
JS 即 JavaScript,同样的,小程序中的 JS 也对传统的 JS 进行了封装,但大致逻辑一致。例如:
展示一个 text 文本
WXML 文件
{{motto}}
WXSS 文件
.usermotto {
margin-top: 10px; // 距离上边距间隔10px
}
JS 文件
Page({
data: {
motto: 'Hello World' // 在界面上显示Hello World这行文本
},
展示一个 button 控件的点击事件
WXML 文件
{{motto}}
WXSS 文件
.test-settext {
margin-top: 50px; // 距离上边框间隔50px
color: rgb(194, 22, 22); // 颜色
}
.usermotto {
margin-top: 10px; // 距离上边距间隔10px
}
JS 文件
Page({
data: {
motto: 'Hello World' // 在界面上显示Hello World这行文本,该值为默认文本
},
// button控件绑定的事件
setText: function (e) {
this.setData({
motto: '改变了我' // 为motto这个text控件赋值
})
},
上文小程序目录结构图中可以看到根目录有一个 pages 的文件夹,该文件夹中又包含了index 和 logs 文件夹,这分别代表两个不同的页面,都包含 wxml、wxss、js文件。其中logs 里面还有一个 json 文件,json为项目配置文件,如非必要,可不必在每个 page 中都配置。
Page 相当于Android中的Activity,有其特有的生命周期函数。尝试创建一个 test 页面, 先在 pages 中创建一个文件夹 test , 然后在 test 中分别创建以下四个文件:
test.json 代码
{
"navigationBarTitleText": "测试页面"
}
test.wxml 代码
{{test}}
test.wxss 代码
/* pages/test/test.wxss */
.container-test {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
}
/* text-align: center; line-height: 500px; 居中*/
.show-ui {
width: 300px;
height: 500px;
border: solid 1px black;
display: flex;
align-items: center;
justify-content: center;
}
.test-return {
color: blue;
}
test.js 代码
// pages/test/test.js
Page({
/**
* 页面的初始数据
*/
data: {
test: '展示界面,点击返回'
},
/**
* button点击事件,返回上一页
*/
testReturn: function(e) {
wx.navigateBack({
changed: true
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log("test 生命周期函数--页面加载");
this.setData({
test: options.testKey + ',点击我返回上个界面'
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
console.log("test 生命周期函数--页面初次渲染完成");
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
console.log("test 生命周期函数--页面显示");
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
console.log("test 生命周期函数--页面隐藏");
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
console.log("test 生命周期函数--页面卸载");
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
console.log("test 页面用户下拉动作");
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
console.log("test 页面上拉触底事件");
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
console.log("test 頁面用户点击右上角分享");
}
})
最后,在 app.json 中配置页面路径:
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/test/test"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
}
}
对于任何类型的程序,和数据库的交互几乎是不可或缺的,小程序作为基于 html5 的网页类型程序,必然会和数据库有很频繁的交互。小程序封装了访问数据库接口 wx.request(Object object),并且对 JSON、GSON 数据有很好的支持,大大方便了我们对数据库的增、删、改、查操作。
这里以销量统计后台服务器(以前做好的后台)作为案例讲解,插入一条数据到数据库代码示例,在 pages 文件夹下新建 addData 文件夹,然后在 addData 中创建以下三个文件:
addData.wxml
addData.wxss
/* pages/databases/addData.wxss */
.container-test {
height: 100%;
width: 100%;
box-sizing: border-box;
}
.input-css {
margin: 5px;
width: 100%;
border: solid 1px #e2e2e2;
}
.add {
margin-top: 10px;
}
addData.js
// pages/databases/addData.js
Page({
/**
* 页面的初始数据
*/
data: {
baseband: '',
hwVer: '',
location: '',
macid: '',
manufacture: '',
meid: '',
model: '',
operator: '',
osVer: '',
pName: '',
sn: '',
sn2: '',
swVer: ''
},
basebandInput: function(e) {
this.setData({
baseband: e.detail.value
})
},
hwVerInput: function (e) {
this.setData({
hwVer: e.detail.value
})
},
locationInput: function (e) {
this.setData({
location: e.detail.value
})
},
macidInput: function (e) {
this.setData({
macid: e.detail.value
})
},
manufactureInput: function (e) {
this.setData({
manufacture: e.detail.value
})
},
meidInput: function (e) {
this.setData({
meid: e.detail.value
})
},
modelInput: function (e) {
this.setData({
model: e.detail.value
})
},
operatorInput: function (e) {
this.setData({
operator: e.detail.value
})
},
osVerInput: function (e) {
this.setData({
osVer: e.detail.value
})
},
pNameInput: function (e) {
this.setData({
pName: e.detail.value
})
},
snInput: function (e) {
this.setData({
sn: e.detail.value
})
},
sn2Input: function (e) {
this.setData({
sn2: e.detail.value
})
},
swVerInput: function (e) {
this.setData({
swVer: e.detail.value
})
},
addData: function (e) {
let params;
params = '{"_id":0' + ',"baseband":' +'"' + this.data.baseband + '"' + ',"hwVer":' + '"' + this.data.hwVer + '"' + ',"location":' + '"' + this.data.location + '"' + ',"macId":' + '"' + this.data.macid + '"' + ',"manufacture":' + '"' + this.data.manufacture + '"' + ',"meid":' + '"' + this.data.meid + '"' + ',"model":' + '"' + this.data.model + '"' + ',"operator":' + '"' + this.data.operator + '"' + ',"osVer":' + '"' + this.data.osVer + '"' + ',"pName":' + '"' + this.data.pName + '"' + ',"sn":' + '"' + this.data.sn + '"' + ',"sn2":' + '"' + this.data.sn2 + '"' + ',"swVer":' + '"' + this.data.swVer + '"' + '}';
console.log("input value: " + params);
wx.request({
url: 'http://www.action-prowave.com:端口号/服务器访问入口地址',
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: "POST",
data: {
json_params: params
},
success: function(res) {
console.log("success: " + res.data + ", result code: " + res.statusCode);
},
fail: function(res) {
console.log("fail: " + res.errMsg + ", result code: " + res.statusCode);
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
最后还需要配置在app.json文件中配置页面路径:
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/test/test",
"pages/databases/addData"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
}
}
可以看到,采用POST的方式请求,服务端插入一条数据的请求地址为 http://www.action-prowave.com:端口号/(省略), 所带的参数为用户在界面输入的值,测试可成功插入数据到后台服务器。
还是以销量统计后台服务器(以前写好的后台)作为案例讲解,查询出数据库中所有的数据。在 pages 文件夹下新建 queryData 文件夹,然后在 queryData 中创建以下三个文件:
queryData.wxml
{{deviceInfo}}
queryData.wxss
/* pages/databases/queryData.wxss */
.device-info {
margin-left: 10px;
margin-right: 10px;
}
queryData.js
// pages/databases/queryData.js
Page({
/**
* 页面的初始数据
*/
data: {
deviceInfo: '所有设备数据'
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var self = this;
wx.request({
url: 'http://www.action-prowave.com:端口号/访问服务器入口地址',
data: {
},
header: {
'content-type': 'application/json' // 默认为json格式
},
method: 'POST',
success: function(res) {
console.log("success: " + JSON.stringify(res.data));
if (res.statusCode == 200) {
self.setData({
deviceInfo: JSON.stringify(res.data) // 由于我的后台数据设计的就是gson数据格式,这里将将获取到的gson数据转换成String类型,以便显示
})
}
},
fail: function(res) {
console.log("fail: " + res.errMsg);
}
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
最后在app.json文件中配置页面路径:
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/test/test",
"pages/databases/addData",
"pages/databases/queryData"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
}
}
服务器读取所有数据访问地址为 http://www.action-prowave.com:端口号/(省略), 也是采用POST方式请求,并且,服务器端的数据格式当初设计的是一个数据集合,返回一个List,并转换成了 GSON 格式数据返回,这种类型的数据不能直接显示。小程序为 JSON、GSON 格式数据提供了很好的支持,只需要调用 JSON.stringify(Object obj) 既可将格式化的集合类型数据转换成字符串。 当然也可以将字符串转换成 JSON 数据,也可以和 Map 等数据格式的集合类型数据进行互相转换。
由于我有一点网页编程的基础,这个小需求最终在1.5天搞定。针对一个全新领域的开发,实战是最好的老师。在需求下来后,切忌盲目折腾,提前制定好针对性的目标也很重要。
http 开头的域名不能在小程序服务器中声明认证,该域名只有在测试的时候可临时访问,真正发布上线版本后没有在小程序服务器中认证的域名将无法访问,必须将服务器域名升级为 https, 并且登录小程序官网,在 开发设置-服务器域名 中配置好域名。