微信小程序-认识 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 语法中的 viewimagetext 来搭建页面结构,绑定数据和交互处理函数。
比如 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 页面最终渲染出来的结构如下图所示:

 

下个课时我们详细讲解微信小程序各个组件的使用。