对于用户而言,小程序的直观表现只是多个相互关联的页面。我们的小程序Hello WXapplet也一样,它由2个相互关联的页面构成:首页(index)与信息页(logs)。点击首页的头像就切换到信息页,在信息页点击“返回”可以再返回到首页,如图2-1所示。
图2-1 两个相互关联的页面
小程序的开发实际上就是实现这些页面的展示(视图),以及实现“页面上用户交互事件”、“页面间切换逻辑”、“数据存储及网络调用”等事务与逻辑处理的过程。
我们先从文件目录结构上来了解Hello WXapplet项目的构成。点击开发者工具左侧导航的“编辑”,我们可以看到Hello WXapplet项目的目录结构及包含文件如图2-2所示。
图2-2 Hello WXapplet项目的目录结构
目录结构显示:小程序项目在创建时的目录(示例的本地开发目录为:“D:\小程序巧应用”)之下,包含3个app开头的文件(app.js、app.json、app.wxss)以及pages目录与utils目录。其中pages目录存放2个页面(index与logs)的构成文件。从示例中,我们看到:每个页面都是一个目录,目录名就是唯一的页面名,其下再由以页面名为前缀的2~4个文件组成。
对小程序的目录文件结构,我们可以归纳一下,如图2-3所示。
图2-3 小程序的目录文件结构
左侧3个app文件必须放在小程序根目录下,其他文件则由开发者自由控制。这3个文件说明如下:
·app.js是小程序的脚本代码,用来监听并处理小程序的生命周期函数、声明全局变量。
·app.json是对整个小程序的全局配置,配置小程序是由哪些页面组成,配置小程序的窗口背景色等。
·app.wxss是整个小程序的公共样式表。
其中app.js和app.json是必需的。
小程序页面是由同路径下同名但不同后缀的2~4个文件组成:
·.js后缀的文件是页面脚本文件,该文件实现页面逻辑与事件处理。
·.json后缀的文件是页面配置文件。
·.wxss后缀的是页面样式表文件。
·.wxml后缀的文件是页面结构文件,该文件与.wxss文件一起构建出页面。
其中.js与.wxml文件是必需的。
我们再从代码层面了解Hello WXapplet项目的实现,根据小程序的目录结构,代码实现有两大部分——小程序实例与页面,下面分别介绍。
1.小程序实例的代码实现
在Web开发者工具的“编辑”模式下,我们来看Hello WXapplet这个项目里的代码文件,最关键也必不可少的是app.js、app.json、app.wxss这三个文件,分别代表脚本文件、配置文件、样式表文件。微信小程序运行时会读取这些文件,并生成小程序实例。
app.js是小程序的脚本代码,我们一般在这个文件中监听并处理小程序的生命周期函数、声明全局变量、调用框架提供的丰富的API等。如本例app.js文件中,我们调用了的同步存储及同步读取本地数据的API——wx.setStorageSync()及wx.getStorageSync()。代码见程序清单2-1。想了解更多可用API,可参考本书第5章“API接口的开发应用”。
程序清单2-1 小程序app.js
// app.js
App({
onLaunch: function () {
// 调用API从本地缓存中获取数据
// 定义一个数组变量logs
var logs = wx.getStorageSync('logs') || []
// 在数组logs的集合开头插入一个元素,值为当前时间
logs.unshift(Date.now())
// 将数组logs写入本地名为logs缓存块中
wx.setStorageSync('logs', logs)
},
// 定义一个后面index.js中会调用到的函数,参数cb意为callback,即回调函数*
getUserInfo:function(cb){
var that = this;
if(this.globalData.userInfo){
// 判断cb是否存在且为function类型,若是则传进参数调用cb
typeof cb == "function" && cb(this.globalData.userInfo)
}else{
// 调用登录接口
wx.login({
success: function () {
wx.getUserInfo({
success: function (res) {
that.globalData.userInfo = res.userInfo;
typeof cb == "function" && cb(that.globalData.userInfo)
}
})
}
});
}
},
globalData:{
userInfo:null
}
})
注意
没有编程基础的读者很容易被上述代码中的“回调函数”打蒙圈,这种情况建议先百度或Google了解一下“回调函数”的概念。
app.json配置文件是对整个小程序的全局配置,代码见程序清单2-2。开发者将在这个文件中配置小程序是由哪些页面组成、配置小程序的窗口背景色、配置导航条样式、配置默认标题等。
注意
app.json不可添加任何注释。
更多关于小程序的全局配置项可参考本书3.1节。
程序清单2-2 小程序app.json
{
"pages":[
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
}
}
app.wxss样式表文件是整个小程序的公共样式表,代码见程序清单2-3。我们可以在页面“组件”的class属性上直接使用app.wxss中声明的样式规则。
关于.wxss文件中的样式规则可参考本书3.3.2节“wxss详解”。
程序清单2-3 小程序app.wxss
/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
app.wxss文件中上述程序代码定义了一个名为container的样式。
2.小程序页面的代码实现
在我们创建的Hello WXapplet小程序项目中,包含两个页面——index页面和logs页面,即首页和小程序启动日志的展示信息页,它们都在pages目录下。
每一个小程序页面可由同路径下同名的2~4个不同后缀文件组成,如:Hello WXapplet小程序的index页面就由pages目录下index路径中index.js、index.wxml、index.wxss、index.json四个文件组成,它们分别是页面脚本文件、页面结构文件、页面样式表文件、页面配置文件。其中,.wxml与.js文件是页面所必需的,而.wxss与.json文件则是可选的。
注意
微信小程序中的每一个页面的“路径+页面名”都需要写在app.json的pages中,且pages中的第一个页面是小程序的首页。
.wxml文件与.wxss文件作为小程序开发框架的一部分,我们在本章后面会详细介绍。
下面分别介绍index页面与logs页面。
index.wxml是页面的结构文件,代码见程序清单2-4。
程序清单2-4 页面结构index.wxml
<!--index.wxml-->
<!--下一行代码使用view组件,构建样式为container的视图块-->
<view class="container">
<!--下一行代码使用view组件,构建样式为userinfo的视图块,并绑定tap事件到
bindViewTap事件处理函数,该事件处理函数在页面js文件定义-->
<view bindtap="bindViewTap" class="userinfo">
<!--下一行代码使用image组件,放置样式userinfo-avatar的图片,图片地址为变量
userInfo.avatarUrl。形如{{var}}的变量,即双大括号包含的动态数据变量,其值
来自页面js文件中的data对象 -->
<image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-
size="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</view>
<view class="usermotto">
<text class="user-motto">{{motto}}</text>
</view>
</view>
index.wxml文件中使用了<view/>、<image/>、<text/>这三个组件来搭建页面结构,绑定数据和交互处理函数。有关页面组件的详细使用可以参考本书第4章。
index.js是页面的脚本文件,代码见程序清单2-5。我们在这个文件中监听并处理页面的生命周期函数~onLoad(),获取小程序实例~getApp(),声明并处理数据,响应页面交互事件等。
程序清单2-5 页面脚本index.js
// index.js
// 获取应用实例
var app = getApp()
Page({
data: {
motto: 'Hello World',
userInfo: {}
},
// 事件处理函数
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad: function () {
console.log('onLoad')
var that = this
// 调用应用实例的方法获取全局数据
app.getUserInfo(function(userInfo){
// 更新数据
that.setData({
userInfo:userInfo
})
})
}
})
从上面的代码我们可以知道,.js文件是页面逻辑处理层。详细的逻辑层编码请参考本书3.2节。
index.wxss是页面的样式表文件,代码见程序清单2-6。
程序清单2-6 页面样式index.wxss
// index.wxss
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
}
.userinfo-avatar {
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
.userinfo-nickname {
color: #aaa;
}
.usermotto {
margin-top: 200px;
}
页面的样式表文件是非必要的。当页面有样式表文件时,文件中的样式规则会层叠覆盖app.wxss中的样式规则。否则,即使没有页面样式表文件,我们也可以在页面的结构文件中直接使用app.wxss中指定的样式规则。
index.json是页面的配置文件。页面的配置文件同样是非必要的,而且只能配置window配置项的属性值。当页面有配置文件时,文件中的配置项在该页面上会覆盖app.json的window中相同的配置项。若没有指定页面的配置文件,则在该页面直接使用app.json中的默认配置项。更多配置文件编写与解析,可参考本书3.1节。
接下来我们再看看logs页面。logs.wxml是logs页面的结构文件,代码见程序清单2-7。
程序清单2-7 页面结构logs.wxml
<!--logs.wxml-->
<view class="container log-list">
<!--以for循环的方式绑定数据,并指定数组元素的变量名为log-->
<block wx:for="{{logs}}" wx:for-item="log">
<!--数组当前元素下标变量名默认为index-->
<text class="log-item">{{index + 1}}. {{log}}</text>
</block>
</view>
logs页面使用<block/>控制标签来组织代码,在<block/>上使用控制属性wx:for绑定logs数据,并将logs数据循环展开节点。
注意
上述代码中的<block/>并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性(如wx:for或wx:if)。
logs.js是logs页面的脚本文件,代码见程序清单2-8。
程序清单2-8 页面脚本logs.js
// logs.js
var util = require('../../utils/util.js')
Page({
data: {
logs: []
},
onLoad: function () {
this.setData({
// 对于logs数组的每个元素使用map方法:即调用匿名回调函数并返回包含结果的数组
logs: (wx.getStorageSync('logs') || []).map(function (log) {
return util.formatTime(new Date(log))
})
})
}
})
上述脚本文件代码中,使用了require()来引入模块化.js脚本文件。关于模块化代码,可参考本书3.2.3节。