微信小程序开发实战2 微信小程序编程基础

2.微信小程序编程基础

2.1小程序目录结构

小程序包含一个描述整体程序的主体部分和多个小程序页面。一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:

文件作用
app.js小程序的入口文件
app.json小程序公共配置
app.wxss小程序公共样式表

表 2-1 小程序主体文件

一个小程序页面由四个文件组成,分别是:

<文件作用>
js页面逻辑文件
wxml页面描述文件,用来设计页面布局,进行数据绑定等。
json页面配置文件
wxss页面样式表文件

表 2-2 小程序页面文件

图2-1是一个简单的小程序目录结构的示例:
在这里插入图片描述

图 2-1 小程序项目结构

2.2小程序全局配置

小程序根目录下的 app.json 文件用来对微信小程序进行全局配置。包括:配置页面文件的路径、窗口表现、设置网络超时时间、设置多tab等。小程序使用json数据格式进行全局配置以及页面配置,下面的代码是一个小程序进行全局配置的例子,在这个例子中设置了两个页面:

{
  "pages":[
    "pages/index/index",
    "pages/user/info"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle":"black"
  }
}

一些常用的配置项:
1)entryPagePath
指定小程序的默认启动路径(初始页面),如果不填则会把 pages 列表的第一项作为默认启动路径。启动路径不支持带路径参数。

{
  "entryPagePath": "pages/index/index"
}

2)pages
用于指定小程序由哪些页面组成,每一项都对应一个页面的路径(含文件名)信息。文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理。未指定 entryPagePath 时,数组的第一项代表小程序的初始页面。小程序中新增/减少页面,都需要对pages数组进行修改。例如对于图2-1中的目录结构,在app.json的相应定义如下:

{
  "pages": [
  "pages/index/index", 
  "pages/logs/logs"
  ]
}

3)tabBar
如果小程序是一个多tab应用(客户端窗口的底部或顶部有tab栏可以切换页面),可以通过tabBar配置项指定tab栏的表现,以及tab切换时显示的对应页面。在配置tabBar数组时,只能配置最少2个、最多5个tab,tab按数组的顺序排序。例如:

{
 "tabBar": {
    "list": [{
        "pagePath": "pages/home/home",
        "text": "首页"
      },
      {
        "pagePath": "pages/self/self",
        "text": "个人"
      }]
  }
}

2.3小程序生命周期

微信客户端打开小程序之前,会把整个小程序的代码包下载到本地。小程序启动之后,会创建一个 App( 在app.js中 定义) 实例,并且会执行App的 onLaunch方法。整个小程序只有一个App实例,是全部页面共享的。进入小程序之后,用户可以点击右上角的关闭,或者按手机设备的Home键离开小程序,此时小程序并没有被直接销毁,而是进入了小程序的后台状态。小程序进入后台状态时App中定义的onHide方法会被调用。当再次回到微信或者再次打开小程序时,微信客户端会把“后台”的小程序唤醒,此时小程序进入前台状态,App中定义的onShow方法会被调用。小程序的生命周期图的示意图如下所示:
在这里插入图片描述

图 2-2 小程序的生命周期
小程序的注册以及生命周期监听函数在app.js中定义。小程序必须在 app.js 中注册App对象,且不能注册多个。下面的代码是App对象的一个简单的实现:

App({
  onLaunch: function () {
   wx.login({
      success: res => {
        //发送 res.code 到后台换取 openId, sessionKey
      }
    })
  },
  onShow: function () {
  },
  onHide: function () {
  },
  globalData: {
    userInfo: null,
  }
})

onLaunch 小程序初始化
当小程序初始化完成时,会触发onlaunch(全局只触发一次),onlaunch在手机后台到前台切换是不会被执行的。如果要再次执行,除非在手机中关闭(关闭不是切换到后台,而是直接删除小程序的后台运行)小程序,然后重新打开,才能执行。
onShow 监听小程序显示
当小程序启动,或者是从后台进入到前台的时候,会执行onshow。
onHide 监听小程序隐藏
小程序从前台进入到后台的时候,会执行onHide。
globalData 全局数据
App中的数据和函数都是全局的,我们可以在App中设置一个全局的对象,让所有的页面都可以使用。调用方式为:在Page中通过getApp()方法得到App对象,然后通过App对象来访问全局数据和调用全局函数。

2.4小程序的页面

一个小程序的页面由三部分组成:界面、配置和逻辑。界面由WXML文件和WXSS文件来负责描述,配置由JSON文件进行描述,页面逻辑则是由JS文件负责。一个页面的文件需要放置在同一个目录下,其中WXML文件和JS文件是必须存在的,JSON和WXSS文件是可选的。
页面路径需要在app.json中的pages字段中声明,否则这个页面不会被注册到宿主环境中。例如有两个页面:pages/index和 pages/logs,则在app.json的定义如下:

