微信小程序-认识 MINA 框架-新手教程2
MINA(MINA IS NOT APP) 是在微信中开发小程序的框架,其目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生体验的服务——官方文档。
什么是开发框架?首先,大家不要被“框架”吓到。在程序开发领域,“框架”这个概念可以这么理解:一组便于开发某类特定程序的工具的集合。具体到 MINA 这个框架,MINA 其实就是”一组便于开发微信小程序的工具的集合“,而这个集合到底哪些工具组成,正是我们这一节课要讲到的内容。
手机 APP 的构成在深入讲解技术细节之前,大家不妨思考一下,我们日常使用的手机 APP 的基本构成单位是什么。
结论:手机 APP 由三部分构成:数据逻辑,页面(数据呈现)以及(页面间的)导航。
比如我们拿新浪微博 APP 举例:
数据逻辑:我关注的人,我的粉丝,微博用户所发的微博,以及这些微博数据的组织,比如按关注人分组等等这类跟数据相关的部分。
页面:对数据的呈现,比如我关注的人的微博列表,某个用户的微博主页,长微博文章页等等。
导航:页面间的跳转。比如从微博列表页面可以进入到微博详情页,点击微博发帖人头像可以进入个人主页等等。
微信小程序的构成微信小程序主要由两部分构成:
- 响应的数据绑定(数据逻辑 + 页面)
- 页面管理(导航)
稍有不同的是,微信的 MINA 框架把数据逻辑和页面这两部分合起来组成了“响应的数据绑定系统”。顾名思义,这个系统实现了数据和页面的绑定,也就是说,当数据变动的时候,渲染这部分数据的页面也会随之变化。另外,“页面”这个概念也常常被称做“视图”,我们后续课程可能会交叉使用这两个概念,但指代的都是同一个东西。下面让我们深入探讨一下微信小程序的这两个组成部分。
1. 响应的数据绑定
响应的数据绑定系统包含了数据逻辑和页面两个部分。
首先,对于页面部分,MINA 框架提供了自己的视图层描述语言 WXML 和 WXSS。
其中 WXML 跟 HTML 语言(一种用于定义网页的标记语言)很像,用于定义视图结构。那么什么是视图结构呢,举个例子,比如我想做一个页面,这个页面的上半部分要放一张图片,然后图片下面要放一个按钮,这种相对结构关系就是用 WXML 语言来描述和定义的。这类文件均以 .wxml 为后缀名。
而 WXSS 则跟 CSS(一种用于给 HTML 增加样式的语言) 很像,用于定义视图中元素的样式。比如上述的页面,如果我们想要让那张图片有一个 5px 宽的黑色的边(border),或者下面的按钮你想换成绿色并给一个 2px 的圆角(border-radius),那么这类样式相关的定义就是通过 WXSS 定义的。这类文件均以 .wxss 为后缀名。WXSS 其实是 CSS 的一个超集,也就是说 CSS 支持的功能它都有,另外它还实现了一些 CSS 没有的功能,详细细节我们将在后续的课程中探讨。
对于数据逻辑部分,MINA 提供了基于 JavaScript 的逻辑层框架,并在视图层和逻辑层之间提供了数据传输和事件系统。数据逻辑部分的代码都是以 JavaScript 写的,后缀名为 .js。
2. 页面管理
MINA管理了整个小程序的页面路由,可以做到页面间的无缝切换,并给以页面完整的生命周期。开发者需要做的只是将页面的数据,方法,生命周期函数注册进MINA中,其他的一切复杂的操作都交由MINA处理。——官方文档
所谓的页面管理,指的就是下面这两个部分:
- 页面间的切换
- 页面生命周期
第一部分:页面间的切换。这个很好理解,就是页面间的转换,比如点击一个按钮跳到另一个页面,发现某种数据变化之后跳转到另一个页面等等。
第二部分:页面的生命周期,我们可以这么理解,一个页面从开始出现,到渲染完成,及至最后的消失,这整个过程就是页面的生命周期。MINA 的生命周期机制,会在页面整个生命历程的不同阶段触发特定的事件,通过响应这类事件,我们就可以实现丰富的页面功能。比如,我做了一个微信抽奖页面,我想在用户打开这个页面之后给服务器发送一个请求,用于统计打开这个页面的人数,这个时候我们就可以通过自定义这个页面的 onLoad 事件(也叫监听 onLoad 事件),来完成相应的功能。再比如,当用户关掉某个页面的时候我想要给服务器发另一个请求,用于统计离开人数。这时候我们就可以通过 onHide 事件来完成这个功能的接入。
官方示例理论知识讲完了,让我们看一下官方的示例:
首先下载官方开发工具:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html
安装并打开开发者工具,扫码登录,然后点击➕ 加号新建项目,其中 APPID 可以选择无 APPID,然后填写项目名称,项目目录选择一个空的文件夹,勾选“是否需要创建 quick start 项目”,最后点击“添加项目”就会创建官方的示例项目。
官方示例项目运行截图:
这个示例项目由两个页面构成: index 页,显示当前用户的头像和昵称,并在最下面显示一行 Hello World。点击 index 页的用户头像和昵称部分,会跳到 logs 页,这个页面会显示小程序每次被打开的时间戳列表。logs 页面效果如下图所示:
项目代码结构如下图所示
最外层的三个名为 app 的文件是整个项目的入口,配置以及样式文件。
其中, app.js是小程序的脚本代码。
我们可以在这个文件中监听并处理小程序的生命周期函数、声明全局变量。调用框架提供的丰富的 API,如本例的同步存储及同步读取本地数据。
//app.jsApp({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync(‘logs’) || [] logs.unshift(Date.now()) wx.setStorageSync(‘logs’, logs) }, getUserInfo:function(cb){ var that = this; if(this.globalData.userInfo){ 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 }})
首先在代码的最下面我们看到,实例化 App 的时候创建了一个名为 globalData 的全局变量。
得益于 MINA 框架的生命周期机制,app.js 监听了程序启动之后的 onLaunch 事件,并在响应的代码中做了一件事情,那就是把当前时间通过 setStorageSync 接口存储起来,在下面的 logs 页面我们将看到对这部分数据的使用。
getUserInfo 则是自定义的一个方法,这个方法的功能是获取微信用户的基本信息,并把它存在全局变量 globalData 中去。
app.json 是对整个小程序的全局配置。我们可以在这个文件中配置小程序是由哪些页面组成,配置小程序的窗口背景色,配置导航条样式,配置默认标题。
{ “pages”:[ “pages/index/index”, “pages/logs/logs” ], “window”:{ “backgroundTextStyle”:”light”, “navigationBarBackgroundColor”: “#fff”, “navigationBarTitleText”: “WeChat”, “navigationBarTextStyle”:”black” }}
app.wxss 是整个小程序的公共样式表,也可以理解为基础样式。我们可以在页面组件的 class 属性上直接使用 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;}
紧接着,utils 文件夹则是公用的工具函数,这个文件包含了一个名为 formatTime 的函数,在接下来的 logs 页面,我们可以看到 logs.js 这个文件对 utils 中的函数的使用。
最后,pages 文件夹包含了项目包含的页面,内部的 index 和 logs 两个文件夹分别表示两个不同的页面。其中每个文件夹内部都有同名的 js,wxml,wxss 文件,分别对应我们之前讲到的数据逻辑部分和视图部分。其中 json 文件是页面的配置文件。
index 页面结构index.wxml 是页面的结构文件,页面使用了 WXML 语法中的 view、image、text 来搭建页面结构,绑定数据和交互处理函数。
比如 userinfo 这个 view 上就绑定了 tap 事件,事件触发后对应的处理函数则是 bindViewTap。
<!–index.wxml–><view class=”container”> <view bindtap=”bindViewTap” class=”userinfo”> <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.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 }) }) }})
index.js 做的事情是实例化了一个 Page,也就是一个页面。其中 data 对应的是这个页面所绑定的动态数据。bindViewTap 则是上面 index.wxml 中的所绑定的 tap 事件对应的处理函数。
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 是页面的配置文件,页面的配置文件是非必要的。当有页面的配置文件时,配置项在该页面会覆盖 app.json 的 window 中相同的配置项。如果没有指定的页面配置文件,则在该页面直接使用 app.json 中的默认配置。
index 页面最终渲染出来的结构入下图所示:
logs 页面的页面结构
<!–logs.wxml–><view class=”container log-list”> <block wx:for-items=”{{logs}}” wx:for-item=”log”> <text class=”log-item”>{{index + 1}}. {{log}}</text> </block></view>
logs 页面使用 block 控制标签来组织代码,在 block 上使用 wx:for-items 绑定 logs 数据,并将 logs 数据循环展开节点。block 和 wx:for-items 的具体用法将在下面的课程中详细介绍。
//logs.jsvar util = require(‘../../utils/util.js’)Page({ data: { logs: [] }, onLoad: function () { this.setData({ logs: (wx.getStorageSync(‘logs’) || []).map(function (log) { return util.formatTime(new Date(log)) }) }) }})
logs 页面最终渲染出来的结构如下图所示:
下个课时我们详细讲解微信小程序各个组件的使用。