第一章 什么是微信小程序
1. 小程序介绍
微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验(微信小程序官方介绍)
2. 微信小程序和app的区别
(1)下载安装
微信小程序:通过微信(扫描二维码、搜索、分享)即可获得;
App:从应用商店(App Store、应用汇等)下载安装;
(2)内存占用
微信小程序:无需安装,和微信共用内存使用,占用内存空间忽略不计;
App:安装于手机内存,一直占用内存空间,太多的 App 可能会导致内存不足;
(3)手机适配
微信小程序:一次开发,多终端适配;
App:需适配各种主流手机,开发成本大;
(4)产品发布
微信小程序:提交到微信公众平台审核,云推送;
App:向十几个应用商店提交审核,且各应用商店所需资料不一样,非常繁琐;
(5)功能区别
微信小程序:限于微信平台提供的功能;
App:可实现完整功能 。
03、营销推广区别
(1)推广难度
微信小程序:通过二维码、微信搜索、朋友分享等方式直接获得
App:需要用户主动下载十几M的安装包,在没有Wi-Fi的情况下推广困难;
(2)消息推送
微信小程序:仅能回复模版消息,消息推送次数有限制;
App:可自行设置推送次数。
04、市场及应用场景区别
(1)面向用户群
微信小程序:面向所有微信用户,月活跃用户超8亿人,日使用账号5.7亿;
App:面向所有智能手机用户,约20亿台;
(2)创业机会
微信小程序:蓝海市场,在新的使用场景中可以寻求很多好机会;
App:市场基本饱和,几乎所有的领域均已覆盖;
(3)应用场景
微信小程序:
适合快速场景化服务,可以快速验证客户需求
适合初创团队,试错成本低,需要较少时间和资金投入,可以迅速占领空白领域客户渠道
App:
适合已验证可行的商业模式
适合产品复杂度高,功能受限低的产品开发
适合成熟的商业大公司,对自我品牌要求较高的企业
具备充裕的开发时间和资金储备
3. 小程序开发准备
- 申请账号
· 在微信公众平台官网首页(mp.weixin.qq.com)注册。
· 根据指引填写信息和提交相应的资料。
· 选择注册的主体类型(个人/企业)
企业类型与个人类型区别:
·目前个人类型申请的小程序不支持微信认证、微信支付、卡券应用、获取用户绑定的手机号码、附近小程序功能。
·获取用户信息,上传下载,音视频播放,获取定位,map地图,发布等其他基础功能都是可以用的。 - 小程序信息完善
· 小程序名称、头像、介绍、服务类目等
· 配置服务器域名(小程序可以跟指定的域名与进行网络通信,发起请求、上传下载等 )
· 登录小程序后台,可以在菜单 “设置”-“开发设置” 看到小程序的AppID - 开发
· 在小程序管理平台设置开发者权限
· 安装小程序开发者工具
· 编写小程序代码 - 发布小程序
· 在小程序开发者工具菜单栏点击上传代码
· 登录管理平台 - 开发管理 进行 提交审核 或者 选为体验版本
· 选择提交审核
项目发布需要:
· 项目填写了AppID(注册后在小程序管理平台 - 设置 - 开发设置中获取,新建项目时就得填写)
· 已经完善了小程序信息(名称、头像、介绍、服务类目等)
4. 小程序开发工具介绍
微信开发者工作是微信官方提供的针对微信小程序的开发工具,集中了开发,调试,预览,上传等功能,帮助开发者简单和高效地开发微信小程序。
[小程序开发工具介绍](https://developers.weixin.qq.com/miniprogram/dev/devtools/devtools.html)
第二章 微信小程序开发基础
1. 文件目录和每个文件的功能
- app全局文件
app.js 小程序逻辑
app.json 小程序公共配置
app.wxss 小程序公共样式表 - 小程序页面page
pages文件夹中每一个文件夹代表一个页面,每个页面包含一个.js文件,一个.wxml文件,一个.wxss文件以及一个.json文件。
js 页面逻辑
wxml 页面结构
json 页面配置
wxss 页面样式表
wxml文件
.wxml文件类似我们写的.html文件,但标签上有些不同。
使用<view></view>
标签代替<div></div>
,使用<image></image>
代替<img/>
,使用<block></block>
标签代替<template></template>
,引入<text></text>
标签等等,后续做项目的时候会了解到更多标签。wxss文件
.wxss文件就是.css文件。在app.wxss中控制全局样式,在pages文件夹中的页面文件的.wxss文件中控制局部样式。局部样式的优先级大于全局样式。
微信小程序为了适配多种不同屏幕大小的设备,采取了一种的响应式单位rpx,使用rpx单位就好像使用百分比作为单位一样实现响应式布局。rpx是一种相对大小,我们使用iphone6的机型时,1px=2rpx,iphone6的大小为375px667px,对于的rpx为750rpx1334rpx。
官网-rpxjson文件
.json文件是项目的配置文件。app.json文件进行全局配置,每个页面文件夹下还有一个.json文件用于局部配置,局部配置只能配置window属性。
[官网-配置小程序](https://developers.weixin.qq.com/miniprogram/dev/framework/config.html)js文件
小程序的生命周期分为应用声明周期和页面生命周期,应用声明周期函数在app.js中定义,且在小程序运行过程中一直存在内存中处于运行状态,在这里可以定义应用声明周期函数以及定义全局变量。每个页面中的.js文件中则可以定义页面声明周期函数等。
2. WXML语法
3. 事件
小程序中不使用click事件,而用tab(触摸)事件代替,事件的前缀有两种分别是bind和catch,bind:tab表示冒泡事件,即事件触发后继续冒泡触发后续事件,catch:tab表示非冒泡事件,即事件触发后不再冒泡,类似执行了event.stopPropagation()方法。
//实现一个点击加一的计数器
//wxml
<view>
<text bind:tab="addNumber">{{count}}</text>
</view>
//js
Component({
//调用组件传来的变量
properties:{
},
//内部变量
data() {
return {
count:0
}
},
//内部方法
methods:{
//点击加一
addNumber() {
this.setData({
count:count+1
});
}
}
});
4. 小程序的生命周期
小程序的声明周期分为整个应用的声明周期以及单个页面的声明周期以及自定义组件的生命周期。生命周期不用马上弄明白,但是以后会有帮助。
应用生命周期回调函数
在app.js中,可以设置应用生命周期的回调函数。
App({
onLaunch (options) {
//初始化完成后执行
},
onShow (options) {
//进入小程序后执行
},
onHide () {
//离开小程序后执行,注意不是销毁,如切换其他App时,微信处于后台中,这时调用onHide
},
onError (msg) {
//小程序出错时执行
}
});
页面上生命周期回调函数
Page({
onLoad: function(options) {
//页面初始化后执行
},
onReady: function() {
//初次渲染结束执行
},
onShow: function() {
//进入页面执行
},
onHide: function() {
//页面隐藏/切入后台时触发,如 wx.navigateTo 或底部 tab 切换到其他页面,小程序切入后台等
},
onUnload: function() {
//页面卸载时触发。如wx.redirectTo或wx.navigateBack到其他页面时。
},
onPullDownRefresh: function() {
// 监听下拉刷新事件,必须开启enablePullDownRefresh
},
onReachBottom: function() {
// 监听用户上拉触底事件
},
onShareAppMessage: function () {
// 点击转发按钮
},
onPageScroll: function() {
// 页面滚动事件
},
onResize: function() {
// 旋转屏幕触发
}
})
组件生命周期函数
Component({
lifetimes: {
created:function() {
//组件实例化的时候执行,此时不能调用setData()
}
attached: function() {
// 在组件实例进入页面节点树时执行
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
},
});
第三章 小程序的模板和自定义组件
1. template模板
相对于component而言,template更加轻量级,它的功能有限,主要是用于展示。模板只有两个以.wxml 和.wxss结尾的文件
- 定义模板
新建一个与pages同级的文件夹 => templates,在templates下创建templateItem文件夹,然后在这个文件夹里创建同名的.wxml和.wxss的文件
使用name属性作为模板的名字,然后在<template><template/>
标签内定义代码片段
// templateItem.wxml
<template name="msgItem">
<view class="content">我是模板的内容</view>
</template>
//templateItem.wxss
.content {
width: 100%;
height: 400rpx;
background-color: yellowgreen;
display: flex;
justify-content: center;
align-items: center;
}
- 使用模板
要使用模板,首先得在要使用的页面引入模板,使用import标签导入,使用is属性,声明需要使用的模板,比如在index.wxml中使用:
// index.wxml
<import src="../../templates/templateItem/templateItem.wxml" />
<view>
<template is="msgItem" />
</view>
现在,模板的内容可以显示在页面上了,但是模板的样式却没有生效。想要让样式生效,必须在index.wxss 中引入模板的样式文件:
/** index.wxss **/
@import "../../templates/templateItem/templateItem.wxss"
- 模板传值
在index.js中的data中添加一些数据:
// index.js
cosnt app = getApp();
Page({
data: {
hero: {
name: '盖伦',
profession: '战士',
skill: '得玛西亚正义'
}
}
})
修改一下模板:
<template name="msgItem">
<view class="content">
<view class="content">我是模板的内容</view>
<view>
<text>{{ name }}</text>---
<text>{{ professios }}</text>---
<text>{{ skill }}</text>
</view>
</view>
</template>
修改一下index.wxml中的模板引用:
<template is="msgItem" data="{{...hero}}" />
2. component自定义组件
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
- 定义组件
在pages的同级目录下创建components文件夹用来存放所有组件。一个自定义组件由json wxml wxss js 4个文件组成,类似于页面。
要编写一个自定义组件,首先需要在json文件中进行自定义组件声明(将component字段设为true),使用微信开发者工具,创建component会自动生成。
// componentItem.json
{
"component": true
}
在组件文件中编写代码
// componentItem.wxml
<view class="herocontainer">
<view>姓名:{{ hero.name }}</view>
<view>职业:{{ hero.profession }}</view>
<view>技能:{{ hero.skill }}</view>
</view>
在自定义组件的js文件中,需要使用 Component 来注册组件,并提供组件的属性定义、内部数据和自定义方法。
// componentItem.js
Component({
//组件传递的数据
properties: {
// 这里定义了hero属性,可以设置默认值和类型
hero: Object
},
// 内部数据
data: {
},
// 自定义方法
methods: {
}
})
自定义组件样式
注意:在组件wxss中不应使用ID选择器、属性选择器和标签名选择器,请使用class选择器。
/** componentItem.wxss **/
.herocontainer {
width: 750rpx;
height: 200rpx;
background-color: yellow;
color: hotpink;
}
- 使用自定义组件
使用自定义组件前,首先要在要使用的页面的json文件中进行引用声明,此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径。
{
"usingComponents": {
"component-item": "/components/componentItem/componentItem"
}
}
这样,在页面的wxml 中就可以像使用基础组件一样使用自定义组件啦,节点名就是自定义组件的标签名,节点属性就是自定义组件的属性值
// index.wxml
<view>
<component-item hero="{{hero}}" />
</view>
// index.js
cosnt app = getApp();
Page({
data: {
hero: {
name: '盖伦',
profession: '战士',
skill: '得玛西亚正义'
}
}
})
- 自定义组件传值
- 页面=>组件
组件中通过proprtties写上要接受的值,页面通过属性名称和值的方式传递,上面就是页面传值到组件。 - 组件=>页面
页面中写监听事件,组件中写触发事件。
在自定义组件中添加一个事件:
// componentItem.wxml
<button bind:tap="onTap">点击按钮触发事件</button>
在组件的js文件methods中添加方法,通过this.triggerEvent()给页面传值。在data中定义一个数据用来传给页面。
// componentItem.js
Component({
//组件传递的数据
properties: {
hero: Object,
userName:{
type:String,
//调用该组件时传入的值的类型,可以是Number、String、Boolean、Object、Aray、Null(任意类型)
value:"李雷" //默认值,调用该组件时未传入userName时,则userName的值为预设值李雷
}
},
// 内部数据
data: {
message: '欢迎来到英雄联盟'
},
// 自定义方法
methods: {
onTap(){
const message = this.data.message
this.triggerEvent('myevent',message)
}
}
})
在页面中绑定事件
// index.wxml
<component-item hero="{{hero}}" bind:myevent="onMyEvent"/>
在页面的js中,添加一个与data同级的方法,通过事件对象的e.detail拿到传过来的值
// index.js
page({
onMyEvent(e){
console.log(e.detail)
}
})
总结:
template 和 components 的共性: 不能单独呈现出来,必须依附显示在页面
不同点:template比较轻量级,没有自己的逻辑处理能力,只能来着页面显示值
组件要重量级一些,它有自己的逻辑处理能力
参考:
官网-自定义组件
第四章 小程序几个重要API
1. 小程序网络请求
注意事项
服务器域名配置
每个微信小程序需要事先设置通讯域名,小程序只可以跟指定的域名与进行网络通信。包括普通 HTTPS 请求(wx.request)、上传文件(wx.uploadFile)、下载文件(wx.downloadFile) 和 WebSocket 通信(wx.connectSocket)。网络请求
- 默认超时时间和最大超时时间都是 60s;
超时时间可以在 app.json 中通过 networktimeout 配置。 - wx.request、wx.uploadFile、wx.downloadFile 的最大并发限制是 10 个;
wx.connectSockt 的最大并发限制是 5 个。
小程序进入后台运行后,如果 5s 内网络请求没有结束,会回调错误信息 fail interrupted;在回到前台之前,网络请求接口调用都会无法调用。 - 只要成功接收到服务器返回,无论 statusCode 是多少,都会进入 success 回调。请开发者根据业务逻辑对返回值进行判断。
wx.request(Object object) 发起请求
属性 类型 默认值 必填 说明
url string 是 开发者服务器接口地址
data string/object/ArrayBuffer 否 请求的参数
header Object 否 设置请求的header,header中不能设置 Referer。
content-type 默认为 application/json
method string GET 否 HTTP 请求方法
dataType string json 否 返回的数据格式
responseType string text 否 响应的数据类型
success function 否 接口调用成功的回调函数
fail function 否 接口调用失败的回调函数
complete function 否 接口调用结束的回调函数(调用成功、失败都会执行)
示例代码
wx.request({
url: 'test.php', //仅为示例,并非真实的接口地址
data: {
x: '',
y: ''
},
header: {
'content-type': 'application/json' // 默认值
},
success (res) {
console.log(res.data)
}
})
RequestTask 网络请求任务对象
示例代码
const requestTask = wx.request({
url: 'test.php', //仅为示例,并非真实的接口地址
data: {
x: '' ,
y: ''
},
header: {
'content-type': 'application/json'
},
success (res) {
console.log(res.data)
}
})
requestTask.abort() // 取消请求任务
方法
RequestTask.abort()
// 中断请求任务
RequestTask.onHeadersReceived(function callback)
// 监听 HTTP Response Header 事件。会比请求完成事件更早
RequestTask.offHeadersReceived(function callback)
// 取消监听 HTTP Response Header 事件
2. 小程序路由
路由配置
所有页面都必须在app.json中注册,例如:
{
"pages": [
"pages/index/index",
"pages/logs/logs"
]
}
路由跳转方式
- 打开新页面 wx.navigateTo(Object object)
- 注:保留当前页面,跳转到新页面;不能跳到 tabbar 页面
- 进栈
wx.navigateTo({
url: 'test?id=1',
success: function(res) {}
})
// test.js 接收参数
Page({
onLoad: function(option){
console.log(option)
}
})
- 页面重定向 wx.redirectTo(Object object)
- 注:关闭当前页面,跳转到新页面,不能返回;不能跳到 tabbar 页面
- 当期页面出栈新页面入栈
wx.redirectTo({
url: 'test?id=1'
})
- 页面返回 wx.navigateBack(Object object)
- 注: 关闭当前页面,返回上一页面或多级页面。
- 当前页面出栈
wx.navigateBack({
delta: 2 // 返回的页面数,如果 delta 大于现有页面数,则返回到首页。
})
- switchTab 切换 wx.switchTab(Object object)
- 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
- 页面全部出栈,只留下新的 Tab 页面
// 需在 app.json 的 tabBar 字段定义页面
{
"tabBar": {
"list": [{
"pagePath": "index",
"text": "首页"
},{
"pagePath": "other",
"text": "其他"
}]
}
}
// 需要跳转的 tabBar 页面的路径,路径后不能带参数。
wx.switchTab({
url: '/index'
})
- 重启动 wx.reLaunch(Object object)
- 关闭所有页面,打开到应用内的某个页面;可以打开任意页面。
- 页面全部出栈保留当期页面栈
// 跳转页面
wx.reLaunch({
url: 'test?id=1' // 需要跳转的应用内页面路径,路径后可以带参数
})
// test.js 接收参数
Page({
onLoad: function(option){
console.log(option)
}
})
- 获取当前页面栈 getCurrentPages()
- 该函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。在小程序中栈最多只能维护10个数据
- 也可以在某个页面修改另一个页面的data或者调用另一个页面的方法。
var pages = getCurrentPages();
var currentPage = pages[pages.length - 1]; // 当前页面
var prevPage = pages[pages.length - 2]; // 当前页面的上一个页面
// 修改另一个页面的data
prevPage.setData({
Flag: true
})
// 调用另一个页面的方法实现页面重新加载
prevPage.onLoad();
// 应用场景:调用wx.navigateBack()的方法,返回到上级页面并刷新数据
- 使用 navigator 组件跳转
示例代码
// 页面导航元素
<view class="btn-area">
<navigator url="/page/navigate/navigate?title=navigate" hover-class="navigator-hover">跳转到新页面</navigator>
<navigator url="../../redirect/redirect/redirect?title=redirect" open-type="redirect" hover-class="other-navigator-hover">在当前页打开</navigator>
<navigator url="/page/index/index" open-type="switchTab" hover-class="other-navigator-hover">切换 Tab</navigator>
</view>
// 新页面接收参数
Page({
onLoad: function(options) {
this.setData({
title: options.title
})
}
})
- url 当前小程序内的跳转链接
- open-type 跳转方式,默认navigate
navigate 打开新页面 redirect 重定向页面 switchTab 打开Tab页
navigateBack 返回页面 reLaunch 重启动页面 exit 退出小程序 - hover-class 指定点击时的样式类
3. 小程序本地缓存
本地缓存操作接口
操作 异步方法 同步方法
设置缓存 wx.setStorage wx.setStorageSync
获取缓存 wx.getStorage wx.getStorageSync
删除缓存 wx.removeStorage wx.removeStorageSync
清空缓存 wx.clearStorage wx.clearStorageSync
获取缓存信息 wx.getStorageInfo wx.getStorageInfoSync
以Sync结尾都是同步方法。同步方法和异步方法的区别是:
同步方法会堵塞当前任务,直到同步方法处理返回。
异步方法不会堵塞当前任务。使用同步还是异步?
同步写入缓存时,如果缓存太大,会阻塞整个JS的运行,有时候会出现报错的情况。一般情况下建议使用异步写入缓存的方法,少用同步方法。
但是如果有些场景,异步缓存不适合,必须同步去写入缓存。此时,并没有万全之策,最好的办法,是加一个try catch。缓存数据类型
小程序的数据缓存,最大支持10M,可以写入多种类型数据,number、boolean、array、string、object等,基本上,小程序支持的JS数据类型,都支持写入。
示例代码
// 异步设置缓存
wx.setStorage({
key:"key",
data:"value"
})
// 同步设置缓存
try {
wx.setStorageSync('key', 'value')
} catch (e) { }
// 异步获取缓存
wx.getStorage({
key: 'key',
success (res) {
console.log(res.data)
}
})
// 同步获取缓存
try {
var value = wx.getStorageSync('key')
if (value) {
// Do something with return value
}
} catch (e) {
// Do something when catch error
}
// 其它省略 ... ...
第四章 小程序Demo实例
1. 小程序授权登录实现
步骤
- 调用 wx.login 获取 code。
- 使用 wx.getSetting 获取用户的授权情况
如果用户已经授权,直接调用 API wx.getUserInfo 获取用户最新的信息;
用户未授权,在界面中显示一个按钮提示用户登入,当用户点击并授权后就获取到用户的最新信息。 - 将获取到的用户数据连同wx.login返回的code一同传给后端
index.wxml
<view wx:if="{{isHide}}">
<view wx:if="{{canIUse}}" >
<view class='header'>
<image src='/images/wx_login.png'></image>
</view>
<view class='content'>
<view>申请获取以下权限</view>
<text>获得你的公开信息(昵称,头像等)</text>
</view>
<button class='bottom' type='primary' open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="bindGetUserInfo">
授权登录
</button>
</view>
<view wx:else>请升级微信版本</view>
</view>
<view wx:else>
<view>我的首页内容</view>
</view>
index.js
Page({
data: {
//判断小程序的API,回调,参数,组件等是否在当前版本可用。
canIUse: wx.canIUse('button.open-type.getUserInfo'),
isHide: false
},
onLoad: function() {
var that = this;
// 1. wx.getSetting 获取用户的授权情况
wx.getSetting({
success: function(res) {
if (res.authSetting['scope.userInfo']) {
wx.getUserInfo({
success: function(res) {
// 用户已经授权过,不需要显示授权页面,所以不需要改变 isHide 的值
// 根据自己的需求有其他操作再补充
// 我这里实现的是在用户授权成功后,调用微信的 wx.login 接口,从而获取code
wx.login({
success: res => {
// 获取到用户的 code 之后:res.code
console.log("用户的code:" + res.code);
// 可以传给后台,再经过解析获取用户的 openid
// 或者可以直接使用微信的提供的接口直接获取 openid ,方法如下:
// wx.request({
// // 自行补上自己的 APPID 和 SECRET
// url: 'https://api.weixin.qq.com/sns/jscode2session?appid=自己的APPID&secret=自己的SECRET&js_code=' + res.code + '&grant_type=authorization_code',
// success: res => {
// // 获取到用户的 openid
// console.log("用户的openid:" + res.data.openid);
// }
// });
}
});
}
});
} else {
// 用户没有授权
// 改变 isHide 的值,显示授权页面
that.setData({
isHide: true
});
}
}
});
},
bindGetUserInfo: function(e) {
if (e.detail.userInfo) {
//用户按了允许授权按钮
var that = this;
// 获取到用户的信息了,打印到控制台上看下
console.log("用户的信息如下:");
console.log(e.detail.userInfo);
//授权成功后,通过改变 isHide 的值,让实现页面显示出来,把授权页面隐藏起来
that.setData({
isHide: false
});
} else {
//用户按了拒绝按钮
wx.showModal({
title: '警告',
content: '您点击了拒绝授权,将无法进入小程序,请授权之后再进入!!!',
showCancel: false,
confirmText: '返回授权',
success: function(res) {
// 用户没有授权成功,不需要改变 isHide 的值
if (res.confirm) {
console.log('用户点击了“返回授权”');
}
}
});
}
}
})