微信小程序入门总结

一、目录结构

目录结构

1.1 主体(app)

app.jsapp.jsonapp.wxss组成,包括小程序的注册、全局配置、页面注册、公共变量和方法,还有样式。

1.1.1 注册小程序

每个小程序都需要在 app.js 中调用 App() 方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。整个小程序只有一个 App 实例,是全部页面共享的。

App({
  onLaunch: function () {},
  onShow: function () {},
  onHide: function () {},
  globalData: {
    appName: '',
    tplId: {},
    APPID: '',
    domain: '',
    userInfo: null,
    userWXInfo: null
  }
})

开发者可以通过 getApp() 方法获取到全局唯一的 App 实例,获取App上的数据或调用开发者注册在 App 上的函数。

const appInstance = getApp()
console.log(appInstance.globalData.userInfo)

1.1.2 小程序全局配置

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

{
  "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,
  "navigateToMiniProgramAppIdList": [
    "wxe5f52902cf4de896"
  ]
}

1.2 页面(Page)

一个小程序页面由四个文件组成,分别是:index.js、index.wxml、index.json、index.wxss。

⚠️为了方便开发者减少配置项,描述页面的四个文件必须具有相同的路径与文件名。

1.2.1 WXML

WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。具有数据绑定列表渲染条件渲染模板引用等能力。

1.2.1.1 数据绑定

WXML 中的动态数据均来自对应 Page 的 data。语法使用大家熟悉的Mustache,比如:

<view>{{ message }}</view>

Page({
  data: {
    message: 'Hello,World'
  }
})

更新数据需要用到Page原型链上的setData函数

this.setData({
  message: 'updating view'
})
1.2.1.2 列表渲染

在组件上可以使用wx:for绑定一个数组,来循环渲染该组件。使用 wx:for-item 可以指定数组当前元素的变量名,使用 wx:for-index 可以指定数组当前下标的变量名。比如:

<view wx:for="{{ array }}">
  {{ index }}:{{ item.message }}
</view>
<view wx:for="{{ array }}" wx:for-index="idx" wx:for-item="itemName">
  {{ idx }}:{{ itemName.message }}
</view>
Page({
  data: {
    array: [{
      message: 'foo',
    }, {
      message: 'bar'
    }]
  }
})

如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。

wx:key 的值以两种形式提供:

  1. 字符串:代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。

  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。

1.2.1.3 条件渲染

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

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

WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。

<!--
  index: int
  msg: string
  time: string
-->
<template name="msgItem">
  <view>
    <text>{{ index }}: {{ msg }}</text>
    <text>Time: {{ time }}</text>
  </view>
</template>

使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:

<template is="msgItem" data="{{ ...item }}"/>
Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2020-09-11'
    }
  }
})

注意:模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的 <wxs /> 模块。

1.2.1.5 引用

WXML 提供两种文件引用方式importinclude

import

import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。

<!-- A.wxml -->
<template name="A">
  <text> A template </text>
</template>

<!-- B.wxml -->
<import src="a.wxml"/>
<template name="B">
  <text> B template </text>
</template>

<!-- C.wxml -->
<import src="b.wxml"/>
<template is="A"/>  <!-- Error! Can not use tempalte when not import A. -->
<template is="B"/>

include

include 可以将目标文件除了 <template/> <wxs/> 外的整个代码引入,相当于是拷贝到 include 位置,如:

<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>

<!-- header.wxml -->
<view> header </view>

<!-- footer.wxml -->
<view> footer </view>

1.2.2 JS

页面文件夹下的.js文件里面是页面的脚本代码,通过调用Page方法注册页面,在方法内可指定页面的初始数据、生命周期函数、事件处理方法等。

⚠️ 小程序脚本逻辑运行在JSCore中,JSCore没有dom环境,因此小程序完全不支持dom操作

1.2.3 WXSS

页面文件夹下的.wxss文件里面是页面的样式表

1.2.4 JSON

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

二、生命周期

2.1 APP生命周期

APP生命周期

首次(小程序没有运行在后台)打开小程序,触发且只触发一次onLaunch方法。
小程序初始化完成后,触发onShow方法。
小程序切换到后台,触发onHidden方法。
小程序从后台切换到前台,触发onShow方法。
小程序代码出错,触发onError方法。
小程序要跳转的页面不存在,触发onPageNotFound方法。
小程序进入后台一定时间,或者系统资源占用过高,会被销毁。

2.2 页面生命周期

页面生命周期

打开Page页面,页面初始化,触发onLoad方法。
页面初始化完成,进入前台展示,触发onShow方法。
首次渲染页面完毕,触发onReady方法,一个页面只会触发一次。
小程序切换到后台或者跳转到其他页面时,触发onHide方法。
小程序从后台切换到前台或者从其他页面返回本页面时,触发onShow方法。
使用wx.redirectTo()或wx.navigateBack()等重定向方法销毁页面时,触发onUnload方法。

2.3 app生命周期影响page生命周期

