小程序是一个全新的、轻量级的移动端应用。
在小程序出现之前的移动端开发的解决方案有:
移动端开发发展了一段时间之后也暴漏出来一些问题,传统 App (Android / iOS)的开发和运营成本很高:
为了解决传统 App 的这些问题,小程序应运而生。
最早推出小程序的是微信团队。
小程序上线之后取得了巨大的成功,这导致一些其它平台也跟风上线了小程序的内容,如支付宝、京东、今日头条、百度。
微信小程序推出的最早且目前最有影响力(后文默认介绍微信小程序)。
小程序的运行环境与网页开发不同:
所以小程序与网页开发有一些区别:
小程序的运行环境主要有两个:
不同的操作系统,内部的支持也不一样:
运行环境 | 逻辑层 | 渲染层 |
---|---|---|
iOS | JSCore | WKWebView |
Android | V8 引擎 | Chromium 定制内核 |
虽然不同操作系统的渲染机制和逻辑不一样,但外部的表现是一样的。
下面通过案例学习微信小程序的基础使用,主要包括:
在小程序官网注册:https://mp.weixin.qq.com/wxopen/waregister?action=step1
注意:注册过微信公众号的邮箱,不能再注册微信小程序账号
也可以申请测试号,免注册快速体验小程序开发(不能发布)。
注册成功后,可以在后台获取小程序的 AppID,它是小程序的唯一标识。
去小程序官网,下载安装微信开发者工具:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
详细界面介绍参考官网文档:界面
打开微信开发者工具新建小程序项目:
“不使用模板” 与 “JavaScript - 基础模板” 基本没区别。
本文创建的项目:
下面依次解读小程序初始化项目:
官方文档:
- 目录结构
- 小程序配置
全局文件存放在根目录,文件名是固定的,不能修改,其中包括:
页面文件存放在 pages 文件夹下,初始包括 index 和 logs 两个页面,每个页面由四个文件组成,以 index 为例:
需要注意的是:
.js
、.json
、.wxml
、.wxss
,四个文件的文件名必须一致app.json
的 pages
,保存文件后就会自动创建页面文件例如:
"pages": [
// 未指定 entryPagePath 时,数组的第一项代表小程序的初始页面(首页)
"pages/index/index",
"pages/logs/logs",
// pages 是页面存放目录
// test 是页面存放的子目录
// my-test 是页面文件名称
"pages/test/my-test"
],
小程序生命周期主要指两个循环状态:前台到后台、初始化到销毁。
关于前后台的定义参考官方文档:小程序运行机制
小程序中的生命周期有两类:
app.js
文件中
.js
中
常用小程序生命周期函数及触发时机:
// app.js
App({
onLaunch() {
console.log('onLaunch - 小程序加载')
// 默认初始代码...
},
onShow() {
console.log('onShow - 小程序显示')
},
onHide() {
console.log('onHide - 小程序隐藏')
},
globalData: {
userInfo: null
}
})
保存文件后,小程序会重新初始化,依次触发 onLaunch
和 onShow
。
可点击工具栏上的 切后台
切换到后台触发 onHide
。
PS:如果工具栏没有
切后台
按钮,可以通过工具 - 工具栏管理
设置显示的工具栏图标。
常用页面生命周期函数及触发时机:
修改 test 页面的生命周期函数
// pages/test/test.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
console.log('onLoad - 页面加载')
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
console.log('onReady - 页面就绪')
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
console.log('onShow - 页面显示')
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
console.log('onHide - 页面隐藏')
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
console.log('onUnload - 页面卸载')
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
/**
* 重载页面
*/
goIndex: function() {
// 调用原生 API 重新加载页面
wx.reLaunch({
url: "/pages/index/index"
})
}
})
添加重载功能:
<text bindtap="goIndex">跳转到 indextext>
配置小程序的底部导航栏 tabBar
,用于切换页面:
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/test/test"
],
// 窗口配置
"window": {
"backgroundTextStyle": "light",
// 顶部导航栏背景
"navigationBarBackgroundColor": "#fff",
// 顶部导航栏标题
"navigationBarTitleText": "Weixin",
// 顶部导航栏文字颜色
"navigationBarTextStyle": "black"
},
// 底部导航配置
"tabBar": {
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/logs/logs",
"text": "日志"
},
{
"pagePath": "pages/test/test",
"text": "测试"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents"
}
上面的空白是图标预留,可以自行配置 iconPath
和 selectedIconPath
。
现在点击导航栏切换页面,查看控制台输出:
官方文档 - 生命周期 中有一张图分别介绍了 View(视图)和 AppService(App 服务)两个线程的生命周期顺序:
综合小程序和页面的生命周期就是微信小程序的启动流程:
小程序开发框架提供了丰富的微信原生 API,这些 API 存放在云端(服务器端),不需要自己开发和部署。
除了一些 web API,还提供了一些移动端的 API,只能在移动端调用,如:
原生 API 通过 wx.
调用,详细参考 API 文档。
// app.js
App({
onLaunch() {
console.log('onLaunch - 小程序加载')
// 展示本地存储能力
// 同步方式获取本地缓存数据
const logs = wx.getStorageSync('logs') || []
// 添加当前启动时间
logs.unshift(Date.now())
// 同步方式设置本地缓存数据
wx.setStorageSync('logs', logs)
// 登录
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
},
onShow() {
console.log('onShow - 小程序显示')
},
onHide() {
console.log('onHide - 小程序隐藏')
},
// 全局数据
globalData: {
// 旧版初始化的项目将用户信息保存在这里
// 新版初始化的项目将用户信息保存在 index.js 中
userInfo: null
}
})
以前的版本会在
onLaunch
内部调用wx.getSettings
获取用户当前设置,根据返回的数据判断授权状态,如果用户已授权,则调用wx.getUserInfo
获取用户信息。现在更改为在
pages/index/index.js
中调用的wx.getUserProfile
,每次请求都会弹出授权窗口,用户同意后返回userInfo
。
初始化项目默认启动获取用户信息,可将 canIUseOpenData
设置 false
变更为手动获取:小程序启动后页面会显示 获取头像昵称
,点击请求 wx.getUserProfile
// index.js
// 获取应用实例
// 获取的是 app.js 中生成的小程序的实例
const app = getApp()
Page({
// 绑定数据,类似 vue 的 data
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo'),
canIUseGetUserProfile: false,
canIUseOpenData: wx.canIUse('open-data.type.userAvatarUrl') && wx.canIUse('open-data.type.userNickName') // 如需尝试获取用户信息可改为false
},
// 事件处理函数
bindViewTap() {
wx.navigateTo({
url: '../logs/logs'
})
},
onLoad() {
if (wx.getUserProfile) {
// 修改数据
this.setData({
canIUseGetUserProfile: true
})
}
},
getUserProfile(e) {
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
console.log(res)
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
getUserInfo(e) {
// 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
console.log(e)
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
}
})
官方文档:
- WXML 语法参考
- 视图层 / WXML
- 组件
WXML(WeiXin Markup Language) 是框架设计的一套标签语言。
作用 | 小程序组件 | HTML 标签 |
---|---|---|
展示区块 | view | div |
展示图片 | image | img |
展示文本 | text | p |
链接导航 | navigator | a |
… | … | … |
WXML 的模板可以定义代码片段,在不同地方中调用。
使用 定义模板,使用
引用模板,使用 is
声明需要使用的模板名称,使用 data
传入模板所需的数据。
注意:如果
app.json
配置了"lazyCodeLoading": "requiredComponents"
,开发者工具中使用了引入模板的页面会显示白屏,因为项目开启了按需注入,但真机预览中会正常显示,开发时可以将该配置项删除。
<view class="container">
<view class="userinfo">
<block wx:if="{{canIUseOpenData}}">
<view class="userinfo-avatar" bindtap="bindViewTap">
<open-data type="userAvatarUrl">open-data>
view>
<open-data type="userNickName">open-data>
block>
<block wx:elif="{{!hasUserInfo}}">
<button wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile"> 获取头像昵称 button>
<button wx:elif="{{canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 button>
<view wx:else> 请使用1.4.4及以上版本基础库 view>
block>
<block wx:else>
<image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover">image>
<text class="userinfo-nickname">{{userInfo.nickName}}text>
block>
view>
<view class="usermotto">
<text class="user-motto">{{motto}}text>
view>
view>
// logs.js
// 小程序中的模块化开发遵循 CommonJS 规范(exports 导出,require 导入)
const util = require('../../utils/util.js')
Page({
data: {
logs: []
},
onLoad() {
// 获取日志并设置到 data 中
this.setData({
// 同步获取本地缓存
logs: (wx.getStorageSync('logs') || []).map(log => {
return {
// 格式化时间
date: util.formatTime(new Date(log)),
timeStamp: log
}
})
})
}
})
<view class="container log-list">
<block wx:for="{{logs}}" wx:key="timeStamp" wx:for-item="log">
<text class="log-item">{{index + 1}}. {{log.date}}text>
block>
view>
{
// 优先级高于全局配置的导航标题
"navigationBarTitleText": "查看启动日志",
"usingComponents": {}
}