小程序01 入門
準備工作
申請賬號
點擊 https://mp.weixin.qq.com/wxopen/waregister?action=step1 申請賬號,擁有自己的小程序賬號
安裝開發工具
前往 開發者工具下載頁面 下載開發工具并安裝
app.json
app.json
扮演的了靜態配置的角色,在小程序運行之前就決定了小程序一些表現,需要注意的是小程序是無法在運行過程中去動態更新JSON 配置文件從而發生對應的變化的。
{
"pages":[
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
}
}
配置IDE
小程序的WXML文件相當于HTML文件,WXSS文件相當于CSS文件,所以,在webstorm的setting
中找到File Types
→Cascading Style Sheet
,添加*.wxss
同理,在File Types
→HTML
,添加*.wxml
Github上有人編寫了wx的語法提醒,下載下來,然后在webStorm 的 File -> import settings 中導入即可
WXML
WXML 全稱是 WeiXin Markup Language,是小程序框架設計的一套標簽語言,結合小程序的基礎組件、事件系統,可以構建出頁面的結構。
數據綁定
使用{{}}
綁定數據,在JS中通過data
屬性傳入數據
<text>當前時間:{{time}}</text>
<text data-test="{{test}}"> hello world</text>
支持在模版內進行邏輯運算、字符串拼接
條件邏輯
條件邏輯使用wx:if
/wx:elif
/wx:else
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
列表渲染
使用wx:for
進行列表渲染,默認數組的當前項的下標變量名默認為index
,數組當前項的變量名默認為item
<view wx:for="{{arr}}">{{index}} ---- {{item}}</view>
可以通過wx:for-index
和wx:for-item
指定數組當前下標的變量名
<view wx:for="{{arr}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}} ---- {{itemName}}
</view>
為保證列表的可復用性,增加wx:key
來作為列表項目的唯一標識符。
<switch wx:for="{{objectArray}}" wx:key="unique" > {{item.id}} </switch>
注意,wx:for
后面變量有{{}}
,而wx:for-index
和wx:for-item
和 wx:key
后面變量沒有{{}}
模版
將內容用<template>
標簽包裹起來,并增加name
屬性,<template>
中內容不會顯示,會在使用<template>
標簽增加對應的is
屬性時使用
<template name="test">模板內容<template>
<template is="test"></tempate>
可以對is
屬性后面綁定數據進行動態模板渲染。
引用
引用的方式有兩種:import
和include
使用import
引用時不會遞歸引用,就是說C引用B,B引用A,在C中可以使用B定義的template
,在B中可以使用A定義的template
,但是C不能使用A定義的template
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
使用include
應用時,會將目標文件中除了<template/>``<wxs/>
外的整個代碼引入,相當于是拷貝到include
位置
<!-- index.wxml -->
<include src="header.wxml"/>
<view> body </view>
<include src="footer.wxml"/>
共同屬性
所有wxml標簽都支持的一些屬性:
- id
- class
- style
- hidden
,布爾值,組件是否顯示,默認顯示
- data-*
,自定義屬性,組件上觸發的事件會發送給事件處理函數
- bind*/catch*
:綁定事件
WXSS
WXSS(WeiXin Style Sheets)是一套用于小程序的樣式語言,用于描述WXML的組件樣式,與CSS類似。為了更適合小程序開發,WXSS對CSS做了一些補充以及修改。
組成
- 項目公共樣式:位于根目錄的
app.wxss
,它會被注入到小程序的每個頁面 - 頁面樣式:與
app.json
注冊過的頁面同名且位置同級的WXSS
文件 - 其它樣式:其它樣式可以被項目公共樣式和頁面樣式引用,
尺寸單位
WXSS引入了rpx
(responsive pixel)作為尺寸單位,可以適配不同寬度屏幕。小程序編譯后,會將rpx
換算為px
,換算是以375個物理像素為基準,也就是在一個寬度為375物理像素的屏幕下,1rpx = 1px。
舉個例子:iPhone6屏幕寬度為375px,共750個物理像素,那么1rpx = 375 / 750 px = 0.5px。
引用
可以使用@import
實現樣式的引用
@import './test_0.wxss'
由于WXSS最終會被編譯打包到目標文件中,用戶只需要下載一次,在使用過程中不會因為樣式的引用而產生多余的文件請求。
內聯樣式
WXSS內聯樣式與Web開發一致:
<!--index.wxml-->
<!--內聯樣式-->
<view style="color: red; font-size: 48rpx"></view>
小程序支持動態更新內聯樣式
選擇器
目前支持的選擇器有:
- id選擇器
- 類選擇器
- 元素選擇器
- 偽元素選擇器::after
/::before
WXSS選擇器權重與CSS類似
官方樣式庫
WeUI.wxss是官方提供的基礎樣式庫,與微信原生視覺體驗一致
JavaScript腳本
組成
小程序中的JavaScript包含三個部分:
1. ECMAScript
2. 小程序框架
3. 小程序API
與瀏覽器中運行的JavaScript沒有BOM和DOM對象。
小程序執行環境
小程序可以運行在三個平臺:
- iOS平臺,包括iOS9、iOS10、iOS11
- Android平臺
- 小程序IDE
在小程序中, iOS9和iOS10所使用的運行環境并沒有完全的兼容到ES6標準,小程序IDE提供語法轉碼工具幫助開發者,將ES6代碼轉ES5代碼(開發者需要在項目設置中,勾選 ES6 轉 ES5 開啟此功能)
模塊化
小程序提供了類似webpack的模塊化機制
執行順序
小程序的執行的入口文件是app.js
。并且會根據其中require
的模塊順序決定文件的運行順序
當app.js
執行結束后,小程序會按照開發者在app.json
中定義的pages
的順序,逐一執行。
作用域
同瀏覽器中運行的腳本文件有所不同,小程序的腳本的作用域同NodeJS更為相似。
在文件中聲明的變量和函數只在該文件中有效,不同的文件中可以聲明相同名字的變量和函數,不會互相影響。
當需要使用全局變量的時,通過使用全局函數getApp()
獲取全局的實例,并設置相關屬性值,來達到設置全局變量的目的
// 獲取全局變量
var global = getApp()
global.globalValue = 'globalValue'
當需要保證全局的數據可以在任何文件中安全的被使用到,那么可以在App()
中進行設置,
// app.js
App({
globalData: 1
})
// a.js
// 局部變量
var localValue = 'a'
// 獲取 global 變量
var app = getApp()
// 修改 global 變量
app.globalData++ // 執行后 globalData 數值為 2
理解小程序宿主環境
渲染層和邏輯層
小程序的運行環境分成渲染層和邏輯層了,渲染層渲染數據,邏輯層在Page實例里產生、處理數據(setData
)
通信模型
小程序的渲染層和邏輯層分別由2個線程管理:
- 渲染層的界面使用了WebView進行渲染
- 邏輯層采用JsCore線程運行JS腳本
一個小程序存在多個界面,所以渲染層存在多個WebView線程,這兩個線程的通信會經由微信客戶端(下文中也會采用Native來代指微信客戶端)做中轉,邏輯層發送網絡請求也經由Native轉發。
數據驅動
和流行的前端框架一樣,小程序也是使用數據驅動頁面視圖的。
邏輯層和渲染層
雙線程下的界面渲染
小程序的邏輯層和渲染層是分開的兩個線程。
在渲染層,宿主環境會把WXML轉化成對應的JS對象,在邏輯層發生數據變更的時候,我們需要通過宿主環境提供的setData
方法把數據從邏輯層傳遞到渲染層,再經過對比前后差異,把差異應用在原來的Dom樹上,渲染出正確的UI界面
程序和頁面
程序
APP()
構造器
宿主環境提供了App()
構造器來注冊一個程序App,App()
必須卸載根目錄的app.js
里,是一個單利對象,在其他腳本中使用getApp()
獲取程序實例
var appInstance = getApp()
App()
接受一個Object
參數:
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})
onLaunch
:小程序初始化完成后全局執行一次onShow
:小程序啟動或從后臺進入前臺顯示時觸發onHide
:小程序從前臺進入后臺時觸發onError
:當小程序發生腳本錯誤,或者API調用失敗時,會觸發并帶上錯誤信息globalData
:將全局變量定義在其中- 其他:可以添加任意的函數或數據到
Object
參數中,在App實例回調用this
可以訪問
進入小程序之后,用戶可以點擊左上角的關閉,或者按手機設備的Home鍵離開小程序,此時小程序并沒有被直接銷毀,我們把這種情況稱為“小程序進入后臺狀態”,App構造器參數所定義的
onHide
方法會被調用。
App的生命周期是由微信客戶端根據用戶操作主動觸發的。為了避免程序上的混亂,我們不應該從其他代碼里主動調用App實例的生命周期函數。
在微信客戶端中打開小程序有很多途徑:從群聊會話里打開,從小程序列表中打開,通過微信掃一掃二維碼打開,從另外一個小程序打開當前小程序等,針對不同途徑的打開方式,小程序有時需要做不同的業務處理,所以微信客戶端會把打開方式帶給onLaunch
和onShow
的調用參數options
需要留意小程序的宿主環境在迭代更新過程會增加不少打開場景,因此要獲取最新的場景值說明請查看官方文檔。
小程序全局數據
小程序的JS腳本是運行在JsCore的線程里,小程序的每個頁面各自有一個WebView線程進行渲染,所以小程序切換頁面時,小程序邏輯層的JS腳本運行上下文依舊在同一個JsCore線程中。
所有頁面的腳本邏輯都跑在同一個JsCore線程,所以頁面使用setTimeout
或者setInterval
的定時器,然后跳轉到其他頁面時,這些定時器并沒有被清除,需要開發者自己在頁面離開的時候進行清理。
頁面
組成
一個頁面是分三部分組成:
- 界面:界面由WXML文件和WXSS文件來負責描述
- 配置:由JSON文件進行描述
- 邏輯:頁面邏輯則是由JS腳本文件負責
一個頁面的文件需要放置在同一個目錄下,其中WXML文件和JS文件是必須存在的,JSON和WXSS文件是可選的。
頁面路徑需要在小程序代碼根目錄app.json
中的pages
字段聲明,否則這個頁面不會被注冊到宿主環境中。默認pages
字段的第一個頁面路徑為小程序的首頁。
Page()
構造器
宿主環境提供了Page()
構造器用來注冊一個小程序頁面,Page()
在頁面腳本page.js
中調用,接受一個Object
參數
Page({
data: { text: "This is page data." },
onLoad: function(options) { },
onReady: function() { },
onShow: function() { },
onHide: function() { },
onUnload: function() { },
onPullDownRefresh: function() { },
onReachBottom: function() { },
onShareAppMessage: function () { },
onPageScroll: function() { }
})
data
:頁面的初始數據- 生命周期函數:
onLoad
:監聽頁面加載,觸發時機早于onShow
和onReady
,在頁面沒被銷毀之前只會觸發1次,在回調中可以獲取當前頁面所調用的打開參數option
onShow
:監聽頁面顯示,觸發事件早于onReady
,一般從別的頁面返回到當前頁面時,都會被調用。onReady
:監聽頁面初次渲染完成,在頁面沒被銷毀前只會觸發1次,onReady
觸發時,表示頁面已經準備妥當,在邏輯層就可以和視圖層進行交互了。onHide
:監聽頁面隱藏,會在使用wx.naviagteTo
切換到其他頁面、底部tab
切換時觸發。onUnload
:監聽頁面卸載,使用wx.redirectTo
或wx.navigateBack
返回到其他頁時,當前頁面會被微信客戶端銷毀回收
- 頁面事件函數:
onPullDownRefresh
:監聽用戶下拉動作,需要在app.json
的window
選項中或頁面配置page.json
中設置enablePullDownRefresh
為true
。當處理完數據刷新后,wx.stopPullDownRefresh
可以停止當前頁面的下拉刷新。onReachBottom
:頁面上拉觸底事件的處理函數,可以在app.json
的window
選項中或頁面配置page.json
中設置觸發距離onReachBottomDistance
。在觸發距離內滑動期間,本事件只會被觸發一次。onShareAppMessage
:用戶點擊右上角轉發,只有定義了此事件處理函數,右上角菜單才會顯示“轉發”按鈕,在用戶點擊轉發按鈕的時候會調用,此事件需要return
一個Object
,包含title
和path
兩個字段,用于自定義轉發內容,onPageScroll
:頁面滾動觸發事件的處理函數,參數為Object
,包含scrollTop
字段,表示頁面在垂直方向已滾動的距離(單位px)。
- 其他:可以添加任意的函數或數據,在
Page
實例的其他函數中用this
可以訪問
Page
的生命周期是由微信客戶端根據用戶操作主動觸發的。為了避免程序上的混亂,我們不應該在其他代碼中主動調用Page
實例的生命周期函數。
setData()
方法
setData
是一個異步函數,第二個參數是一個callback回調,在這次setData
對界面渲染完畢后觸發。
// page.js
Page({
onLoad: function(){
this.setData({
text: 'change data'
}, function(){
// 在這次setData對界面渲染完畢后觸發
})
}
})
data
中的key
還可以非常靈活,以數據路徑的形式給出,例如:
this.setData({"d[0]": 100});
this.setData({"d[1].text": 'Goodbye'});
我們只要保持一個原則就可以提高小程序的渲染性能:每次只設置需要改變的最小單位數據。
此外需要注意以下3點:
- 直接修改
Page
實例的this.data
而不調用this.setData
是無法改變頁面的狀態的,還會造成數據不一致。 - 由于
setData
是需要兩個線程的一些通信消耗,為了提高性能,每次設置的數據不應超過1024kB。 - 不要把
data
中的任意一項的value
設為undefined
,否則可能會有引起一些不可預料的bug。
頁面跳轉
小程序把頁面的打開路徑定義成頁面URL,其組成格式和網頁的URL類似,在頁面Page
構造器里onLoad
的option
可以拿到當前頁面的打開參數,其類型是一個Object
,其鍵值對與頁面URL上query
鍵值對一一對應
和網頁URL一樣,頁面URL上的value
如果涉及特殊字符(例如:&字符、?字符、中文字符等),需要采用UrlEncode
后再拼接到頁面URL上。
通過wx.navigateTo
推入一個新的頁面,在首頁使用2次wx.navigateTo
后,頁面層級會有三層,我們把這樣的一個頁面層級稱為頁面棧。
wx.navigateTo({url: '/pages/index/index'})
我們采用這樣的方式進行描述頁面棧:[pageA, pageB, pageC]
,其中pageA
在最底下,pageC
在最頂上,也就是用戶所看到的界面
- 使用
wx.navigateTo({ url: 'pageD' })
可以往當前頁面棧多推入一個pageD
,此時頁面棧變成[pageA, pageB, pageC, pageD]
- 使用
wx.navigateBack()
可以退出當前頁面棧的最頂上頁面,此時頁面棧變成[pageA, pageB, pageC]
- 使用
wx.redirectTo({ url: 'pageE' })
是替換當前頁變成pageE
,此時頁面棧變成[pageA, pageB, pageE ]
,當頁面棧到達10層沒法再新增的時候,往往就是使用redirectTo
這個API進行頁面跳轉。
小程序提供了原生的Tabbar
支持,我們可以在app.json
聲明tabBar
字段來定義Tabbar
頁
{
"tabBar": {
"list": [
{ "text": "Tab1", "pagePath": "pageA" },
{ "text": "Tab1", "pagePath": "pageF" },
{ "text": "Tab1", "pagePath": "pageG" }
]
}
}
wx.switchTab({ url: 'pageF' })
,此時原來的頁面棧會被清空(除了已經聲明為Tabbar
頁pageA
外其他頁面會被銷毀),然后會切到pageF
所在的tab頁面,頁面棧變成 [pageF]
,此時點擊Tab1
切回到pageA
時,pageA
不會再觸發onLoad
,因為pageA
沒有被銷毀。
wx.navigateTo
和wx.redirectTo
只能打開非TabBar
頁面,wx.switchTab
只能打開Tabbar
頁面。
總結一下,切換路由的方式有:
1. navigateTo
2. navigateBack
3. redirectTo
4. switchTab
前三者只能打開非TabBar
頁面,最后一個只能打開Tabbar
頁面。
注意Tabbar頁面初始化之后不會被銷毀。
組件
提供一些現成的組件,具體參考:https://developers.weixin.qq.com/miniprogram/dev/component/
API
宿主環境提供了豐富的API,可以很方便調起微信提供的能力。幾乎所有小程序的API都掛載在wx
對象底下(除了Page
/App
等特殊的構造器)
小程序提供的API按照功能主要分為幾大類:網絡、媒體、文件、數據緩存、位置、設備、界面、界面節點信息還有一些特殊的開放接口,API一般調用的約定:
wx.on*
開頭的API是監聽某個事件發生的API接口,接受一個Callback函數作為參數。當該事件觸發時,會調用Callback函數。- 如未特殊約定,多數API接口為異步接口 ,都接受一個
Object
作為參數。 - API的
Object
參數一般由success
、fail
、complete
三個回調來接收接口調用結果。 wx.get*
開頭的API是獲取宿主環境數據的接口。wx.set*
開頭的API是寫入數據到宿主環境的接口。
有部分API會拉起微信的原生界面,此時會觸發Page
的onHide
方法,當用戶從原生界面返回到小程序時,會觸發Page
的onShow
方法。
API列表:https://developers.weixin.qq.com/miniprogram/dev/api/
事件
事件類型
常見的事件類型
- touchstart
- touchmove
- touchcancel
:手指觸摸動作被打斷,如來電提醒,彈窗
- touchend
- tap
:手指觸摸后馬上離開
- longpress
:手指觸摸后,超過350ms再離開,如果指定了事件回調函數并觸發了這個事件,tap事件將不被觸發
- longtap
:(推薦使用longpress事件代替)
- transitionend
:會在WXSS transition或wx.createAnimation動畫結束后觸發
- animationstart
- animationiteration
:會在一個 WXSS animation 一次迭代結束時觸發
- animationend
事件的回調函數會受到一個事件對象,具備以下屬性:
- type
- timeStamp
:頁面打開到觸發事件所經過的毫秒數
- target
:觸發事件的組件的一些屬性值集合
- id
- tagName
- dataset:當前組件上由
data-開頭的自定義屬性組成的集合
currentTarget
-:當前組件的一些屬性值集合
detail
-:額外的信息
touches
-:觸摸事件,當前停留在屏幕中的觸摸點信息的數組
identifier
-:觸摸點的標識符
pageX
-,
pageY:距離文檔左上角的距離,文檔的左上角為原點 ,橫向為X軸,縱向為Y軸
clientX
-,
clientY:距離頁面可顯示區域(屏幕除去導航條)左上角距離,橫向為X軸,縱向為Y軸
changedTouches`:觸摸事件,當前變化的觸摸點信息的數組
-
事件綁定
以bind
或catch
開頭,在基礎庫1.5版本后,bind
和catch
后面可以加一個冒號,后面是事件類型,如bind:tap
、catch:touchstart
同時bind
和catch
前還可以加上capture-
來表示捕獲階段。
以下示例中,點擊inner view
會先后調用handleTap1
、handleTap2
、handleTap3
、handleTap4
。
<view id="outer" bind:tap="handleTap4" capture-bind:tap="handleTap1">
outer view
<view id="inner" bind:tap="handleTap3" capture-bind:tap="handleTap2">
inner view
</view>
</view>
bind
不會阻止事件冒泡,catch
會阻止冒泡
如果將以上代碼的capture-bind:tap="handleTap1"
改成capture-catch:tap="handleTap1"
,點擊inner view
只會觸發handleTap1
(catch
事件阻止了tap
事件捕獲和冒泡)。
除上面列舉的事件類型之外的其他組件自定義事件,如無特殊聲明都是非冒泡事件,如<form/>
的submit
事件,<input/>
的input事件,<scroll-view/>
的scrol
l事件(即旨在本身觸發一次,不會向內或向外傳播)。
兼容
可以使用wx.getSystemInfo
或者wx.getSystemInfoSync
來獲取手機品牌、操作系統版本號、微信版本號以及小程序基礎庫版本號等,通過這個信息,我們可以針對不同平臺做差異化的服務。
wx.getSystemInfoSync()
/*
{
brand: "iPhone", // 手機品牌
model: "iPhone 6", // 手機型號
platform: "ios", // 客戶端平臺
system: "iOS 9.3.4", // 操作系統版本
version: "6.5.23", // 微信版本號
SDKVersion: "1.7.0", // 小程序基礎庫版本
language: "zh_CN", // 微信設置的語言
pixelRatio: 2, // 設備像素比
screenHeight: 667, // 屏幕寬度
screenWidth: 375, // 屏幕高度
windowHeight: 667, // 可使用窗口寬度
windowWidth: 375, // 可使用窗口高度
fontSizeSetting: 16 // 用戶字體大小設置
}
*/
可以通過判斷此API是否存在來做程序上的兼容。
if (wx.openBluetoothAdapter) {
wx.openBluetoothAdapter()
} else {
// 如果希望用戶在最新版本的客戶端上體驗您的小程序,可以這樣子提示
wx.showModal({
title: '提示',
content: '當前微信版本過低,無法使用該功能,請升級到最新微信版本后重試。'
})
}
小程序還提供了wx.canIUse
這個API,用于判斷接口或者組件在當前宿主環境是否可用,其參數格式為: ${API}.${method}.${param}.${options}
或者${component}.${attribute}.${option}
各個段的含義如下:
${API}
代表 API 名字${method}
代表調用方式,有效值為return
,success
,object
,callback
${param}
代表參數或者返回值${options}
代表參數的可選值${component}
代表組件名字${attribute}
代表組件屬性${option}
代表組件屬性的可選值
調用的示例代碼如下。
// 判斷接口及其參數在宿主環境是否可用
wx.canIUse('openBluetoothAdapter')
wx.canIUse('getSystemInfoSync.return.screenWidth')
wx.canIUse('getSystemInfo.success.screenWidth')
wx.canIUse('showToast.object.image')
wx.canIUse('onCompassChange.callback.direction')
wx.canIUse('request.object.method.GET')
// 判斷組件及其屬性在宿主環境是否可用
wx.canIUse('contact-button')
wx.canIUse('text.selectable')
wx.canIUse('button.open-type.contact')
我們可以選擇合適的判斷方法來做小程序的向前兼容,以保證我們的小程序在舊版本的微信客戶端也能工作正常。
在不得已的情況下(小程序強依賴某個新的API或者組件時),還可以通過在小程序管理后臺設置“基礎庫最低版本設置”來達到不向前兼容的目的。例如你選擇設置你的小程序只支持1.5.0版本以上的宿主環境,那么當運行著1.4.0版本宿主環境的微信用戶打開你的小程序的時候,微信客戶端會顯示當前小程序不可用,并且提示用戶應該去升級微信客戶端。
場景應用
開發流程
- 首先通過交互圖或者收稿描繪小程序界面交互和界面間的跳轉關系
- 緊接著,我們優先完成WXML+WXSS還原設計稿,把界面涉及到的元素和視覺細節先調試完成
- 最后我們把按照頁面交互梳理出每個頁面的data部分,填充WXML的模板語法,還有完成JS邏輯部分。
布局
建議使用flex布局
截面常見的交互反饋
觸摸反饋
點擊按鈕時需要給一個反饋:小程序的view
容器組件和button
組件提供了hover-class
屬性,觸摸時會往該組件加上對應的class
改變組件的樣式。
/*page.wxss */
.hover{
background-color: gray;
}
<!--page.wxml -->
<button type="default" hover-class="hover"> 點擊button </button>
<view hover-class="hover"> 點擊view</view>
button
組件可以增加loading
屬性,在按鈕文字前出現loading,讓用戶感覺操作會比較耗時:
:
<!--page.wxml -->
<button loading="{{loading}}" bindtap="tap">操作</button>
// page.js
Page({
data: { loading: false }
tap: function() {
// 把按鈕的loading狀態顯示出來
this.setData({
loading: true
})
// 接著做耗時的操作
}
})
Toast和模態框
Toast告知用戶操作成功并且不打斷接下來的操作,默認1.5s后消失
wx.showToast({
title: '已發送',
icon: 'success',
duration: 1599
})
特別要注意,我們不應該把Toast用于錯誤提示,因為錯誤提示需要明確告知用戶具體原因,因此不適合用這種一閃而過的Toast彈出式提示。一般需要用戶明確知曉操作結果狀態的話,會使用模態對話框來提示,同時附帶下一步操作的指引。
wx.showModal({
title: '標題',
content: '告知當前狀態,信息和解決方法',
confirmText: '主操作',
cancelText: '次要操作',
success: function(res) {
if (res.confirm) {
console.log('用戶點擊主操作')
} else if (res.cancel) {
console.log('用戶點擊次要操作')
}
}
})
界面滾動
下拉整個界面刷新:需要通過配置開啟當前頁面的下拉刷新,用戶往下拉動界面觸發下拉刷新操作時,Page
構造器的onPullDownRefresh
回調會被觸發,
//page.json
{
"enablePullDownRefresh": true
}
//page.js
Page({
onPullDownRefresh: function() {
// 用戶觸發了下拉刷新操作
// 拉取新數據重新渲染界面
// wx.stopPullDownRefresh() // 可以停止當前頁面的下拉刷新。
}
})
上拉觸底:
//page.json
// 界面的下方距離頁面底部距離小于onReachBottomDistance像素時觸發onReachBottom回調
{
"onReachBottomDistance": 100
}
//page.js
Page({
onReachBottom: function() {
// 當界面的下方距離頁面底部距離小于100像素時觸發回調
}
})
頁面中某一小塊區域需要可滾動需要使用scroll-view
可滾動視圖組件。可以通過組件的scroll-x
和scroll-y
屬性決定滾動區域是否可以橫向或者縱向滾動
網絡請求
wx.request
接口
發起網絡請求使用wx.request
接口
wx.request({
url: 'https://test.com/getinfo',
success: function(res) {
console.log(res) // 服務器回包信息
}
})
參數:
- url
:接口地址
- data
:請求的參數
- header
:設置請求的header,header中不能設置Referer
,默認header['content-type'] = 'application/json'
- method
:(需大寫)有效值:OPTIONS
, GET
(默認), HEAD
, POST
, PUT
, DELETE
, TRACE
, CONNECT
- dataType
:回包的內容格式,如果設為json,會嘗試對返回的數據做一次 JSON解析
- success
- fail
- complete
小程序宿主環境要求request
發起的網絡請求必須是https
協議請求
請求參數
請求參數有兩種形式:直接在url
中增加參數或通過data
參數
// 通過url參數傳遞數據
wx.request({
url: 'https:/ / test.com / getinfo ? id = 1 & version = 1.0.0 ',
success: function(res) {
console.log(res) // 服務器回包信息
}
})
// 通過data參數傳遞數據
wx.request({
url: '
https : //test.com/getinfo',
data: {
id: 1,
version: '1.0.0'
},
success: function(res) {
console.log(res) // 服務器回包信息
}
})
有時候需要傳一些比較復雜的數據結構到后臺的時候,用JSON格式會更加合適。此時我們可以在wx.request
的header
參數設置content-type
頭部為application/json
,小程序發起的請求的包體內容就是data
參數對應的JSON
字符串
收到回包
回調函數中的參數信息包含的字段:
- data
: 服務器返回數據
- statusCode
:開發者服務器返回的 HTTP 狀態碼
- header
:開發者服務器返回的 HTTP Response Header
尤其注意,只要成功收到服務器返回,無論HTTP狀態碼是多少都會進入success
回調。因此開發者自己通過對回包的返回碼進行判斷后再執行后續的業務邏輯。
success
回調的參數data字段類型是根據header['content-type']
決定的,默認header['content-type']
是'application/json'
,在觸發success
回調前,小程序宿主環境會對data
字段的值做JSON解析,如果解析成功,那么data
字段的值會被設置成解析后的Object
對象,其他情況data
字段都是String
類型,其值為HTTP
回包包體。
一般使用技巧
設置超時時間:
小程序request默認超時時間是60秒,在小程序項目根目錄里邊的app.json
可以指定request
的超時時間。
{
"networkTimeout": {
"request":
3000
}
}
wx.request
常見的示例代碼
var hasClick = false;
Page({
tap: function() {
if (hasClick) {
return
}
hasClick = true
wx.showLoading()
wx.request({
url: 'https://test.com/getinfo',
method: 'POST',
header: {
'content-type': 'application/json'
},
data: {},
success: function(res) {
if (res.statusCode === 200) {
console.log(res.data) // 服務器回包內容
}
},
fail: function(res) {
wx.showToast({
title: '系統錯誤'
})
},
complete: function(res) {
wx.hideLoading()
hasClick = false
}
})
}
})
為了防止用戶極快速度觸發兩次tap
回調,我們還加了一個hasClick
的“鎖”,在開始請求前檢查是否已經發起過請求,如果沒有才發起這次請求,等到請求返回之后再把鎖的狀態恢復回去。
排查異常的方法
- 檢查手機網絡狀態以及wifi連接點是否工作正常。
- 檢查小程序是否為開發版或者體驗版,因為開發版和體驗版的小程序不會校驗域名。
- 檢查對應請求的HTTPS證書是否有效,同時TLS的版本必須支持1.2及以上版本,可以在開發者工具的console面板輸入showRequestInfo()查看相關信息。
- 域名不要使用IP地址或者localhost,并且不能帶端口號,同時域名需要經過ICP備案。
- 檢查
app.json
配置的超時時間配置是否太短,超時時間太短會導致還沒收到回報就觸發fail
回調。 - 檢查發出去的請求是否302到其他域名的接口,這種302的情況會被視為請求別的域名接口導致無法發起請求。
微信登陸
通過wx.login
在success
中拿到登陸憑證,然后通過wx.request
把code傳到開發者服務器。如果當前微信用戶還沒有綁定當前小程序業務的用戶身份,那在這次請求應該順便把用戶輸入的帳號密碼一起傳到后臺,然后開發者服務器就可以校驗賬號密碼之后再和微信用戶id進行綁定,小程序端的示例代碼如下所示。
Page({
tapLogin: function() {
wx.login({
success: function(res) {
if (res.code) {
wx.request({
url: 'https://test.com/login',
data: {
username: 'zhangsan', // 用戶輸入的賬號
password: 'pwd123456', // 用戶輸入的密碼
code: res.code
},
success: function(res) {
// 登錄成功
if (res.statusCode === 200) {
console.log(res.data.sessionId) // 服務器回包內容
}
}
})
} else {
console.log('獲取用戶登錄態失敗!' + res.errMsg)
}
}
});
}
})
res.code
就是登陸憑證
開發者的后臺就拿到了前邊wx.login()
所生成的微信登錄憑證code,此時就可以拿這個code到微信服務器換取微信用戶身份。微信服務器為了確保拿code過來換取身份信息的人就是剛剛對應的小程序開發者,到微信服務器的請求要同時帶上AppId和AppSecret,
開發者服務器和微信服務器通信也是通過HTTPS協議,微信服務器提供的接口地址是:https://api.weixin.qq.com/sns/jscode2session?appid=<AppId>&secret=<AppSecret>&js_code=<code>&grant_type=authorization_code
openid
:微信用戶的唯一標識session_key
:會話**unionid
:用戶在微信開放平臺的唯一標識符。本字段在滿足一定條件的情況下才返回。
開發者服務器和開發者的小程序應該也有會話**SessionId。用戶登錄成功之后,開發者服務器需要生成會話**SessionId,在服務端保持SessionId對應的用戶身份信息,同時把SessionId返回給小程序。小程序后續發起的請求中攜帶上SessionId,開發者服務器就可以通過服務器端的Session信息查詢到當前登錄用戶的身份,這樣我們就不需要每次都重新獲取code,省去了很多通信消耗。
本地數據
小程序提供了讀寫本地數據緩存的接口,通過wx.getStorage
/wx.getStorageSync
讀取本地緩存,通過wx.setStorage
/wx.setStorageSync
寫數據到緩存,其中Sync
后綴的接口表示是同步接口,執行完畢之后會立馬返回
讀取:
wx.getStorage({
key: 'key1',
success: function(res) {
// 異步接口在success回調才能拿到返回值
var value1 = res.data
},
fail: function() {
console.log('讀取key1發生錯誤')
}
})
try {
// 同步接口立即返回值
var value2 = wx.getStorageSync('key2')
} catch (e) {
console.log('讀取key2發生錯誤')
}
存儲:
// 異步接口在success/fail回調才知道寫入成功與否
wx.setStorage({
key: "key",
data: "value1"
success: function() {
console.log('寫入value1成功')
},
fail: function() {
console.log('寫入value1發生錯誤')
}
})
try {
// 同步接口立即寫入
wx.setStorageSync('key', 'value2')
console.log('寫入value2成功')
} catch (e) {
console.log('寫入value2發生錯誤')
}
每個小程序的緩存空間上限為10MB,如果當前緩存已經達到10MB,再通過wx.setStorage
寫入緩存會觸發fail
回調。
由于本地緩存是存放在當前設備,用戶換設備之后無法從另一個設備讀取到當前設備數據,因此用戶的關鍵信息不建議只存在本地緩存,應該把數據放到服務器端進行持久化存儲。
利用本地緩存提前渲染截面
討論一個需求:我們要實現了一個購物商城的小程序,首頁是展示一堆商品的列表。一般的實現方法就是在頁面onLoad
回調之后通過wx.request
向服務器發起一個請求去拉取首頁的商品列表數據,等待wx.request
的success
回調之后把數據通過setData
渲染到界面上,如下代碼所示。
Page({
onLoad: function() {
var that = this
wx.request({
url: 'https://test.com/getproductlist',
success: function(res) {
if (res.statusCode === 200) {
that.setData({
list: res.data.list
})
}
}
})
}
})
設想一下當用戶退出小程序再進來,界面仍然會有白屏現象,因為我們需要等待拉取商品列表的請求回來才能渲染商品列表。當然我們還可以再做一些體驗上的優化,例如在發請求前,可能我們會在界面上顯示一個Loading提示用戶在加載中,但是并沒有解決這個延遲渲染的現象,這個時候我們可以利用本地緩存來提前渲染界面。
我們在拉取商品列表后把列表存在本地緩存里,在onLoad發
起請求前,先檢查是否有緩存過列表,如果有的話直接渲染界面,然后等到wx.request
的success
回調之后再覆蓋本地緩存重新渲染新的列表,如下代碼所示。
Page({
onLoad: function() {
var that = this
var list = wx.getStorageSync("list")
if (list) { // 本地如果有緩存列表,提前渲染
that.setData({
list: list
})
}
wx.request({
url: 'https://test.com/getproductlist',
success: function(res) {
if (res.statusCode === 200) {
list = res.data.list
that.setData({ // 再次渲染列表
list: list
})
wx.setStorageSync("list", list) // 覆蓋緩存數據
}
}
})
}
})
這種做法可以讓用戶體驗你的小程序時感覺加載非常快,但是你還要留意這個做法的缺點,如果小程序對渲染的數據實時性要求非常高的話,用戶看到一個舊數據的界面會非常困惑。因此一般在對數據實時性/一致性要求不高的頁面采用這個方法來做提前渲染,用以優化小程序體驗。
緩存SessionId
通常用戶在沒有主動退出登錄前,用戶的登錄態會一直保持一段時間[10],就無需用戶頻繁地輸入賬號密碼。如果我們把SessionId記錄在Javascript中某個內存變量,當用戶關閉小程序再進來小程序時,之前內存的SessionId已經丟失,此時我們就需要利用本地緩存的能力來持久化存儲SessionId。
利用本地緩存持久存儲用戶登錄態SessionId:
//page.js
varapp = getApp()
Page({
onLoad: function() {
// 調用wx.login獲取微信登錄憑證
wx.login({
success: function(res) {
// 拿到微信登錄憑證之后去自己服務器換取自己的登錄憑證
wx.request({
url: 'https://test.com/login',
data: {
code: res.code
},
success: function(res) {
var data = res.data
// 把 SessionId 和過期時間放在內存中的全局對象和本地緩存里邊
app.globalData.sessionId = data.sessionId
wx.setStorageSync('SESSIONID', data.sessionId)
// 假設登錄態保持1天
var expiredTime = +new Date() + 12460601000
app.globalData.expiredTime = expiredTime
wx.setStorageSync('EXPIREDTIME', expiredTime)
}
})
}
})
}
})
在重新打開小程序的時候,我們把上一次存儲的SessionId內容取出來,恢復到內存。
//app.js
App({
onLaunch: function(options) {
var sessionId = wx.getStorageSync('SESSIONID')
var expiredTime = wx.getStorageSync('EXPIREDTIME')
var now = +new Date()
if (now - expiredTime <= 12460601000) {
this.globalData.sessionId = sessionId
this.globalData.expiredTime = expiredTime
}
},
globalData: {
sessionId: null,
expiredTime: 0
}
})
設備能力
wx.scanCode
:獲取二維碼的數據wx.getNetworkType
:獲取網絡狀態(networkType
字段的有效值:wifi
/2g
/3g
/4g
/unknown
(Android下不常見的網絡類型)/none
(無網絡)wx.onNetworkStatusChange
:可以動態監聽網絡狀態變化的接口
小程序的協同工作和發布
暫時略略略
底層框架
暫時略略略
性能優化
暫時略略略
開發者工具
暫時略略略
參考
智能推薦
小程序 - 簡單入門
開發小程序的第一步,你需要擁有一個小程序帳號,通過這個帳號你就可以管理你的小程序。 點擊 https://mp.weixin.qq.com/wxopen/waregister?action=step1 根據指引填寫信息和提交相應的資料,就可以擁有自己的小程序帳號。 登錄 https://mp.weixin.qq.com ,我們可以在菜單 “設置”-“開發設置&r...
SpringBoot 入門小程序
SpringBoot 入門小程序CSDN下載 SpringBoot 入門小程序GitHub下載 老規矩,先看項目結構。 有必要參考前面的博客。maven構建web工程 我們構建好maven工程之后,用pom導入jar包。 pom.xml 然后我們需要一個 controller @RestController告訴Spring以字符串的形式渲染結果,并直接返回給調用者。 @EnableAutoConf...
netty入門小程序
Netty的特性 設計統一的API,適用于不同的協議(阻塞和非阻塞)基于靈活、可擴展的事件驅動模型高度可定制的線程模型可靠的無連接數據Socket支持(UDP) 性能更好的吞吐量,低延遲更省資源盡量減少不必要的內存拷貝 安全完整的SSL/TLS和STARTTLS的支持能在Applet與Android的限制環境運行良好 健壯性不再因過快、過慢或超負載連接導致OutOfMemoryError不再有在高...
Mybatis入門小程序
Mybatis的配置過程 以下內容是maven工程下的mybatis小程序測試 1.導入jar文件 可以去maven官網搜mybatis,找到依賴并導入pom.xml 2.封裝工具類,獲取sqlsession 這里我們將獲取sqlsession這部分代碼封裝成一個工具類,以后需要的時候就可以直接調用工具類的getSqlSession()方法,提高代碼復用性。 3.配置mybatis核心配置文件 這...
猜你喜歡
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...