生命周期

小程序初始化完成触发App的onShow方法后,Page开始加载并只触发一次Page的onLoad方法。
小程序切换到后台时,先触发Page的onHide方法再触发App的onHide方法。
小程序从后台进入到前台时,先执触发App的onShow方法再触发Page的onShow方法

三、路由与通信

3.1 页面路由

在小程序中所有页面的路由全部由框架进行管理。

开发者可以使用 getCurrentPages() 函数获取当前页面栈。

路由方式 页面栈表现 跳转方式
初始化 新页面入栈 打开小程序
打开新页面 新页面入栈 使用wx.navigateTo方法或<navigator opten-type="navigateTo"/>组件
页面重定向 当前页面出栈,新页面入栈 使用wx.redirectTo方法或<navigator opten-type="redirectTo"/>组件
页面返回 页面不断出栈,直到目标返回页 使用wx.navigateBack方法或<navigator opten-type="navigateBack"/>组件或点击左上角返回按钮
Tab 切换 页面全部出栈,只留下新的 Tab 页面 使用wx.switchTab方法或<navigator opten-type="switchTab"/>组件或点击tab按钮
重加载 页面全部出栈,只留下新的页面 使用wx.reLaunch方法或<navigator opten-type="reLaunch"/>组件

wx.navigateTo():保留当前页面,跳转到应用内除了tabbar页面外的其他某个页面。

wx.redirectTo():关闭当前页面,跳转到应用内除了tabbar页面外的其他某个页面。

wx.navigateBack():关闭当前页面,返回上一页面或多级页面。

wx.switchTab():跳转到tabBar页面,并关闭其他所有非tabBar页面。

wx.reLaunch():关闭所有页面,打开到应用内的某个页面。

页面栈

  • wx.navigateTo()方法会增加页面栈层数,直到页面栈为十层。
  • wx.redirectTo()方法不会增加页面栈层数。
  • wx.navigateBack()方法会减少页面栈层数,直到页面栈层数为一。
  • wx.switchTab()和wx.reLaunch()方法会将页面栈层数变为一。
  • 可以在小程序页面中通过getCurrentPages()方法获取页面栈,获取到的第一个元素为首页,最后一个元素为当前页。
  • 小程序中页面栈最多十层。
  • 不要尝试修改页面栈,会导致路由以及页面状态错误。
  • 不要在App.onLaunch的时候调用getCurrentPages(),此时 page 还没有生成。

3.2 页面通信

小程序页面之间的通信有三种方式:全局变量、本地存储、url传参。

全局变量: globalData

本地存储: 将数据存储在本地缓存中指定的key中,除非用户主动删除或因存储空间原因被系统清理,否则数据都一直可用,且不管是在app.js还是在其他page文件的js中都可对本地存储进行增删改查。

url传参:navigator组件的url参数内,需要跳转的页面路径可用“?”后接参数,参数键与参数值用“=”相连,不同参数用“&”分隔。其他组件可以用data-“参数名”格式绑定属性,js内可用event.currentTarget.dataset.“参数名”的格式获取参数值,然后使用js跳转方法,方法的ur参数内跳转路径用“?”连接跳转参数。

参数接收: 跳转后的页面在onLoad生命周期函数中可以接收一个参数,该参数以json的形式接收url跳转传递的值

Page({
    onLoad: function(options) {
        console.log(options)
    }
})

四、常用功能

4.1 小程序登录

小程序登录
  1. 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID会话密钥 session_key

4.2 UnionID 机制说明

如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,UnionID是相同的。

4.3 获取手机号

获取微信用户绑定的手机号,需先调用wx.login接口。

因为需要用户主动触发才能发起获取手机号接口,所以该功能不由 API 来调用,需用 button 组件的点击来触发。

<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
Page({
  getPhoneNumber (e) {
    console.log(e.detail.errMsg)
    console.log(e.detail.iv)
    console.log(e.detail.encryptedData)
  }
})

4.4 分享转发

通过给 button 组件设置属性 open-type="share",可以在用户点击按钮后触发 Page.onShareAppMessage 事件:

<button open-type="share"></button>

4.5 授权

获取用户授权: 开发者可以使用wx.getSetting获取用户当前的授权状态。

打开设置页面: 开发者可以使用wx.openSetting打开设置界面,引导用户开启授权。

提前发起授权请求: 开发者可以使用wx.authorize在调用需授权 API 之前,提前向用户发起授权请求。

常用授权接口:

