本手册只是简单概述,具体还请看 微信官方文档
项目构建
注册申请小程序
微信小程序开发第一步,在微信公众平台注册,申请一个AppID
然后我们选择小程序注册并填写相关信息
申请完成以后,将会得到一个AppID(小程序ID)和AppSecret(小程序密钥,生成后保存好如忘记需要重新生成)
下载开发工具
下载微信提供的小程序开发工具
安装完成之后打开刚刚安装的这个软件,会要求使用微信扫码登录
登录完成之后,进入新建项目的页面,可以新建不同的项目,默认是新建小程序
- 项目名称: 自定义即可
- 目录: 项目存放目录
- AppID: 填入刚刚申请的AppID
- 开发模式: 选择小程序即可
- 后端服务: 暂时不会用到,这里选择不使用云服务即可
- 模版选择: 这里选择JavaScript - 基础模版进行演示,如果习惯TypeScript,也可以选择TS
这里点击创建按钮,开始创建小程序
认识开发工具
整个界面主要分为四个部分,分别和编辑器左上角看到的模拟器、编辑器、调试器三个绿色按钮对应
- 模拟器 我们可以在这里实时看到我们所编写的内容,可以切换机型、网络状态等
- 资源管理器 项目目录,方便我们更快的找到某一个页面
- 编辑器 也就是我们编写代码的区域
- 调试器 和谷歌浏览器很相似
再右一些我们可以看到一个预览按钮,点击会提供一个有效期限的二维码,我们可以通过微信扫描二维码在手机端真是查看,以及右边的真机调试,也是同理
这里整理了一些项目调试时的注意事项
认识项目基础结构
目录结构:一个完整的小程序包含一个描述整体程序的app 和多个描述页面的page
- app.js 小程序入口逻辑
- app.json 小程序公共配置
- app.wxss 小程序公共样式
小程序代码构成
- index…wxml 模版文件,同 html 的角色
- index.wxss 样式文件,同 css 的角色
- index.js 脚本执行逻辑文件
- index.json 页面级配置文件
项目配置
JSON 是一种数据格式,并不是编程语言,在小程序中,JSON扮演的静态配置的角色。全局配置项细节可参考文档 app.json配置
全局配置
app.json
是当前小程序的全局配置,包含小程序的所有页面路径、界面表现、网络超时时间、底部tab等
以下列了一些常用的属性,完整配置项请参考小程序全局配置
pages
指定小程序由那些页面组成,文件名不需要写文件后缀,框架会自动去寻找对应位置的.js
、.json
、.wxml
、.wxss
四个文件进行处理
在未设置entryPagePath
时,默认加载数组第一项的页面路径为首页
⚠️ 小程序中新增/减少页面时,都需要对pages数组进行修改
如开发目录为
├── pages
│ │── index
│ │ ├── index.wxml
│ │ ├── index.js
│ │ ├── index.json
│ │ └── index.wxss
│ └── logs
│ ├── index.wxml
│ ├── index.js
│ ├── index.json
│ └── index.wxss
则需要在app.json
中写
{"pages": ["pages/index/index", "pages/logs/logs"]
}
window
定义小程序所有页面的顶部背景颜色、文字颜色定义等
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
navigationBarBackgroundColor | HexColor | #000000 | 导航栏背景颜色,如 #000000 |
navigationBarTextStyle | string | white | 导航栏标题颜色,仅支持 black / white |
navigationBarTitleText | string | 导航栏标题文字内容 | |
navigationStyle | string | default | 导航栏样式,仅支持以下值:default 默认样式 、custom 自定义导航栏,只保留右上角胶囊按钮 |
backgroundColor | HexColor | #ffffff | 窗口的背景色 |
tabBar
可以通过tabBar
设置一个多tab栏应用小程序
⚠️ tab栏最少两个,最多五个
属性 | 类型 | 必填 | 默认值 | 描述 |
---|---|---|---|---|
color | HexColor | ✔️ | tab 上的文字默认颜色,仅支持十六进制颜色 | |
selectedColor | HexColor | ✔️ | tab 上的文字选中时的颜色,仅支持十六进制颜色 | |
backgroundColor | HexColor | ✔️ | tab 的背景色,仅支持十六进制颜色 | |
borderStyle | string | ❌ | black | tabbar 上边框的颜色, 仅支持 black / white |
list | Array | ✔️ | tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab | |
position | string | ❌ | bottom | tabBar 的位置,仅支持 bottom / top |
custom | boolean | ❌ | false | 自定义 tabBar |
页面配置
每一个小程序页面也可以使用同名,.json
文件来对页面的窗口表现进行配置,页面中配置项会覆盖app.json
的 window
中相同的配置项
更多完整版页面配置请参考小程序页面配置
配置JSON
语法注意
- JSON文件内容需包裹在
{}
中,并以"key": value
的形式展示。⚠️key
一定要上双引号,没加引号或加单引号都会报错 - JSON 的值仅支持以下数据类型,其它格式都会报错
Number
String
Boolean
Array
Object
Null
- 不能使用注释
WXML与WXSS
WXML
WXML(WeiXin Markup Language)是微信框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。
我们在写HTML时,会习惯用div
标签进行整体布局,使用span
标签进行文本信息描述,使用img
标签进行图片展示等,但在WXML有些不同。
在小程序中视图容器view
相当于HTML 中的div
可用于布局,基础内容text
对应着span
可进行多行文本展示,媒体组件image
相当于img
用来承载图片信息
<view><text>这里是文本</text><!-- 图片直接拿的网图,万一以后打不开了,大家自己随便找个图 --><image src="https://img0.baidu.com/it/u=1546227440,2897989905&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500"></image>
</view>
<view class="pages"><!-- 这里是头部 --><view class="hander"></view><!-- 这里是内容 --><view class="content"></view><!-- 这里是底部 --><view class="footer"></view>
</view>
小程序WXML提供的每一个标签都是一个组件,每个组件都为我们提供了一些有趣的属性。
如text
组件为我们提供一个user-select
文本是否可选,为true
时开启可选
⚠️ 如user-select
的属性是Boolean值我们可以简写成"user-select"
代表该属性的值为true
<text user-select>这里是可选内容</text>
如image
组件的mode
属性,当mode
值为widthFix
时,宽度不变,使图片高度自动变化
<image mode="widthFix" src="https://img0.baidu.com/it/u=2065155373,1177612096&fm=253&fmt=auto&app=120&f=JPEG?w=462&h=1000"></image>
对比添加mode="widthFix"
前后的区别
更多组件详细属性见 小程序组件文档
WXSS
WXSS (WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。
WXSS 对于 CSS 这一块并没有太多的扩展,WXSS 扩展了尺寸单位和样式导入两个特性
尺寸单位:**rpx**
rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx
。如在 iPhone6 上,屏幕宽度为375px
,共有750个物理像素,则750rpx == 375px == 750
物理像素,1rpx == 0.5px == 1
物理像素
设备 | rpx换算px(屏幕宽度/750) | px换算rpx(750/屏幕宽度) |
---|---|---|
iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
样式导入
使用@import
语句可以导入外联样式表,@import
后跟需要导入的外联样式表的相对路径,用;
表示语句结束。
如果使用过scss
或者less
的小伙伴一定对这一块特别熟悉,通常的说就是我们可以通过import
导入的形式,引入外部的样式,同HTML中的link
作用类似。例如:
我们先在index.wxml
中添加一个标签
<view class="main">这里是内容</view>
然后在index.wxss
中添加样式
.main {width: 400rpx;height: 400rpx;background-color: antiquewhite;
}
我们再新建一个全局样式表common.wxss
.main {text-align: center;
}
然后我们在index.wxss
中通过import
引入刚才创建的common.wxss
@import "../../common.wxss";
.main {width: 400rpx;height: 400rpx;background-color: antiquewhite;
}
以下是引入前后对比,从图中我们可以看到import
引入样式成功
⚠️ 定义在app.wxss
中的样式会作用于每个页面,与import
导入的不同,import
只会作用于你所引入的页面
选择器
选择器这一块官方文档有一丢丢坑,对于支持的选择器展示的不是很全面
选择器 | 示例 | 描述 |
---|---|---|
.class | .main | 选择所有拥有class="main" 的组件 |
#id | #map | 选择拥有id="map" 的组件 |
element | view | 选择所有view 组件 |
element, element | view, checkbox | 选择所有文档的view 组件或左右的checkbox 组件 |
::after | view::after | 在view 组件后边插入内容 |
::before | view::before | 在view 组件前边插入内容 |
但通过实际情况,我们可以发现 子代选择题/后代选择器/伪类选择器
大部分都支持
<view class="main"><view class="item">文本1</view><view class="item">文本2</view>
</view>
.main > .item {font-size: 30rpx;font-weight: bold;
}
.main .item:first-child{color: red;
}
既然有支持的,也会有不支持的,例如我们无法在手机上使用:hover
伪类,因为在手机端没有鼠标悬浮这一概念
数据交互
用过react的小伙伴对于小程序的数据绑定肯定相当熟悉,在react中我们是用{}
在视图层进行解析JS中的变量,而小程序中是通过{{}}
解析。
数据绑定
我们先看一个简单的数据绑定
Page({data: {message: "Hello World"}
})
<view>{{message}}</view>
JS 文件中data
对象里定义着我们在小程序中所用变量的初始值
列表渲染
在vue
中我们有v-for
,而在小程序中我们也有wx:for
达到同样的效果
<view wx:for="{{array}}" wx:key="*this">{{"下标: " + index + ", 值: " + item}}</view>
Page({data: {array: [1, 2, 3, 4, 5]}
})
从上面的代码中我们可以看出index
和item
都是小程序对于wx:for
默认提供的,在我们遍历数组时,一个代表着当前元素的下标,一个代表着当前元素自身
对于index/item
既然是小程序默认提供的,那么我们也可以通过wx:for-index
/wx:for-item
来分别自定义index
/item
的名称,如下例
<view wx:for="{{array}}" wx:for-index="arrayIndex" wx:for-item="arrayItem" wx:key="*this"
>{{arrayItem.name}}</view>
Page({data: {array: [{ name: '关关雉鸠' },{ name: '在河之洲' }]}
})
在上面的例子中我们都是把wx:for
放在一个组件上的,假如现在我们有个需求需要同时遍历多个组件怎么办,这个时候可能有小伙伴想到用view
包裹那些组件,但是这个就会造成DOM结构变得异常复杂,我们知道小程序的DOM节点嵌套是有一定限制警告的,可查看小程序性能中 控制WXML节点数量和层级
这个时候怎么办呢?官方为我们提供了block
标签,同vue中的template
可用来渲染一个包含多节点的结构块
<block wx:for="{{[1, 2, 4]}}" wx:key="*this"><view>下标: {{index}} </view><view>元素: {{item}} </view>
</block>
从图中我们可以看出block
是不作为标签渲染在DOM节点中
有细心的小伙伴应该发现了,我在写每一个wx:for
都加了一个wx:key
,这又是因为什么呢?
用过 vue 和 react 应该对这个key
比较了解了,对于wx:for
循环wx:key
就是性能优化的方法,假设我们现在有一个数组[1, 2, 3, 4, 5]
,然后将这五项渲染出来,现在我们要改变其中某一个值,如果我们没设置key
就会重新渲染,如果我们添加了key
,小程序就会以key
为一个查找标准,避免全部重新渲染,从而达到一个渲染优化
wx:key
的值以两种形式展示
- 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
- 保留关键字
*this
代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
⚠️ 如不提供 **wx:key**
,会报一个 **warning**
, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略
字符串形式
<view wx:for="{{array}}" wx:key="id">{{item.name}}}</view>
Page({data: {array: [{ id: 0, name: '关关雉鸠' },{ id: 1, name: '在河之洲' }]}
})
关键字形式
<view wx:for="{{[1, 2, 3, 4]}}" wx:key="*this">{{item}}}</view>
条件渲染
小程序为我们提供了wx:if
判断是否显示该模块,同 vue 中的v-if
类似
<view wx:if="{{bool}}"> True </view>
wx:if
的值为false
时,整个组件不会解析在wxml
中,与display: none
不同
同vue中条件渲染小程序还提供了wx:elif
和 wx:else 让我们可以处理多种情况
<view wx:if="{{length > 5}}">大于5</view>
<view wx:elif="{{length < 1}}">小于1</view>
<view wx:else>小于5并且大于1</view>
因为wx:if
是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/>
标签将多个组件包装起来,并在上边使用wx:if
控制属性
<block wx:if="{{true}}"><view> view1 </view><view> view2 </view>
</block>
这些需要注意的是,wx:if
的条件值切换时,在小程序中会有一个局部渲染的过程,这个过程会触发组件的销毁与创建过程,同时,wx:if
是惰性的,在初始化值为false
时,小程序会什么都不做
另外小程序也提供了一个hidden
属性,类似于v-show
的效果,对于组件进行简单的显示于隐藏,hidden
接受值为Boolean值
同v-if
与v-show
的区别,wx:if
是对组件的创建与销毁,hidden
是对组件进行display: none
的操作
事件交互
在vue中我们通过v-on + 事件名
(<button v-on:click="onClick">点击</button>
)绑定事件
在react中则是on + 事件名
(<button onClick="onClick">点击</button>
)绑定事件
而在小程序中我们需要通过bind
进行绑定事件
另外小程序中的点击事件为tap
,更多事件请见事件分类
<view id="tapTest" data-text="Weixin" bindtap="onClick">点击</view>
⚠️ 小程序绑定事件和绑定数据不同,小程序的方法事件绑定时是不需要**{{}}**
表达式
与vue、react 不同的是,小程序的事件传值,需要在组件节点上附加一些自定义数据,这自定义数据以data-
开头,多个单词由连字符-
连接,默认会转换为小写字符,如下示例
data-element-type
,最终会呈现为event.currentTarget.dataset.elementType
data-elementType
,最终会呈现为event.currentTarget.dataset.elementtype
Page({onClick: function(event) {console.log(event)}
})
{"type":"tap","timeStamp":895,"target": {"id": "tapTest","dataset": {"text":"Weixin"}},"currentTarget": {"id": "tapTest","dataset": {"text":"Weixin"}},"detail": {"x":53,"y":14},"touches":[{"identifier":0,"pageX":53,"pageY":14,"clientX":53,"clientY":14}],"changedTouches":[{"identifier":0,"pageX":53,"pageY":14,"clientX":53,"clientY":14}]
}
更多详情内容,请见事件详解
生命周期
小程序的生命周期同其它框架,都是从创建到销毁的一个过程
应用级生命周期
当用户首次打开小程序时 触发 onLaunch
全局只触发一次
小程序初始化完成后 触发onShow
监听小程序显示
小程序从前台进入后台 触发onHide
小程序从后台进入前台 触发onShow
⚠️ “前台/后台”:当用户点击右上角的关闭,或者按Home键离开微信时,这时候小程序并没有销毁,而是进入了后台,当我们再次进入微信或者打开小程序,小程序就会从后台进入前台
页面级生命周期
小程序加载页面 触发onLoad
页面载入后显示页面 触发onShow
首次显示页面 触发onReady
渲染页面元素和样式一个页面只调用一次
小程序后台运行时或者跳转到其他页面时(wx.navigateTo
) 触发onHide
小程序从后台切换至前台运行时或者重新进入页面时, 触发onShow
使用重定向wx.redirectTo
或者关闭当前页返回上一页时(wx.navigateBack
) 触发onUnload
应用级与页面级生命周期
小程序初始化后,页面首次加载触发onLoad
只会触发一次
小程序进入后台时,先执行页面onHide
再执行应用onHide
小程序从后台切入前台时,先执行应用onShow
再执行页面onShow
网络请求
wx.request
在前端开发中请求数据的方法有new XMLHttpRequest()
、fetch()
、ajax()
、axios
等,小程序中也为我们提供了对应的数据请求方法wx.request
用来发起HTTPS网络请求
wx.request({url: 'example.php', //仅为示例,并非真实的接口地址data: {x: '',y: ''},header: {'content-type': 'application/json' // 默认值},success (res) {console.log(res.data)}
})
使用wx.request
注意事项
- 使用前需要在 微信公众平台-开发-开发管理-开发设置 中配置服务器域名,可见注意事项
- 请求的接口协议须是https开头,也就是加密协议
以下简单列了一些wx.request
常用属性,更多属性请见小程序文档
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
url | string | ✔️ | 开发者服务器接口地址 | |
data | string/object/ArrayBuffer | ❌ | 请求的参数 | |
header | Object | ❌ | 设置请求的 header,header 中不能设置 Referer | |
timeout | number | 6000 | ❌ | 超时时间,单位为毫秒 |
method | string | GET | ❌ | HTTP请求方法 |
dataType | string | json | ❌ | 返回的数据格式 |
success | function | ❌ | 接口调用成功的回调函数 | |
fail | function | ❌ | 接口调用失败的回调函数 | |
complete | function | ❌ | 接口调用结束的回调函数(调用成功、失败都会执行) |
基于wx.request
进行二次封装
/*** 对微信提供的request请求进行二次封装* @param {String} api 接口地址* @param {String} method 请求类型* @param {*} data 请求参数* @param {Object} header 请求头参数* @return 返回微信小程序request请求*/
export default function request(api, method, data, header) {let headers = {"content-type": "application/json",...header};// 这里统一设置请求头token// if (getToken()) headers['Authorization'] = 'Bearer ' + getToken() // getToken() 获取token;return new Promise((reslove, reject) => {wx.request({url: api,method: method || 'GET',header: headers,data: data || {},success: (res) => {if (res.statusCode === 200) {reslove(res.data);}if (res.statusCode === 401 && res.data.error === 'invalid_token') {// 状态码为401时的特殊处理// ...reslove(res.data);}},fail: (err) => {reject(err);},complete: () => {// 不管是调用成功还是失败都会执行// 统一取消loading加载动画wx.hideLoading();}})});
}['options', 'get', 'post', 'put', 'head', 'delete', 'trace', 'connect', 'patch'].forEach((method) => {request[method] = (api, data, header) => request(api, method, data, header)
});
本地存储
storage 数据缓存
如果我们想和在web端一样将一些信息存储在本地(cookie
、localStorage
)怎么办呢?小程序为我们准备数据缓存接口,同localStorage
类似
方式 | 名称 | 描述 |
---|---|---|
异步 | wx.setStorage | 将数据存储在本地缓存中指定的key 中 |
wx.getStorage | 从本地缓存中异步获取指定key 的内容 | |
wx.removeStorage | 从本地缓存中移除指定key | |
同步 | wx.setStorageSync | 同wx.setStorage 同步版 |
wx.getStorageSync | 同wx.getStorage 同步版 | |
wx.removeStorageSync | 同wx.removeStorage 同步版 |
更多详情内容,请见小程序文档,以下简单列了一些用法
wx.setStorage
/*** @param {String} key 本地缓存中指定的 key* @param {any} data 需要存储的内容(仅支持原生类型、Date、以及通过JSON.stringify序列化的对象) */
wx.setStorage({key:"key",data:"value"
})
如上面的代码存储之后我们可以通过调试器的Storage功能进行查看,刚刚存储的数据
wx.getStorage
/*** @param {String} key 本地缓存中指定的 key* @param {function} success 接口调用成功的返回,返回值为key对应的内容 */
wx.getStorage({key: 'key',success (res) {console.log(res.data)}
})
wx.removeStorage
/*** @param {String} key 本地缓存中指定的 key */
wx.removeStorage({key: 'key',success (res) {console.log(res)}
})
globalData 共享数据
和vue中的vuex
,以及react中的redux
的作用类似,都是用于给所有页面都可以使用的数据
globalData
是定义在app.js 中,是一个对象,对象中存放着要使用到的共享数据
获取globalData
的数据,在页面中通过调用全局变量的方式获取app.globalData.xxxx
const app = getApp(); // 获取应用实例Page({data: {userInfo: app.globalData.userInfo}
})
修改globalData
的数据app.globalData.xxxx = data
const app = getApp(); // 获取应用实例Page({data: {userInfo: undefined},onLoad: () {app.globalData.userInfo = {name: 'Jane'}}
})
路由跳转
小程序中的路由大致可以划分为两种形式,标签跳转与API 接口跳转
标签跳转
在vue框架中提供<router-view>
标签进行路由跳转,而小程序中也提供了类似的<navigator>
组件
<navigator url="/page/index/index" open-type="switchTab" hover-class="node"
>切换底部 Tab</navigator>
API接口跳转
wx.navigateTo
实例代码
wx.navigateTo({url: 'test?id=1',events: {// 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据acceptDataFromOpenedPage: function(data) {console.log(data)},someEvent: function(data) {console.log(data)}...},success: function(res) {// 通过 eventChannel 向被打开页面传送数据res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })}
})