{
  "pages": [
"pages/index/index", 
"pages/logs/logs"
]
}
2.4.1 JSON文件

每一个小程序页面也可以使用同名 .json 文件来对本页面的窗口表现进行配置。例如:

{
  "navigationBarBackgroundColor": "#ffffff",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "功能演示",
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
}

小程序页面中的配置项会覆盖 app.json 文件中相同名称的配置项。

2.4.2 WXML文件

WXML全称是WeiXin Markup Language,是小程序框架设计的一套标签语言,结合小程序的基础组件、事件系统,可以构建出程序的页面。WXML 与网页编程中的 HTML类似。例如:

<!--user_info.wxml-->
<view>
  <view class="userinfo">
    <block wx:if="{{!hasUserInfo}}">
      <button bindtap="getUserProfile">获取头像昵称</button>
    </block>
    <block wx:else>
      <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
      <text>{{userInfo.nickName}}</text>
    </block>
  </view>
</view>

与HTML类似,WXML 由标签、属性等元素构成。但是WXML的标签与H5所使用的标签有一定的区别。例如:H5主要的容器标签是div,但是在WXML文件中没有div标签,其主要容器标签为view。除了view, button, text 等标签外,小程序还提供了地图、视频、音频等标签。在小程序里边,你只需要在 WXML 写上对应的标签为小程序提供地图、视频、音频等组件功能。例如,你需要在界面上显示地图,你只需要这样写即可:

使用标签的时候,还可以通过属性传递值给组件,让组件可以以不同的状态去展现,例如,通过src将图片的地址传递给image组件,通过mode将图片的显示方式、图片裁剪和缩放的模式传递给image组件:

另外你也可以通过 style 或者 class 来控制组件的显示样式,以便适应你的界面宽度高度等等。
小程序的WXML功能非常强大,支持通过数据绑定方式来更新页面内容。另外还支持条件渲染,以适应不同条件显示不同的界面内容。支持使用wx:for控制属性绑定一个数组,使用数组中的数据重复渲染组件。WXML的详细介绍请参见后续章节。

2.4.3 WXSS文件

WXSS(WeiXin Style Sheets)是一套用于小程序的样式语言,用于描述WXML的组件样式,也就是视觉上的效果。WXSS与网页开发中的CSS类似。为了更适合小程序开发,WXSS对CSS做了一些补充以及修改。另外 WXSS 仅支持部分 CSS 选择器,具体内容如下所示:

<选择器样例样例描述>
.class.intro选择所有拥有 class=“intro” 的组件
#id#firstname选择拥有 id=“firstname” 的组件
elementview选择所有 view 组件
element, elementview, checkbox选择所有文档的 view 组件和所有的 checkbox 组件
::afterview::after在 view 组件后边插入内容
::beforeview::before在 view 组件前边插入内容

表 2-3 WXSS 支持的 CSS 选择器

小程序支持全局样式与局部样式,定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在page的wxss文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。例如user_info页面对应的WXSS文件的内容为:

.userinfo {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #aaa;
  padding: 200rpx 0rpx;
}
.userinfo-avatar {
  overflow: hidden;
  width: 128rpx;
  height: 128rpx;
  margin: 20rpx;
  border-radius: 50%;
}

与网页开发相同的是小程序也支持内联样式:

<!--内联样式-->
<view style="color: red; font-size: 48rpx"></view>
2.4.4 JS文件

页面逻辑由JS脚本文件负责,对于小程序中的每个页面,都需要在页面对应的 js 文件中进行定义,指定页面的初始数据、生命周期的回调函数、事件处理函数等。例如user_info页面的js文件的内容如下:

//pages/user_info.js
Page({
  //页面的初始数据
  data: {
    userInfo: {},
    hasUserInfo: false,
  },
  //获取头像昵称
  getUserProfile(e) {
    wx.getUserProfile({
      desc: '获取用户信息', 
      success: (res) => {
        console.log(res)
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        });
      }
    })
  }
})

宿主环境使用Page() 构造器用来注册一个小程序页面。Page构造器接受一个Object参数,其中的data属性是页面的初始数据。页面的WXML模板使用data属性进行数据绑定。上面代码中的getUserProfile是一个事件处理函数,用户点击【获取头像昵称】按钮会激发对getUserProfile函数的调用。

2.5数据绑定

