伊现成 微信小程序
# 微信小程序的特点
小程序是一种不需要下载、安装即可使用的应用,它出现了触手可及的梦想,用户扫一扫或者搜一下即开打开应用,也出现了用完即走的理念,用户不用关心安装太多应用的问题,应用随处可用,但又无须安装卸载。我当时是这样来定义什么是小程序的。
1:无需下载:我们直接使用它,所以无须安装是小程序最基础的一个特性;
2:触手可及:当我们拿着智能手机接触周边的时候,我们可以通过手机直接获得信息,就是周边的信息;
3:用完即走:对于周边要完成的任务来说我们需要的是用完即走;
4:无须卸载:小程序看起来是程序,但是它以完全不同于过去APP的形状出现,它有更灵活的应用组织形态;
5:操作更接近原生App---可以实现一些打开摄像头,录像,上传图片、语音、支付等功能
# 微信小程序官方地址
- 官方工具:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=201714 (opens new window)
- 简易教程:<https://mp.weixin.qq.com/debug/wxadoc/dev/
- 运营规范:https://mp.weixin.qq.com/debug/wxadoc/product/index.html (opens new window);在这里查看禁止事项;
- 接入指南:<https://mp.weixin.qq.com/debug/wxadoc/introduction/index.html
- 客服消息:https://mp.weixin.qq.com/debug/wxadoc/introduction/custom.html?t=20161221 (opens new window)
- 支付文档:<https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
- 数据分析:<https://mp.weixin.qq.com/debug/wxadoc/analysis/index.html?t=201714
# 开发前必读
- 必须通读运营规范;有需要禁止开发的事项需要了解;
- 配置域名:必须配置域名后,才可以使用配置的域名,比如你需要请求XX地址的数据,必须在后台设置了域名之后,才可以真机请求数据;必须https的域名才可以请求(本地测试时可以在小程序详情中设置不校验合法域名功能)
# 小程序审核
- 因小程序名称,logo,简介等不符合要求被拒
- 功能页面与所选的服务类目范围不符
- 小程序功能流程不完整,功能过于简单
- 常见的审核拒绝情形:<https://mp.weixin.qq.com/debug/wxadoc/product/reject.html?t=201714
# 小程序架构
- 主体部分主要由3个文件构成
- app.js:小程序逻辑,初始化APP
- app.json :小程序配置,比如导航、窗口、页面http请求跳转等
- app.wxss:公共样式配置
主体配置完成之后,就是对应的业务开发了,也就是开发者最常操作的页面。小程序页面设计基本上也是遵循 MVC 结构进行构建。
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
- 页面由4个文件构成
- js:页面逻辑,相当于控制层(C);也包括部分的数据(M)
- wxml:页面结构展示,相当于视图层(V)
- wxss:页面样式表,纯前端,用于辅助wxml展示
- json:页面配置,配置一些页面展示的数据,充当部分的模型(M)

