现学现卖微信小程序开发(一)

现学现卖微信小程序开发(一)
现学现卖微信小程序开发(二)
现学现卖微信小程序开发(三):引入Rx,为小程序插上翅膀

现学现卖微信小程序开发

很早就注册了微信小程序内测,因为是拖延症晚期患者,所以一直都没有学习。直到昨天(2016年12月28日),张小龙童鞋宣布微信小程序2017年1月9日正式上线,满屏都是小程序的消息。我这时才想起来,我不能白交了300块钱啊,得学啊。

反正学习也得总结,倒不如现学现卖写一个教程吧。话说回来现学现卖肯定有不少错误,大家多包涵哈。废话少说,我们开工,首先下载微信小程序开发工具 https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html 选择自己的平台下载安装即可。

微信小程序开发工具下载页面
微信小程序开发工具下载页面

注册小程序号的过程我就不讲了(网上这类教程一堆,不用我花时间在这里。),你要开发的话,最好通过公司申请一个。第一次启动开发工具时需要扫码,然后选择添加一个无appId的项目。做个什么项目好呢,没想好,那就还是先把Todo牵出来吧。我们来实现一个微信小程序版的Todo,同样是带HTTP后台的版本。为什么又选择Todo呢?因为它的逻辑相对完整,增删改查都有,HTTP请求、返回俱全。在一个平台折腾明白这个app就基本可以上手干活了,这货简直就是新时代的Hello World啊。

文件结构

新建工程时,默认开发工具会生成一个样例给我们。我们就来看看这个样例是什么东东。开发工具的左侧是一个预览,中间那栏是文件目录,看目录结构倒是蛮简单:

开发工具自动帮助生成了一个样本工程
开发工具自动帮助生成了一个样本工程

全局文件

先来看一下根目录:这个目录下有三个文件app.js,app.json和app.wxss。app.js是应用入口,里面定义了App() 函数,用来注册一个小程序,就像Android里面的Application和Angular2中的。接受一个 object 参数,其指定小程序的生命周期函数等。

//app.js
App({
  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()本身就是一个函数,在其他页面可以通过系统提供的getApp()来得到应用的实例。
  • onLaunch就是一个生命周期函数,是当小程序初始化完成时,会触发这个函数(只触发一次)。这里在应用初始化时向本地存储写入了一个日期。
  • globalData是个全局数据的存放区域,示例代码中只放置了一个userInfo作为全局变量。
  • 例子代码在这里定义了一个全局方法getUserInfo,这个全局方法的访问可以通过getApp().getUserInfo()。如果你打开 pages/index/index.js,你会发现示例代码通过getApp()取得应用实例后调用了getUserInfo这个全局方法
//获取应用实例
const app = getApp()
...
//调用应用实例的方法获取全局数据
app.getUserInfo(function(userInfo){
  //更新数据
  that.setData({
    userInfo:userInfo
  })
})

那么类似像onLaunch这样的生命周期函数有哪些呢?还有onShow,onHide和onError。onShow和onHide分别是应用从后台切换到前台以及从前台进入后台时调用的。当你关闭小程序或者切换微信应用到别的应用时,微信并没有直接干掉小程序,小程序只是进入后台而已,当你切换回来,小程序就又进入了前台。这个机制其实和Android以及iOS的应用切换很像。onError顾名思义就是小程序发生脚本错误或者api调用失败是触发的。注意:请不要在其他页面调用生命周期函数

接下来我们看看 app.json,这个东东呢,就是小程序的应用全局配置文件。结构也很简单:pages定义页面的文件路径,window定义小程序整个窗口的一些元素,比如Title的文字,Title的颜色,窗口背景颜色等等。

{
  "pages":[
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "todos",
    "navigationBarTextStyle":"black"
  },
  "debug": true
}

比如在上面的配置下,窗口的展现形式是这样的

在默认配置下窗口的表现
在默认配置下窗口的表现

如果我们改动app.json 成为下面的样子,那么是什么效果呢?

{
  "pages":[
    "pages/index/index",
    "pages/todos/todos"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#000",
    "navigationBarTitleText": "todo",
    "navigationBarTextStyle":"white",
    "backgroundColor": "#492b2b"
  },
  "debug": true
}

改动app.json后的效果
改动app.json后的效果

嗯,导航栏背景变成黑色了,文字白色,一切很完美。且慢,我们不是还添加了一个 "backgroundColor": "#492b2b" 吗,它的效果怎么没有出来呢?你点击头像进入log页面后,再点击左上角返回会看到一个动画,这个动画的背景竟然是我们设置的窗口背景色!?

backgroundColor体现在界面切换时
backgroundColor体现在界面切换时

但为毛在静态界面上看不到呢,我猜想是由于Page挡在了背景前面,但是不是呢,我们来用试验验证一下。怎么验证呢,我们在window的设置中再加一个配置 "enablePullDownRefresh": "true",这个是激活下拉刷新的效果的开关,我们试一下,拖住界面往下拉,果然,窗口背景色在后面藏着呢!

下拉后露出了窗口背景色
下拉后露出了窗口背景色

同样 backgroundTextStyle定义的是下拉背景字体、loading 图的样式(仅支持 dark/light)

在这个配置文件中,我们还可以定义用于切换界面的工具栏(tab bar),我们也来试验一下吧。

{
  "pages":[
    "pages/index/index",
    "pages/todos/todos"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#000",
    "navigationBarTitleText": "todo",
    "navigationBarTextStyle":"white",
    "backgroundColor": "#492b2b",
    "enablePullDownRefresh": "true"
  },
  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "pagePath": "pages/logs/logs",
      "text": "日志"
    }]
  },
  "debug": true
}
配置tabBar后的效果
配置tabBar后的效果