在网页的一般开发流程中,我们通常会通过 JS 操作 DOM (对应 HTML 的描述产生的树),用来对页面进行更新。例如,用户点击某个按钮的时候,JS 会记录一些状态到 JS 变量里边,同时通过 DOM API 操控 DOM 的属性或者行为,进而引起界面一些变化。当项目越来越大的时候,你的代码会充斥着非常多的界面交互逻辑和程序的各种状态变量。显然这不是一个很好的开发模式,因此就有了MVVM 的开发模式(例如React, Vue),提倡把渲染和逻辑分离。简单来说就是不要再让 JS 直接操控 DOM,JS 只需要管理状态即可,然后再通过一种模板语法来描述状态和界面结构的关系。小程序的框架也是用到了这个思路,如果你需要把一个 Hello World 的字符串显示在界面上。在WXML中是这么写的:
{{ message }}
这个message数据来自于页面Page构造器的data字段,data参数是页面第一次渲染时从逻辑层传递到渲染层的数据。以下是JS文件的定义:

Page({
  data: {
    message: 'Hello World'
  }
})

若要动态修改界面的显示,只需要调用Page实例的setData函数即可,例如:
WXML文件示例

 <view bindtap="onSetData">{{message}}</view>

JS 文件示例

Page({
  data: {
    message: 'Hello World'
  },
  onChangeData:function(){
     this.setData({message:"hello weixin"});
  }
})

通过 {{ }} 的语法把一个变量绑定到界面上,我们称为数据绑定。WXML通过 {{变量名}} 来绑定WXML文件和对应的JavaScript 文件中的data对象属性。通过 {{变量名}} 语法可以使得WXML拥有动态渲染的能力。
属性值也可以动态的去改变,有所不同的是,属性值必须被包裹在双引号中,如下:

<text data-test="{{test}}"> hello world</text>

2.6页面事件

UI界面的程序需要和用户互动,例如用户可能会点击你界面上某个按钮,又或者长按某个区域,这类反馈应该通知给开发者的逻辑层。在小程序里边,我们把这种“用户在渲染层的行为反馈”以及“组件的部分状态反馈”抽象为渲染层传递给逻辑层的“事件”。
要使用事件首先需要在组件中绑定一个事件处理函数。事件绑定的写法和组件属性一致,以key="value"的形式,其中:key以bind或者catch开头,然后跟上事件的类型,如bindtap、atchtouchstart。例如:

<!-- page.wxml -->
<view data-hi="demo" bindtap="onClick">Click me!</view>

然后在相应的Page定义中写上相应的事件处理函数,参数是event:

//page.js
Page({
  onClick: function(event) {
    console.log(event)
  }
})
2.6.1事件分类

事件分为冒泡事件和非冒泡事件:
冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递,例如tap事件便是一个冒泡事件。
非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递,例如input组件的input事件便是一个非冒泡事件。
除 bind 外,也可以用 catch 来绑定事件。与 bind 不同, catch 会阻止事件向上冒泡。例如在下边这个例子中,点击 inner view 会先后调用handleTap3和handleTap2(因为tap事件会冒泡到middle view,而middle view 阻止了tap事件冒泡,不再向父节点传递),点击middle view会触发handleTap2,点击 outer view 会触发handleTap1。

<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view
    <view id="inner" bindtap="handleTap3">
      inner view
    </view>
  </view>
</view>

自基础库版本1.5.0起,bind和catch后可以紧跟一个冒号,其含义不变,如bind:tap、catch:touchstart。

2.6.2互斥事件绑定

除 bind 和 catch 外,还可以使用 mut-bind 来绑定事件。一个 mut-bind 触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind 绑定函数不会被触发,但 bind 绑定函数和 catch 绑定函数依旧会被触发。例如在下边这个例子中,点击inner view会先后调用 handleTap3 和 handleTap2 ,点击 middle view会调用 handleTap2 和 handleTap1 。

<view id="outer" mut-bind:tap="handleTap1">
  outer view
  <view id="middle" bindtap="handleTap2">
    middle view
    <view id="inner" mut-bind:tap="handleTap3">
      inner view
    </view>
  </view>
</view>
2.6.3事件的捕获阶段

触摸类事件支持捕获阶段。捕获阶段位于冒泡阶段之前,且在捕获阶段,事件到达节点的顺序与冒泡阶段恰好相反。需要在捕获阶段监听事件时,可以采用capture-bind、capture-catch关键字,后者将中断捕获阶段和取消冒泡阶段。在下面的代码中,点击inner view 会先后调用handleTap2、handleTap4、handleTap3、handleTap1。

<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2">
  outer view
  <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
    inner view
  </view>
</view>

如果将上面代码中的第一个capture-bind改为capture-catch,将只触发handleTap2。

<view id="outer" bind:touchstart="handleTap1" capture-catch:touchstart="handleTap2">
  outer view
  <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4">
    inner view
  </view>
</view>

2.7 WXML模板

我们已经了解了WXML的基本用法,并且能够通过通过 {{变量名}} 的语法把一个变量绑定到界面上,也知道如何在组件绑定事件处理函数。接下来看看WXML模板的一些高级特性的使用方法。