scope 对应接口 描述
scope.userInfo wx.getUserInfo 用户信息
scope.userLocation wx.getLocation, wx.chooseLocation 地理位置
scope.userLocationBackground wx.startLocationUpdateBackground 后台定位
scope.address wx.chooseAddress 通讯地址
scope.invoiceTitle wx.chooseInvoiceTitle 发票抬头
scope.invoice wx.chooseInvoice 获取发票
scope.werun wx.getWeRunData 微信运动步数
scope.record wx.startRecord 录音功能
scope.writePhotosAlbum wx.saveImageToPhotosAlbum, wx.saveVideoToPhotosAlbum 保存到相册
scope.camera camera组件 摄像头
  • 如果用户未接受或拒绝过此权限,会弹窗询问用户,用户点击同意后方可调用接口;
  • 如果用户已授权,可以直接调用接口;
  • 一旦用户明确同意或拒绝过授权,其授权关系会记录在后台,直到用户主动删除小程序。
  • 如果用户已拒绝授权,则不会出现弹窗,而是直接进入接口 fail 回调。请开发者兼容用户拒绝授权的场景。

⚠️ 注意:

  1. wx.authorize({scope: "scope.userInfo"}),不会弹出授权窗口,请使用<button open-type="getUserInfo" />
  2. 需要授权 scope.userLocationscope.userLocationBackground 时必须配置地理位置用途说明

4.6 获取系统信息

小程序可以使用wx.getSystemInfo()方法获取系统信息。

wx.getSystemInfo({
  success (res) {
    console.log(res.model)
    console.log(res.pixelRatio)
    console.log(res.windowWidth)
    console.log(res.windowHeight)
    console.log(res.language)
    console.log(res.version)
    console.log(res.platform)
  }
})

wx.getSystemInfoSync()为wx.getSystemInfo()的同步版本。

4.7 获取图片

小程序可以使用wx.chooseImage()方法获取图片,来源可以选择系统相册或相机拍照。

wx.chooseImage({
  count: 1,
  sizeType: ['original', 'compressed'],
  sourceType: ['album', 'camera'],
  success (res) {
    // tempFilePath可以作为img标签的src属性显示图片
    const tempFilePaths = res.tempFilePaths
  }
})

wx.chooseImage()方法获取到的文件路径为本地临时路径,需要将文件长期保存需要调用wx.saveFile()方法。

wx.chooseImage({
  success: function(res) {
    const tempFilePaths = res.tempFilePaths
    wx.saveFile({
      tempFilePath: tempFilePaths[0],
      success (res) {
        const savedFilePath = res.savedFilePath
      }
    })
  }
})

4.8 扫码

小程序使用wx.scanCode()方法可以调起客户端扫码界面进行扫码

// 允许从相机和相册扫码
wx.scanCode({
  success (res) {
    console.log(res)
  }
})

// 只允许从相机扫码
wx.scanCode({
  onlyFromCamera: true,
  success (res) {
    console.log(res)
  }
})

五、其他开发事项

5.1 原生组件

小程序中的部分组件是由客户端创建的原生组件,这些组件有:

由于原生组件脱离在 WebView 渲染流程外,因此在使用时有以下限制:

  • 原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。后插入的原生组件可以覆盖之前的原生组件。
  • 原生组件无法在picker-view组件中使用。基础库 2.4.4 以下版本,原生组件不支持在 scroll-view、swiper、movable-view 中使用。
  • 部分CSS样式无法应用于原生组件,例如:无法对原生组件设置CSS动画;无法定义原生组件为 position:fixed;不能在父级节点使用overflow:hidden来裁剪原生组件的显示区域。
  • 原生组件的事件监听不能使用bind:eventname的写法,只支持bindeventname。原生组件也不支持catch和capture的事件绑定方式。
  • 原生组件会遮挡vConsole弹出的调试面板。在工具上,原生组件是用web组件模拟的,因此很多情况并不能很好的还原真机的表现,建议在使用到原生组件时尽量在真机上进行调试。

为了解决原生组件层级最高的限制。小程序专门提供了 cover-viewcover-image 组件,可以覆盖在部分原生组件上面。这两个组件也是原生组件,但是使用限制与其他原生组件有所不同。

可覆盖的原生组件包括 map、video、canvas、camera、live-player、live-pusher
只支持嵌套 cover-view、cover-image、button。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)。

5.2 分包

某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。

在构建小程序分包项目时,构建会输出一个或多个分包。每个使用分包小程序必定含有一个主包。所谓的主包,即放置默认启动页面/TabBar 页面,以及一些所有分包都需用到公共资源/JS 脚本;而分包则是根据开发者的配置进行划分。

在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示。

目前小程序分包大小有以下限制:

  • 整个小程序所有分包大小不超过 16M
  • 单个分包/主包大小不能超过 2M

对小程序进行分包,可以优化小程序首次启动的下载时间,以及在多团队共同开发时可以更好的解耦协作。

开发者通过在 app.json subpackages 字段声明项目分包结构:

{
  "pages":[
    "pages/index",
    "pages/logs"
  ],
  "subpackages": [
    {
      "root": "packageA",
      "pages": [
        "pages/cat",
        "pages/dog"
      ]
    }, {
      "root": "packageB",
      "name": "pack2",
      "pages": [
        "pages/apple",
        "pages/banana"
      ]
    }
  ]
}

六、参考

https://developers.weixin.qq.com/miniprogram/dev/api/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270