# 配置 app.json
全局配置页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等
pages 接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成
window 用于设置小程序的状态栏、导航条、标题、窗口背景色。
tabBar
通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
tabBar 配置数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。
networkTimeout 可以设置各种网络请求的超时时间。
debug
可以在开发者工具中开启 debug 模式,在开发者工具的控制台面板,调试信息以 info 的形式给出,其信息有Page的注册,页面路由,数据更新,事件触发 。 可以帮助开发者快速定位一些常见的问题。
page.json 每一个小程序页面也可以使用.json文件来对本页面的窗口表现进行配置。 页面的配置比app.json全局配置简单得多,只是设置 app.json 中的 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项。
# App()
用来注册一个小程序。在小程序启动的时候调用,并创建小程序,直到销毁。在整个小程序的生命周期过程中,它都是存在的。很显然它是单例的,全局的。
- 只能在app.js中注册一次。
- 在代码的任何地方都可以通过 getApp() 获取这个唯一的小程序单例,
App()的参数是 object 类型 {} ,指定了小程序的声明周期函数。
globalData 对象
全局数据。
let app = getApp();
let username= "username";
//设置全局变量
app.globalData.userInfo = {
username: username
};
let app = getApp();
let username= ""
//获取全局变量
if (app.globalData.username) {
username= app.globalData.userInfo.username;
}
# 页面生命周期和参数传递
初始页面index.js启动会从app,js中的生命周期方法调用开始:onLaunch---onShow,然后开始调用index.js页面中的生命周期方法:onLoad---onShow---onReady,当点击“后台”时,页面index.js进入后台,这时回调生命周期方法onHide,如果再点击“前台”,使页面回到前台,这时回调页面生命周期方法:onShow(注意,这时不再回调onLoad)
onLaunch 函数
监听小程序初始化。
当小程序初始化完成时,会触发 onLaunch(全局只触发一次)。
onShow 函数
监听小程序显示。
当小程序启动,或从后台进入前台显示,会触发。
onHide 函数
监听小程序隐藏。
当小程序从前台进入后台,会触发。
所谓前后台的定义,类似于手机上的app,比如当不在使用微信时,就进入了后台。
# Page()
Page()的参数也是Object类型
onLoad
监听页面加载
页面刚开始加载的时候触发。只会调用一次。
onReady 监听页面初次渲染完成
类似于html的 onReady。只会调用一次。
onShow
监听页面显示
页面显示的时候触发,比如页面切换
onHide
监听页面隐藏
和onShow对应
onUnload
监听页面卸载
在 redirectTo 或 navigateBack 的时候调用
onPullDownRefresh
监听用户下拉动
1)需要在config的window选项中开启enablePullDownRefresh。 2)当处理完数据刷新后,wx.stopPullDownRefresh 可以停止当前页面的下拉刷新。
onReachBottom
页面上拉触底事件的处理函数
data
页面的初始数据
# Page.prototype.setData()
Page的函数 setData() 用于页面初始数据data的修改。如果该数据绑定到了视图层wxml中展示,那么无须刷新,视图层就会反映出修改。
对于data的修改,** 只能使用 setData() ,不能直接通过 this.data 进行修改 **。数据量限制在 1024 kb以内。
通过this.data赋值的话,只对当前方法生效,在其他方法与wxml中,不会生效。
# getCurrentPages()
let pages = getCurrentPages();
let currPage = null; //当前页面
let prevPage = null; //上一个页面
if (pages.length >= 2) {
currPage = pages[pages.length - 1]; //当前页面
prevPage = pages[pages.length - 2]; //上一个页面
}
if(prevPage){
prevPage.setData({
dataFromB: '2018世界杯冠军:'+this.data.array[e.detail.value]
});
}
# 页面的跳转
wx.navigateTo(Object object)
方法一:
在wxml中跳转到detail页面
<navigator url="detail?id={{item.id}}">
</navigator>
方法二:
在js中触发事件跳转到detail页面
wx.navigateTo({
url: 'detail?id=1'
})
detail.js
Page({
onLoad: function(option){
console.log(option.query)
}
})
# 视图层WXML
# 绑定数据、模板、逻辑
之前介绍了小程序的MVC结构:
- page.js 即控制层(C),也叫业务逻辑层;
- page.js 中的data属性,即数据模型层(M);
- page.wxml 即展现层(V);
- page.wxss 即css,增强展现层效果(V);
通过业务逻辑层(C),修改data属性(M),从而在展现层(V)中展示。即MVC设计模式。
如果在 data 中定义了某个变量,比如
Page({
data:{
title: '小程序分享',
desc: '视图层—WXML'
}
})
wxml中取值
<view class="container">
<view class="title">{{title}}</view>
<view class="desc">{{desc}}</view>
</view>
效果:

# 数据绑定常用语法
内容与组件属性都需要在{{}}中
<view id="item-{{id}}">{{ message }}</view>
控制属性
<view wx:if="{{condition}}"> </view>
关键字(需要内)
<checkbox checked="{{false}}"> </checkbox>
注意:不要直接写 checked="false",其计算结果是一个字符串,转成 boolean 类型后代表真值。
三元运算
<view hidden="{{flag ? true : false}}"> Hidden </view>
算数运算
<view> {{a + b}} + {{c}} + d </view>
字符串运算
<view>{{"hello" + name}}</view>
# 列表渲染
wx:for
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
<view wx:for="{{array}}">
{{index}}: {{item.message}}
</view>
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
使用 wx:for-item 可以指定数组当前元素的变量名,
使用 wx:for-index 可以指定数组当前下标的变量名:
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.message}}
</view>
wx:for 也可以嵌套,下边是一个九九乘法表
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
<view wx:if="{{i <= j}}">
{{i}} * {{j}} = {{i * j}}
</view>
</view>
</view>
block wx:for
类似 block wx:if,也可以将 wx:for 用在<block/>标签上,以渲染一个包含多节点的结构块。例如:
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: </view>
<view> {{item}} </view>
</block>
# WXSS
WXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。
WXSS 用来决定 WXML 的组件应该怎么显示。
为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。
与 CSS 相比,WXSS 扩展的特性有:
尺寸单位
样式导入
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 建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。
# 样式导入
使用
@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。
# 事件
数据绑定、模板、逻辑等,主要的作用是在视图中展示数据,以及如何展示。但是只有展示是不够的,我们需要互动。比如一个HTML页面,可以展示文字、图片,但是还要有一些互动,比如链接、按钮等。互动其实就是事件了。比如HTML中 button 的 onClick ,就是点击的时候,触发的动作以及开发人员相应的业务逻辑处理。
事件小例:bindtap
index.wxml:
<button bindtap="toEvent">视图层WXML:事件</button>
index.js:
toEvent : function(){
// 跳转到 event.wxml页面
wx.navigateTo({
url: '/pages/wxml/event'
})
}
# 事件分类
- 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
- 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
WXML的冒泡事件列表:
| 类型 | 触发条件 |
|---|---|
| touchstart | 手指触摸动作开始 |
| touchmove | 手指触摸后移动 |
| touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 |
| touchend | 手指触摸动作结束 |
| tap | 手指触摸后马上离开(最常用的事件,类似于click事件) |
| longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个 |
| longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) |
| transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 |
| animationstart | 会在一个 WXSS animation 动画开始时触发 |
| animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 |
| animationend | 会在一个 WXSS animation 动画完成时触发 |
| touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 |
事件以bind或者catch开头
bind事件绑定不会阻止冒泡事件向上冒泡,如bindtap。
catch事件绑定可以阻止冒泡事件向上冒泡,如catchtap。
例:
<view id="outer" bindtap="handleTap1">
outer view
<view id="middle" catchtap="handleTap2">
middle view
<view id="inner" bindtap="handleTap3">
inner view
</view>
</view>
</view>
因为handleTap2是catchtap,所以:
点击 inner view,会先后触发 handleTap3、handleTap2
点击 middle view,只会触发 handleTap2
点击 outer view,会触发 handleTap1
# 事件对象
如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数会收到一个事件对象。
BaseEvent 基础事件对象属性列表:
| 属性 | 类型 | 说明 |
|---|---|---|
| type | String | 事件类型 |
| timeStamp | Integer | 事件生成时的时间戳 |
| target | Object | 触发事件的组件的一些属性值集合 |
| currentTarget | Object | 当前组件的一些属性值集合 |
CustomEvent 自定义事件对象属性列表(继承 BaseEvent):
什么是自定义事件呢,你可以简单的理解为:在触发子组件的一些事件的时候,同时也能触发父组件对应的事件并对父组件中的某些数据进行修改的事件就是自定义事件
| 属性 | 类型 | 说明 |
|---|---|---|
| detail | Object | 额外的信息 |
TouchEvent 触摸事件对象属性列表(继承 BaseEvent):
| 属性 | 类型 | 说明 |
|---|---|---|
| touches | Array | 触摸事件,当前停留在屏幕中的触摸点信息的数组 |
| changedTouches | Array | 触摸事件,当前变化的触摸点信息的数组 |
特殊事件:
target
触发事件的源组件。
| 属性 | 类型 | 说明 |
|---|---|---|
| id | String | 事件源组件的id |
| tagName | String | 当前组件的类型 |
| dataset | Object | 事件源组件上由data-开头的自定义属性组成的集合 |
currentTarget
事件绑定的当前组件。
| 属性 | 类型 | 说明 |
|---|---|---|
| id | String | 当前组件的id |
| tagName | String | 当前组件的类型 |
| dataset | Object | 当前组件上由data-开头的自定义属性组成的集合 |
# 自定义组件
当我们在设计我们项目的时候会发现在不同的页面中,有时候会用到相同的功能模块,此时我们就可以将这些相同的部分提取出来并且单独设为一个"页面",然后在要应用到它的地方引用就可以了。
在上面我介绍了,自定义组件其实就像是一个页面,所以我们在编写它的时候也应该像设计页面一样,具备json wxml wxss js这四个文件
前期准备
1.首先,新建一个名为wxcomponent的项目,
2.在pages文件夹下创建一个components文件夹用来盛放我们所有的自定义组件.
3.在components文件夹下创建一个cpt的文件夹用来盛放cpt这个自定义组件,并分别创建好对应的配置文件,如下图:

自定义组件声明
要在cpt.json中进行自定义组件声明,也就是告诉开发者这是一个组件:
{
"component": true
}
# 设计组件结构
在 wxml 文件中编写组件模版,在 wxss 文件中加入组件样式: cpt.wxml文件
<!-- 这是自定义组件的内部WXML结构 -->
<view class="inner">
{{innerText}}
<button bindtap='customMethod'>点击</button>
<slot></slot>
</view>
cpt.wxss文件
/* 这里的样式只应用于这个自定义组件 */
.inner {
color: red;
}
这里的`暂时可以不用管它.
并且自定义组件在设计结构的时候是不应该使用给便签加上id或者使用属性选择器和标签名选择器的. 这是因为组件就是被我们那里重用的,而页面中只能允许有一个id.
PS:公用样式在子组件中无效
# 注册组件
在自定义组件的js文件中,需要使用 Component() 来注册组件,并提供组件的属性定义、内部数据和自定义方法,如在cpt.js中:
Component({
properties: {
// 这里定义了innerText属性,属性值可以在组件使用时指定
innerText: {
type: String,
value: 'default value',
}
},
data: {
// 这里是一些组件内部数据
someData: 1
},
methods: {
// 这里是一个自定义方法
customMethod: function () {
console.log('customMethod')
}
}
})
# 使用自定义组件
这里我为了简便一点,就直接用自带的logs页面来进行自定义组件的调用吧.
首先,你应该在你要用组件的那个页面中引用声明.也就是在.json文件.
如在logs.json中:
{
"navigationBarTitleText": "查看启动日志",
"usingComponents": {
"component-tag-name": "../components/cpt/cpt"
}
}
接下来我们就可以在页面中像使用其他组件一样的使用自定义组件了. 比如我在logs.wxml中使用:
<!--logs.wxml-->
<view class="container log-list">
<!-- 以下是对一个自定义组件的引用 -->
<component-tag-name inner-text="Some text"></component-tag-name>
</view>
跳转到logs页面如下图的效果:

# 使用slot
在上面的例子中,在cpt.wxml中写入了标签,在引用cpt这个组件时我们要是不在组件的标签中写入其他的内容,是不会显示标签的
也就是说如果我们把上面logs.wxml改动一下:
<!--logs.wxml-->
<view class="container log-list">
<!-- 以下是对一个自定义组件的引用 -->
<component-tag-name inner-text="Some text">
<view>这里是插入到组件slot中的内容</view>
</component-tag-name>
</view>
并且<slot></slot>的位置在哪里也是由你在设计cpt组件时决定的
上面的例子中我是将slot放在button之下:
<!-- 这是自定义组件的内部WXML结构 -->
<view class="inner">
{{innerText}}
<button bindtap='customMethod'>点击</button>
<slot></slot>
</view>
你也可以将它放在其他位置来达到不同的效果.
# 使用多个slot
正常情况下,一个组件中只有一个slot,但有时候我们的自定义组件可能不止使用一个slot,别怕,我们的微信小程序也是可以允许你这样的.
需要使用多slot时,可以在组件js中声明启用
1.在cpt.json(你自定义组件的json文件)中声明
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
properties: { /* ... */ },
methods: { /* ... */ }
})
2.此时,可以在这个组件的wxml中使用多个slot,以不同的 name 来区分:
<!-- 这是自定义组件的内部WXML结构 -->
<view class="inner">
<slot name="header"></slot> <-- 一定要记得使用name属性 -->
{{innerText}}
<button bindtap='customMethod'>点击</button>
<slot name="footer"></slot>
</view>
3.使用时,用 slot 属性来将节点插入到不同的slot上。
<!--logs.wxml-->
<view class="container log-list">
<!-- 以下是对一个自定义组件的引用 -->
<component-tag-name inner-text="Some text">
<view slot="header">头部的内容</view>
<view slot="footer">底部的内容</view>
</component-tag-name>
</view>
# 组件数据的传递
组件之间的数据传递分为很多种,例如,在上级组件传递数据、事件给下级页面,上级页面监听下级页面的事件回调,跨级的数据传递,例如,在首页将获取的列表数据传递给列表组件:
上级组件:
wxml:
<component-list list-data="{{ListData}}"></component-list>
js
Page({
data: {
listData:[
{ title: "没点才艺,不敢结婚" },
{ title: "当年母亲因超生将1岁儿子送走,如今患病求助遭拒:他年薪55万,我实在没办法" },
{ title: "男生什么情况下会对女生真正失望?" },
{ title: "马蓉被人背着匆匆逃离医院,你是过街老鼠吗?慢慢走不行吗?非得让我把鼻涕泡笑出来!" },
{ title: "趁年轻要对老婆好点,否则老了没人推你" },
{ title: "恋爱需谨慎 自己养的祖宗 跪着也得哄完!" },
{ title: "这喝水的声音也太可爱了" },
],
},
})
下级组件:
wxml:
<view wx:for="{{ListData}}">
<view class="item">{{item.data}}</view>
......
</view>
js:
Component({
properties: {
ListData: {
type: Array,
value: [],
observer: function(newData, oldData){
//表示属性值被更改时的响应函数
}
}
},
data: {
someData: {}
},
methods: {
customMethod: function(){}
}
})
可以看到,组件的写法和页面基本一样,只是Page变成了Component,这里要注意,只要是由上层组件传递过来的数据,都应该在properties里声明,数据类型是必须值,而observer则是监听数据变化后产生的事件,例如,上级页面的传递的值产生变化了,就可以在observer里捕获到两个值newData, oldData,在observer里可以调用该组件的methods,去做不同的操作。同样,组件也有生命周期,这点和page略有区别,文档里对这一点写的还是很详细的。
# 自定义事件
触发事件的源组件。
什么是自定义事件呢,你可以简单的理解为:在触发子组件的一些事件的时候,同时也能触发父组件对应的事件并对父组件中的某些数据进行修改的事件就是自定义事件.
例:
子组件
methods: {
delete(){
let list = this.data.listData
list.splice(-1, 1)
this.setData({
listData: list
})
//自定义事件向父组件传参
this.triggerEvent('myevent', this.data.listData.length)
},
}
父组件
index.wxml
<component-tag-name bindmyevent="onMyEvent" list-data="{{listData}}">
</component-tag-name>
js
//自定义方法接收子组件传参
onMyEvent(e){
this.setData({
total: e.detail
})
}