嗯嗯,很丑陋,但是毕竟显示出来了。那么问题来了,这个tabBar如何自定义呢?除了list属性,它还提供color,selectedColor,backgroundColor,borderStyle和postion属性。我们都设置一下

  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页",
      "selectedIconPath": "assets/images/home.png"
    }, {
      "pagePath": "pages/logs/logs",
      "text": "日志",
      "iconPath": "assets/images/log.png"
    }],
    "color": "#b0bec5",
    "backgroundColor": "#2196F3",
    "selectedColor": "#fff",
    "borderStyle": "#F8BBD0",
    "position": "bottom"
  }
自定义tabBar效果
自定义tabBar效果

还是比较丑,码农加直男,只能凑活了。你可能发现我在list中一个添加的是selectedIconPath,一个添加的是iconPath。其实我是犯懒,不想再找两个图片而已。恰好首页是默认选中的tab,那么正好两个图标都能显示出来。

除了tabBar,还可以设置一个debug属性,默认是false,如果设置成true,那么你在调试的时候,会发现在console里面显示很多log信息。其信息有Page的注册,页面路由,数据更新,事件触发等。这个开关在开发时建议打开,在发布时记着关闭哈。

debug设置成true会在console里面显示很多log信息
debug设置成true会在console里面显示很多log信息

再来看 app.wxss,估计你猜也猜得到,这个就是类似css的样式表了。这个全局样式所有页面都可以使用。

/**app.wxss**/
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 200rpx 0;
  box-sizing: border-box;
} 

像我这么爱折腾的人,对于这个css肯定要鼓捣一通,看看到底起什么作用?我们首先加一个背景色,红色比较显眼,background-color: red;

首页的变化
首页的变化

首页下方有一片区域没有着色,先不管它,点头像进去看看。

日志页面的变化
日志页面的变化

这种变化说明这个container选择器是应用于页面的,根据页面内容来决定高度,所以这个100%并不能填满全屏。好在微信小程序可以接受css的大部分设置,如果把 height: 100%; 改成 height: 100vh; 就可以填满全屏了。

页面文件

页面文件也很简单,基本就是和文件夹同名的 .js.json.wxss 文件,唯一多了一种类型就是 .wxml 文件。.js文件中定义一个Page函数,用于构建页面视图的逻辑,而.wxml描述页面视图的结构和布局。首先看一下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
      })
    })
  }
})

由上面的结构可以看出基本上Page函数是一个这样的形式。它接受一个对象,这个对象有一些生命周期函数、事件处理函数和数据对象构成。也就是说上面的代码可以改写成下面这样的形式。

let app = getApp()
let pageParams = {
  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
      })
    })
  }
}
Page(pageParams)

事实上我们更倾向于这么写,因为这样的写法可以使我们避免太多嵌套,以及对pageParams做更多灵活的操作。因为是个对象,我们可以写成 pageParams.data = blablablapageParams.onLoad = someFunctionCall。对了,这个对象我也不知道叫什么,自己给它起的名字而已,反正Page函数是需要这么一个参数的。

那么页面的内建函数有哪些呢?可以参照下表,其中前5项属于生命周期函数。

内建函数 作用 调用机制
onLoad 监听页面加载 生命周期内调用一次
onReady 监听页面初次渲染完成 生命周期内调用一次
onShow 监听页面显示 每次打开页面都会调用一次
onHide 监听页面隐藏 当navigateTo或底部tab切换时调用
onUnload 监听页面卸载 当redirectTo或navigateBack的时候调用
onPullDownRefresh 监听用户下拉动作 用户动作触发
onReachBottom 页面上拉触底事件的处理函数 事件触发
onShareAppMessage 用户点击右上角分享 用户点击触发

具体可以参看微信官方文档给出的页面生命周期示意图


微信官方文档给出的页面生命周期示意图
微信官方文档给出的页面生命周期示意图

再来看看 index.wxml,其实就是类似html的一个模版文件,但是微信自己重造轮子的目的不是很清楚。这里 class="container" 应用了我们的全局样式。

<!--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>

事件绑定是通过在组件标签内加 bindXXX=handler 来完成的,这个 handler 需要在pageParams对象中定义。 {{userInfo.nickName}} 是引用了我们在 index.js 中定义的数据对象的属性。

index.json 也是蛮简单,就是设置页面的一些属性,比如我们改写一下的话:

{
    "navigationBarTitleText": "首页"
}

导航栏文字就会变成我们设置的值。

设置导航栏文字
设置导航栏文字

好的,到现在我们应该清楚了大概的微信小应用的基础知识。下一节我们会尝试着做我们的Todo应用了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,448评论 25 707
  • 为了方便大家了解并入门微信小程序,我将一些可能会需要的知识,列在这里,让大家方便的从零开始学习; 首先感谢几位给予...
    极乐叔阅读 3,424评论 2 16
  • 首先向你道歉,明知道的情况,完全顾及自己的念想,说的话反反复复,而且还要自己那么难受、那么委屈,伤自己,也伤你...
    AK47_10年坚持阅读 360评论 0 0
  • 浮生若梦 梦呓易坠 心若蝉翼 随风而碎 与君共聚梦一场 怎奈伊人泪两行 花凋零 落花随水流 茶亦凉 曲终人已散
    Lilyxizheng阅读 383评论 0 1