微信小程序 初见篇


关于微信小程序

小程序是什么?它有着什么样的功能?

A:小程序是一种不需要下载安装即可使用的应用,用户扫一扫或者搜一下即可打开应用。也体现了 “用完即走” 的理念,通过公众号关联,用户可以实现公众号与小程序之间相互跳转。

小程序可以和现有的 App 打通吗?

A:小程序可以借助微信联合登录,和开发者已有的 App 后台的用户数据进行打通,但不会支持小程序和 App 直接的跳转,现在最新版已经支持小程序跳转到另一个小程序了

小程序与传统 HTML5 的区别

  • 开发工具不同
    区别于 H5 的开发工具 + 浏览器 Device Mode 预览的模式,小程序的开发基于自己的开发者工具,可以实现同步本地文件 + 开发调试 + 编译 + 预览 + 上传 + 发布等一整套流程。

  • 开发语言不同
    小程序自己开发了一套 WXML 标签语言和 WXSS 样式语言,并非直接使用标准的 HTML5+css3。

  • 组件封装不同
    小程序独立出来了很多原生 APP 的组件,在 HTML5 需要模拟才能实现的功能,小程序里可以直接调用组件。

传统 HTML5 在加载的时候受限于网络环境,需要顺序加载 HTML、CSS、JS,然后返回数据,最后渲染页面显示在浏览器中

小程序的两个线程:逻辑层和视图层会同时进行、并行加载,甚至视图层会更早执行,当视图线程加载完,通知逻辑层,逻辑层会把准备好的数据用 setData 的方法返回给视图线程

目录结构与配置

官方框架配置

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

1
2
3
4
app.js 小程序入口逻辑
app.json 小程序的公共设置
对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等
app.wxss 小程序的公共样式表

一个程序页面由四个页面组成

1
2
3
4
wxml 页面结构
wxss 页面的样式表 (不必填)
json 页面的配置 (不必填)
js 页面逻辑
为了方便开发者减少配置项,我们规定描述页面的这四个文件必须具有相同的路径与文件名。

逻辑层

小程序开发框架的逻辑层是由 Javascript 编写的
逻辑层将数据进行处理后发送给视图层,同时接收视图层的反馈,为了方便开发 小程序做了以下更改

  1. 增加 APP 和 Page 方法,进行程序和页面的注册

    APP () 函数用来注册一个小程序 接受一个 object 参数 也就是小程序的生命周期函数

1
2
3
4
5
6
7
8
9
onLaunch	监听小程序初始化 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
onShow 生命周期函数--监听小程序显示 当小程序启动,或从后台进入前台显示,会触发 onShow
onHide 生命周期函数--监听小程序隐藏 当小程序从前台进入后台,会触发 onHide

前台、后台定义: 当用户点击左上角关闭,或者按了设备 Home 键离开微信,小程序并没有直接销毁,而是进入了后台;当再次进入微信或再次打开小程序,又会从后台进入前台。需要注意的是:只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。

onError 错误监听函数 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
当然了 开发者也可以添加任意的函数或数据到 Object 参数中,用 this 可以访问比如点击 长按 事件等

Page () 函数用来注册一个页面 接受一个 object 参数 其指定页面的初始数据、生命周期函数、事件处理函数等。

1
2
3
4
5
6
7
8
9
10
data		            页面的初始数据
onLoad(query) 生命周期函数--监听页面加载
onReady 生命周期函数--监听页面初次渲染完
onShow 生命周期函数--监听页面显示
onHide 生命周期函数--监听页面隐藏
onUnload 生命周期函数--监听页面卸载
onPullDownRefresh 页面相关事件处理函数--监听用户下拉
onReachBottom 页面上拉触底事件的处理函数
onShareAppMessage 用户点击右上角转发
onPageScroll(scrollTop) 页面滚动触发事件的处理函数 参数代表页面垂直方向已经滚动的距离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
route 字段可以获取到当前页面的路径。
注册函数里 console.log('this.route',this.route)

setData 函数以key value 的形式接收一个对象 用于将数据从逻辑层发送到视图层,改变对应的 this.data中相应key的值。
set的值不需要在this.data中预先定义

this.setData({
name:'马莹'
})
console.log('this.data',this.data)

注意:

直接修改 this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致
单次设置的数据不能超过1024kB,请尽量避免一次设置过多的数据。

  1. 增加 getAPP 和 getCurrentPages 方法,分别用来获取 App 实例和当前技术栈

    可以根据全局函数获取到小程序的实例

1
2
3
4
5
var appInstance = getApp()
console.log(appInstance.globalData)

console.log('getCurrentPages()',getCurrentPages())

小 tips:

  • App() 必须在 app.js 中注册,只能注册一次

  • 在定义在 APP() 的函数中想获取 app 实例,不要用 getApp() 的方式 直接用 this 就行

  • 不要在 onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成


    3 . 每个页面有独立的作用域,并提供模块化的能力

    在 javascript 文件中声明的变量和函数只是在该文件中有效

    我们可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。

      需要注意的是:
    
      exports 是 module.exports 的一个引用,因此在模块里边随意更改 exports 的指向会造成未知的错误。所以我们更推荐开发者采用 module.exports 来暴露模块接口,除非你已经清晰知道这两者的关系。
      小程序目前不支持直接引入 node_modules , 开发者需要使用到 node_modules 时候需要拷贝出相关的代码到小程序的目录中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function sayHello(name) {
console.log(`Hello ${name} !`)
}
function sayGoodbye(name) {
console.log(`Goodbye ${name} !`)
}