2.7.1 WXML条件渲染

WXML中,使用wx:if="{{condition}}" 来判断是否需要渲染该代码块:

<view wx:if="{{condition}}"> True </view>

也使用 wx:elif 和 wx:else 来添加一个 else 块:

<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>

因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 标签将多个组件包装起来,并在上边使用 wx:if 控制属性。

<block wx:if="{{true}}">
  <view> view1 </view>
  <view> view2 </view>
</block>
2.7.2 WXML列表渲染

在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。数组的当前元素的下标变量名默认为index,数组当前元素的变量名默认为item。示例:

<view wx:for="{{array}}">
  {{index}}: {{item.message}}
</view>

JS 文件中的脚本:

Page({
  data: {
    array: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  }
})

WXML使用 wx:for-item 可指定数组当前元素的变量名,使用 wx:for-index 指定数组当前元素的下标变量名:

<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
  {{idx}}: {{itemName.message}}
</view>

类似wx:if ,也可以将 wx:for 用在 标签上,用来渲染一个包含多节点的结构块。例如:

<block wx:for="{{[1, 2, 3]}}">
  <view> {{index}}: </view>
  <view> {{item}} </view>
</block>
2.7.3 使用template

WXML提供了模板(template)组件,可以在模板中定义代码片段,然后在不同的地方调用。
1)定义模板
使用name属性,作为模板的名字。然后在template标签内定义代码片段,如:

<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>

2)使用模板
使用is属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:

<template is="msgItem" data="{{...item}}"/>

JS 文件中的脚本:

Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    }
  }
})
2.7.4 WXML文件引用

WXML 提供两种文件引用方式:import和include。
1)import
import可以在该文件中使用目标文件定义的template,如:
在 item.wxml 中定义了一个叫item的template:

<!-- item.wxml -->
<template name="item">
  <text>{{text}}</text>
</template>

在 index.wxml 中引用了 item.wxml,就可以使用item模板:

<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>

2)include
include 可以将目标文件除了template标签wxse标签外的整个代码引入,相当于是拷贝到 include 位置,如:
代码清单:index.wxml

<!-- header.wxml -->
<view> header </view>

代码清单:footer.wxml

<!-- footer.wxml -->
<view> footer </view>

代码清单:index.wxml

<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>

2.8页面的生命周期

微信客户端在生成小程序页面时会先根据json 文件配置生成一个界面。紧接着客户端就会装载这个页面的 WXML 结构和 WXSS 样式。最后客户端会装载js文件,并调用页面构造器(Page)生成页面。在生成页面的时候,小程序框架会把 data 数据和WXML文件一起渲染生成最终的页面内容。
页面初次加载的时候,微信客户端就会给Page实例派发onLoad事件,Page构造器所定义的onLoad方法会被调用。onLoad在页面没被销毁之前只会触发1次。
页面显示之后,Page构造器所定义的onShow方法会被调用,一般从别的页面返回到当前页面时,当前页的onShow方法都会被调用。
在页面初次渲染完成时,Page构造器所定义的onReady方法会被调用,onReady在页面没被销毁前只会触发1次,onReady触发时,表示页面已经准备妥当,在逻辑层就可以和视图层进行交互了。
以上三个事件触发的时机是onLoad早于 onShow,onShow早于onReady。
页面不可见时,Page构造器所定义的onHide方法会被调用。这种情况通常发生在使用wx.navigateTo切换到其他页面时、或者是切换底部tab时。
当前页面使用wx.redirectTo或wx.navigateBack切换到其他页面时,当前页面会被微信客户端销毁回收,此时Page构造器所定义的onUnload方法会被调用。
页面页面的生命周期的示意图见图2-3所示:
在这里插入图片描述

图 2-3 小程序页面的生命周期
小程序页面的onLoad / onReady / onShow / onHide /onUnload是Page实例的生命周期函数。下面的代码是增加了生命周期函数的处理逻辑后的user_info.js:

//pages/user_info.js
Page({
  //页面的初始数据
  data: {
    userInfo: {},
    hasUserInfo: false,
  },
  //获取头像昵称
  getUserProfile(e) {
    wx.getUserProfile({
      desc: '获取用户信息', 
      success: (res) => {
        console.log(res)
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        });
      }
    })
  },
  onLoad: function(options) {
    console.log('onLoad监听页面加载');
  },
  onReady: function() {
    console.log('onReady监听页面初次渲染完成');
  },
  onShow: function() {
    console.log('onShow监听页面显示');
  },
  onHide: function() {
    console.log('onHide监听页面隐藏');
  },
  onUnload: function() {
    console.log('onUnload监听页面卸载');
  }
})
  • 5
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go lang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值