小程序开发

概述

简介

​ 小程序是一种不需要下载的安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用。也体现了“用完即走”的理念。用户不用关心是否安装太多应用的问题。应用无处不在,随时可用,但又无需安装卸载。——Allenzhang

特点:

  • 速度快:无需下载安装,加载速度快于HTML5,微信登录,随时可用
  • 无需适配:一次开发。多端兼容,免除了对各种手机型的适配
  • 社交分享:支持直接或App分享给微信好友和群聊
  • 出色体验:可以达到近乎原生App的操作体验和流畅度,在离线状态亦可使用
  • 用完即走:通过扫码、长按、微信搜索、公众号、好友推荐等方式快速获取服务,用完即走
  • 低门槛:已有公众号的组织可以快速注册、可快速生成门店小程序

开发流程

小程序的技术模型:

image.png

小程序的程序语言

  • 渲染层:WXML + WXSS(类似HTML + CSS)
  • 逻辑层:JavaScript
  • 配置:JSON

设计规范

  • 友好:重点突出;流程明确
  • 清晰:导航明确;减少等待;异常反馈
  • 便捷:减少输入;避免误操作;操作流畅
  • 统一:视觉统一;WeUI

运营规范

开发项目

全局配置

​ 小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。

以下是一个包含了部分常用配置选项的 app.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"pages": [
"pages/index/index",
"pages/logs/index"
],
"window": {
"navigationBarTitleText": "Demo"
},
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首页"
}, {
"pagePath": "pages/logs/index",
"text": "日志"
}]
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
},
"debug": true
}

页面配置

​ 每一个小程序页面也可以使用同名 .json 文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.jsonwindow 中相同的配置项。

1
2
3
4
5
6
7
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信接口功能演示",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}

图片标记:路径可以使用http或相对路径

作为容器元素

应用弹性盒子布局

  • 传统方式布局
    • 目标的实现属性赋值非常分散
    • 严重依赖于页面结构与内容的实际大小

弹性盒子布局(统一,灵活)

盒子属性

1
2
3
4
display:flex;
flex-direction:column; //垂直布局
justify-content:space-around; //自上而下放置时素材均匀分布
align-items:center; //元素在水平方向上居中显示

响应式长度单位rpx

让元素大小适配不同宽度屏幕

1
2
3
4
5
.images {
width:375rpx;
height:375rpx;
border-radius:50%; //圆形图片
}

导航链接

navigator组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<navigator style="display:inline" 
open-type="redirect"
url="/pages/logs/logs"
hover-class="nav-hover"
class="nav-default">
启动日志
</navigator>

//style="display:inline" 设置为内联元素
//open-type="redirect" 重定向,不可返回;默认为navigate

.nav-hover {
color:red;
} //点击态

.nav-default {
color:blue;
} //显示态
//交换两者才可实现点击态

配置tabBar(底部标签栏)

app.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"tabBar": {
"list": [
{
"text": "启动日志",
"pagePath": "pages/logs/logs",
"iconPath": "images/icons/person.jpg",
"selectedIconPath": "images/icons/iperson.jpg"
},
{
"text": "主页",
"pagePath": "pages/index/index",
"iconPath": "images/icons/home.jpg",
"selectedIconPath": "images/icons/ihome.jpg"
}
],
"color":"#000",
"selectedColor":"#00f"
},

此时navigate失效,需要修改。

1
<navigator url="/pages/logs/logs" open-type="switchTab">启动日志</navigator>

配置全局导航栏样式

app.json

1
2
3
4
5
6
7
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "关于",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true
},

数据绑定

weather.js

1
2
3
4
5
6
7
8
9
Page({
data: {
todayweather:{
city:"武汉",
weather:"晴",
temperature:"2℃"
}
}
})

小程序的运行环境与基本架构

每个小程序都是运行在它的微信客户端上的,通过微信客户端给它提供的运行环境,小程序可以直接获取微信客户端的原生体验和原生能力。

条件渲染

在框架中,使用 wx:if="" 来判断是否需要渲染该代码块:

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

也可以用 wx:elifwx:else 来添加一个 else 块:

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

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

1
2
3
4
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>

<block/> 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。

wx:if vs hidden

因为 wx:if 之中的模板也可能包含数据绑定,所以当 wx:if 的条件值切换时,框架有一个局部渲染的过程,因为它会确保条件块在切换时销毁或重新渲染。同时 wx:if 也是惰性的,如果在初始渲染条件为 false,框架什么也不做,在条件第一次变成真的时候才开始局部渲染。

相比之下,hidden 就简单的多,组件始终会被渲染,只是简单的控制显示与隐藏。

一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。

列表渲染

swiper组件

幻灯片轮播展示

页面的生命周期函数

<swiper></swiper>添加属性

1
2
current="{{ array.length - 1 }}"	//切换到最后一张幻灯片(有缺陷)

js文件

1
2
3
4
5
6
7
8
9
10
current="{{ currentIndex }}"

//初始化数据
//生命周期函数
onload:function(options) {
this.setData({
currentIndex:this.data.array.length - 1
})
}
//还包括其他的生命周期函数

更新数据

1
2
3
4
5
6
7
8
9
10
11
<button bindtap="f0">按钮<button>

//更新数据不能直接赋值
f0:function(event){
console.log(this.data.count) //读取
this.setData({
count:this.data.count + 1
})
}

//this.serData可以动态设置变量

事件机制

1
2
3
<button bindtap="f0">按钮<button>		//会向上冒泡传递,父元素函数会被调用

<button catchtap="f1">按钮<button> //不会向上冒泡传递,父元素函数不会被调用

组件自定义数据属性

1
2
3
4
5
6
7
8
9
10
11
f1:function(event){
wx.navigateTo({
url:'/pages/index/index'
})
} //点击页面相同

f1:function(event){
wx.navigateTo({
url:'/pages/index/index?id=77'
})
} //点击页面相同

动态导航栏

index.js

1
2
3
4
5
onShareAppMessage: function(){
return {
title:"向你推荐" + this.data.movie.title
}
}

组件化开发

自定义组件item.js

item.json将组件属性设置true

1
2
3
{
"component": true
}

item.js

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
Component({
options: { // 组件配置
addGlobalClass: true,
// 指定所有 _ 开头的数据字段为纯数据字段
// 纯数据字段是一些不用于界面渲染的 data 字段,可以用于提升页面更新性能
pureDataPattern: /^_/,
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
properties: {
vtabs: {type: Array, value: []},
},
data: {
currentView: 0,
},
observers: { // 监测
activeTab: function(activeTab) {
this.scrollTabBar(activeTab);
}
},
relations: { // 关联的子/父组件
'../vtabs-content/index': {
type: 'child', // 关联的目标节点应为子节点
linked: function(target) {
this.calcVtabsCotentHeight(target);
},
unlinked: function(target) {
delete this.data._contentHeight[target.data.tabIndex];
}
}
},
lifetimes: { // 组件声明周期
created: function() {
// 组件实例刚刚被创建好时
},
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
},
methods: { // 组件方法
calcVtabsCotentHeight(target) {}
}
});