module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye

引用
var common = require('common.js') require 暂时不支持绝对路径
Page({
helloMINA: function() {
common.sayHello('MINA')
},
goodbyeMINA: function() {
common.sayGoodbye('MINA')
}
})

4 . 由于框架并非运行在浏览器中,所以 JavaScript 在 web 中一些能力都无法使用,如 document,window 等。

5 . 开发者写的所有代码最终将会打包成一份 JavaScript,并在小程序启动的时候运行,直到小程序销毁,

页面路由
在小程序中所有页面的路由全部由框架进行管理。
框架以栈的形式维护了当前的所有页面。 当发生路由切换的时候,页面栈的表现如下:

1
2
3
4
5
6
初始化-> 新页面入栈
打开新页面-> 新页面入栈
页面重定向-> 当前页面出栈 新页面入栈
页面返回-> 页面不断出栈,直到目标返回页,新页面入栈
Tab切换-> 页面全部出栈 只留下新的Tab页面
重加载-> 页面全部出栈 只留下新的页面

小 tips:

  • navigateTo, redirectTo 只能打开非 tabBar 页面。
  • switchTab 只能打开 tabBar 页面
  • reLaunch 可以打开任意页面
  • 页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。

    视图层

**WXml **
WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。
WXML 在语法上更接近 XML 语言,遵循 SGML 规范,区别于 HTML 语言随意的标签闭合方式,WXML 语言必须包括开始标签和结束标签

基本语法
WXML 中的动态数据均来自对应 Page 的 data。不同于 Vue 它是单向绑定 必须通过调用事件来 setData 新的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<view> {{ message }} </view>
Page({
data: {
message: 'Hello MINA!'
}
})

花括号和引号之间不能留空,否则将最终被解析成为字符串

<view wx:for="{{[1,2,3]}} ">
{{item}}
</view>
等同于

<view wx:for="{{[1,2,3] + ' '}}" >
{{item}}
</view>


当 wx:for 的值为字符串时,会将字符串解析成字符串数组
<view wx:for="yishu">
{{item}}
</view>
===
<view wx:for="{{['y','i','s','h','u']}}">
{{item}}
</view>

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


关键字 需要在双引号之内 checked="false",其计算结果是一个字符串,转成 boolean 类型后代表真值。
<checkbox checked="{{false}}"> </checkbox>

<view hidden="{{flag ? true : false}}"> Hidden </view>

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

如果想一次性判断多个组件标签 可以这么写
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
是一个包装元素,不会在页面中做任何渲染,只接受控制属性。当然了 我是经常用View标签包裹的

关于 wx:if 与 hidden
简单来说 hidden为true的组件始终被渲染 只是切换 display与none状态
而 wx:if只有在条件成立的情况下才会被真的渲染

使用模版 模板拥有自己的作用域,只能使用 data 传入的数据。

1
2
3
4
5
6
7
8
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>
引用
<template is="msgItem" data="{{...item}}"/>

事件 (冒泡 非冒泡)
事件 API
事件是视图层到逻辑层的通讯方式,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。
冒泡事件
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
touchend 手指触摸动作结束
tap 手指触摸后马上离开
longtap 手指触摸后,超过 350ms 再离开

bind 事件绑定不会阻止冒泡事件向上冒泡,
catch 事件绑定可以阻止冒泡事件向上冒泡。

1
2
3
4
5
6
7
8
视图层
<view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! </view>
逻辑层
Page({
tapName: function(event) {
console.log(event)
}
})

文件引入
WXML 提供两种文件引用方式 import 和 include。

import 可以在该文件中使用目标文件定义的 template

1
2
3
4
5
6
7
<!-- item.wxml -->
<template name="item">
<text>{{text}}</text>
</template>
引入模版
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>

import 有作用域的概念,即只会
import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。

include 可以将目标文件除了 <template/> 的整个代码引入 也就是说 copy 到了当前引入的位置

WXSS
WXSS (WeiXin Style Sheets) 是一套样式语言,用于描述 WXML 的组件样式。

WXSS 支持的单位有 px、rem和rpx,其中 rem 和 rpx 可以针对屏幕容器进行适配,px 则为固定尺寸。
其中 1rpx=0.5px,在 WXSS 和 WXML 中定义的 rpx 单位最终会转换为在手机端可以识别的 rem 单位。

建议开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
我们拿到 750 的设计稿,在 PS 中量取的容器大小,可以直接定义为 rpx,不需要进行 2 倍尺寸的换算,rpx 的单位不光在样式中会自适应,写在 WXML 的 style 里也会根据屏幕自适应。

像 CSS 一样 可以使用 @import 语句可以导入外联样式表,@import 后跟需要导入的外联样式表的 相对路径

import "../../wxss/common.wxss";

为了提高渲染速度尽量避免静态样式存在于 style 中,除非需要接收动态样式才应该写在行内(貌似说了一句废话)

<view style="color:{{color}};" />

全局样式与局部样式
全局样式都在 app.wxss 中定义 page 里的 wxss 文件定义的是局部样式 局部样式可以覆盖同名的全局样式

运行机制

  • 小程序没有重启的概念
  • 当小程序进入后台,客户端会维持一段时间的运行状态,超过一定的时间后会自动销毁(目前是五分钟)
  • 置顶的小程序不会被微信主动销毁
  • 当收到内存警号时小程序会销毁