0、雜記
0.1、在實(shí)際的開發(fā)中,圖片資源不會(huì)存儲(chǔ)在小程序的目錄中,因?yàn)樾〕绦虻拇笮〔荒艹^1MB(現(xiàn)在改為2M)。超過則無法真機(jī)運(yùn)行和發(fā)布項(xiàng)目。我們應(yīng)該將圖片都存放在服務(wù)器上,讓小程序通過網(wǎng)絡(luò)來加載圖片資源。
0.2、在wxss中,本地資源是無法使用的,比如:background-image,如果使用本地的圖片是無法顯示的,可以使用網(wǎng)絡(luò)圖片來代替本地圖片,同時(shí)要加上background-size屬性,屬性值的單位為rpx。
0.3、小程序自適應(yīng)尺寸單位rpx,在微信小程序中,尺寸單位可以使用px,也可以使用rpx,使用rpx可以使組件自適應(yīng)屏幕的高度和寬度,但是使用px則不會(huì)。(官網(wǎng):規(guī)定屏幕寬為750rpx。如在 iPhone6 上,屏幕寬度為375px,共有750個(gè)物理像素,則750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素)。在開發(fā)過程中,到底使用rpx還是px?主要取決于業(yè)務(wù)需求,也就是需要元素隨著移動(dòng)設(shè)備尺寸的變化而變化,還是讓元素始終保持不變,所以需要具體問題,具體分析。比如:一個(gè)元素的width、heigth、margin、 font-size,在很多的時(shí)候,需要隨著設(shè)備的尺寸不同,而動(dòng)態(tài)發(fā)生變化,從而保持頁面元素之間的布局可以保持在一定的比例關(guān)系,在這種情況下就應(yīng)該使用rpx,所以rpx作為小程序的自適應(yīng)單位,它非常適合來控制圖片的寬和高以及元素之間的間距。但是border相關(guān)屬性不需要隨著移動(dòng)設(shè)備的尺寸變化而動(dòng)態(tài)發(fā)生變化,因?yàn)?,如果border動(dòng)態(tài)變化,那么會(huì)在屏幕尺寸較大的手機(jī)上會(huì)變的很粗,這個(gè)并不是我們想要的結(jié)果,所以應(yīng)當(dāng)將border相關(guān)屬性的單位設(shè)置為px。
0.4、關(guān)于分辨率和物理像素:在微信小程序的模擬器中,都給出了每種機(jī)型的分辨率,需要注意的是,這里的分辨率指的是邏輯分辨率pt,而非物理分辨率,以iphone6為例,模擬器的邏輯分辨率是375px×667px,設(shè)備像素比(Dpr:Device Pixel Ratio)為2,而iphone6的物理分辨率是1334px×750px,以上的意思是:iphone6的水平方向上有375個(gè)像素點(diǎn),豎直方向上有667個(gè)像素點(diǎn),而每個(gè)邏輯像素點(diǎn)包含2個(gè)物理像素點(diǎn)。所以開發(fā)人員一定要注意邏輯像素和物理像素的區(qū)別,1物理像素不等于1px,通常UI設(shè)計(jì)人員做出來的設(shè)計(jì)圖的像素是物理像素。假設(shè)有一張圖片的寬度是750像素(物理像素),我們想讓這張圖片充滿整個(gè)頁面,如果直接設(shè)置在頁面里面將圖片的寬度設(shè)置為750px,是不對的,正確的設(shè)置方法是為750rpx或者350px。
1、給container添加背景顏色顯示問題

2、image組件9種圖片裁剪與4種縮放的模式
如果一個(gè)元素的寬和高分別設(shè)置成340rpx和100%(在iphone6下就是750rpx),而雪糕圖片的素材原始高度分別為600px和750px。
在現(xiàn)實(shí)的項(xiàng)目中,我們經(jīng)常需要面對原始圖片的尺寸和設(shè)計(jì)圖里的尺寸不一樣的情況(尤其是原始圖片高度是未知和不固定的情況,比如動(dòng)態(tài)從網(wǎng)絡(luò)獲取圖片)。在這種情況下,我們必須要有所舍棄,或放棄等比例,或者裁剪掉圖片的一部分。接受不完美,這也是編程中很重要的心態(tài),如和選擇,需要看業(yè)務(wù)上的需求。
小程序的image組件提供了4種縮放模式和9種裁剪模式,來支持我們的選擇。
小程序頁面設(shè)計(jì)的框架結(jié)構(gòu)MINA(MVVM)
MINA(MINA IS NOT APP)就是微信小程序開發(fā)使用的框架
整個(gè)系統(tǒng)分為兩塊:視圖層(View) 和 邏輯層(App Service)。
MINA可以讓數(shù)據(jù)與視圖保持同步非常簡單。當(dāng)做數(shù)據(jù)修改的時(shí)候,只需要在邏輯層修改數(shù)據(jù),視圖層就會(huì)做相應(yīng)的更新。(通過this.setData進(jìn)行同步數(shù)據(jù))
3、page頁面的生命周期
什么是頁面的生命周期?如同人的成長需要分為出生、童年、青年、中年、老年一樣,一個(gè)頁面從創(chuàng)建到卸載,同樣會(huì)經(jīng)歷以下5個(gè)周期:
- 加載
- 顯示
- 渲染
- 隱藏
- 卸載
MINA框架分別提供了5個(gè)生命周期函數(shù)來監(jiān)聽這個(gè)5個(gè)特定的生命周期,以方便開發(fā)人員可以在這些特定的時(shí)刻執(zhí)行一些自己的代碼邏輯,它們分別是:
- onLoad 監(jiān)聽頁面加載,一個(gè)頁面只能會(huì)調(diào)用一次。
- onShow 監(jiān)聽頁面顯示,每次打開頁面都會(huì)被調(diào)用
- onReady 監(jiān)聽頁面初次渲染完成,一個(gè)頁面只會(huì)調(diào)用一次,代表頁面已經(jīng)準(zhǔn)備妥當(dāng),可以和視圖層進(jìn)行交互
- onHide 監(jiān)聽頁面隱藏
- onUnload 監(jiān)聽頁面卸載
可以在Page()方法中去演示這些頁面的生命周期
Page({ data:{}, onLoad:function(options){ console.log('onLoad:頁面被加載') }, onShow: function() { console.log('onShow函數(shù)被加載') }, onReady: function() { console.log('onReady函數(shù)被加載') }, onHide: function() { console.log('onHide函數(shù)被加載') }, onUnload: function() { console.log('onUnload函數(shù)被加載') } })onHide和onUnload這兩個(gè)函數(shù)需要執(zhí)行一些API操作,比如頁面執(zhí)行tab欄切換頁面、navigateTo方法、或者使用小程序切換后臺(tái)的按鈕時(shí)會(huì)執(zhí)行onHide函數(shù);當(dāng)頁面執(zhí)行redirectTo或navigateBack的時(shí)候會(huì)執(zhí)行onUnload函數(shù)。
當(dāng)然我們還可以添加任意的數(shù)據(jù)和函數(shù)到這個(gè)Page方法的Object參數(shù)中,在頁面的函數(shù)中用this就可以訪問這些自定義的數(shù)據(jù)或者函數(shù)
以下內(nèi)容你不需要立馬完全弄明白,不過以后它會(huì)有幫助。
生命周期

通過以上圖解可知
- onLoad 、onShow和onReady是按照先后順序,依次執(zhí)行
- onLoad、onReady在整個(gè)頁面的生命周期中只會(huì)執(zhí)行一次(除非這個(gè)頁面被執(zhí)行onUnload卸載掉了)
- onHide和onShow在一次生命周期內(nèi)可能會(huì)執(zhí)行多次。
- 除了頁面的First Render第一次渲染,頁面還有可能會(huì)Rerender 再次渲染多次,數(shù)據(jù)更新會(huì)造成頁面的重新渲染,這里要注意的是,小程序僅在第一次 First Render完成后,提供了監(jiān)聽函數(shù)onReady,對于以后的Rerednder并沒有提供相應(yīng)的監(jiān)聽函數(shù)。所以,onReady僅用來監(jiān)聽“第一次渲染”完成。
4、數(shù)據(jù)綁定
小程序借鑒了比如像Angular、vue這些流行框架的思想,采用數(shù)據(jù)綁定的機(jī)制來做數(shù)據(jù)的初始化和更新,不同于像Angular和Vue的雙向數(shù)據(jù)綁定,小程序僅實(shí)現(xiàn)了單向數(shù)據(jù)綁定,即支持從邏輯層傳遞到視圖層的數(shù)據(jù)綁定,反之則不行。
小程序使用Page方法參數(shù)里的data變量作為數(shù)據(jù)綁定的橋梁,寫在data里的數(shù)據(jù),被稱為數(shù)據(jù)綁定的初始化數(shù)據(jù)。
數(shù)據(jù)的綁定有以下兩種:
- 一種是初始化數(shù)據(jù)的數(shù)據(jù)綁定,通常將這些數(shù)據(jù)直接寫在Page方法參數(shù)的data對象下面。
- 另外一種是使用setData方法來做數(shù)據(jù)綁定,這中方式也可以理解為數(shù)據(jù)更新。這樣的數(shù)據(jù)更新將引起頁面的重新渲染(Rerender)
說明:小程序的腳本是運(yùn)行在JSCore中,JSCore是一個(gè)沒有DOM的環(huán)境,所以小程序只能使用數(shù)據(jù)綁定來做數(shù)據(jù)的相關(guān)操作。
4.1、初始化數(shù)據(jù)綁定
小程序使用Mustache語法雙大括號{{}}在微信,wxml組件里進(jìn)行數(shù)據(jù)的綁定,通過Page實(shí)例的生命周期,解釋一下初始化數(shù)據(jù)綁定的過程
當(dāng)頁面執(zhí)行了onShow函數(shù)后,邏輯層會(huì)收到一個(gè)通知(Notify),隨后邏輯層會(huì)將data對象以json的形式發(fā)動(dòng)到view視圖層(Send Initial Data),視圖層接收到初始化數(shù)據(jù)后,開始第一次渲染,顯示初始化數(shù)據(jù)(First Render),最終將數(shù)據(jù)呈現(xiàn)在開發(fā)者的眼前。
說明:如果數(shù)據(jù)綁定是作用在組件的屬性中,比如
<image src="{{pic}} />",一定要在{{}}外邊加上雙引號,否則小程序會(huì)報(bào)錯(cuò),如果是內(nèi)容型的數(shù)據(jù),則不需要加雙引號,比如<text>{{data}}</text>
4.2、查看數(shù)據(jù)綁定對象
通過AppData面板來查看和調(diào)試數(shù)據(jù)綁定變量,AppData下的數(shù)據(jù)以頁面為組織單位,更改這里的某一項(xiàng)數(shù)據(jù)的值,都是實(shí)時(shí)進(jìn)行更新的。
4.3、數(shù)據(jù)綁定更新
可以通過setData函數(shù)來做數(shù)據(jù)綁定,這種方法可以理解為“數(shù)據(jù)更新”。setData方法位于Page對象的原型鏈上:Page.prototype.setData。大多數(shù)情況下,我們使用this.setData的方式來調(diào)用這個(gè)方法。
setData的參數(shù)接受一個(gè)對象,以key和value的形式將this.data中的key對應(yīng)的值設(shè)置成value。
上面的話要注意兩點(diǎn):
- setData 會(huì)改變this.data變量里相同key的值。
- setData執(zhí)行后會(huì)通知邏輯層執(zhí)行Rerenader,并立刻重新渲染視圖層。
this.setData所綁定或者更新的數(shù)據(jù),并不要求在this.data中已預(yù)先定義。
Page({
data:{
},
onLoad: function(){
var objData = {
obj: {
text:"hello"
},
name:"小明"
}
this.setData({
pageData: objData
})
}
})
<text> {{pageData.obj.text}} </text>
5、列表渲染 wx:for
<block></block>標(biāo)簽沒有實(shí)質(zhì)意義,它并不是組件,所以我們稱作“標(biāo)簽”,它僅僅是一個(gè)包裝,不會(huì)在頁面內(nèi)被渲染,在這里可以理解為一個(gè)JavaScript編程語言中的括號,在block標(biāo)簽中被包裹的元素將被重復(fù)渲染。
wx:for-item="item" 指定數(shù)組當(dāng)前子元素的變量名,我們將元素的變量名指定為item,當(dāng)然這個(gè)變量名可以更改。
如果不定義item數(shù)組子元素的變量名,依然是可以正常顯示的,原因是小程序默認(rèn)子元素的變量名就是item。
wx:for-index="idx" 指定當(dāng)前元素在數(shù)組中索引號的變量名,我們命名為idx。
wx:for 并不是一定要作用在block標(biāo)簽上的,也可以作用在view組件上,一樣可以照常運(yùn)行,但是并不推薦使用view等組件來做列表渲染,因?yàn)槲覀兿M麡?biāo)簽或者組件元素是語義明確的,view組件通常被用來當(dāng)做視圖容器或者是區(qū)域的分隔,它有它的使命,不應(yīng)該濫用。
6、事件
要從一個(gè)頁面跳轉(zhuǎn)到另外一個(gè)頁面,需要使用事件來響應(yīng)點(diǎn)擊某個(gè)動(dòng)作。
什么是事件?
事件定義:事件是視圖層(wxml)到邏輯層的通訊方式。簡單理解:事件可以讓我們在js里處理一些用戶在界面上的一些操作,并對這些操作做出反饋。比如:點(diǎn)擊一個(gè)按鈕,從一個(gè)頁面跳轉(zhuǎn)到另外一個(gè)頁面,在這個(gè)過程中,需要在js里調(diào)用MINA框架的API,使從一個(gè)頁面跳轉(zhuǎn)到另外一個(gè)頁面。
想要實(shí)現(xiàn)頁面跳轉(zhuǎn)的這個(gè)機(jī)制,需要做兩件事情
- 在組件上注冊事件,告訴小程序要監(jiān)聽哪個(gè)組件的什么事件?
- 在js中編寫事件處理函數(shù)響應(yīng)事件,也就是說,監(jiān)聽到事件后,需要編寫自己的業(yè)務(wù)。
6.1、冒泡事件和非冒泡事件
冒泡事件是指某個(gè)組件上的事件被觸發(fā)后,事件還會(huì)向父級元素傳遞,一直到頁面的頂級元素。
非冒泡事件則不會(huì)向父級元素傳遞事件。
bind和catch的區(qū)別
bind不會(huì)阻止事件的傳播,而catch會(huì)阻止事件繼續(xù)向父節(jié)點(diǎn)傳播。
7、導(dǎo)航
小程序提供了5個(gè)導(dǎo)航API,從而幫助開發(fā)者實(shí)現(xiàn)頁面的跳轉(zhuǎn)
-
wx.navigateTo保留當(dāng)前頁面,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁面,使用wx.navigateBack可以返回到原頁面 -
wx.redirectTo關(guān)閉當(dāng)前頁面,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁面 -
wx.switchTab只能用于跳轉(zhuǎn)到帶有 tabBar 頁面,并關(guān)閉其他所有非 tabBar 頁面 -
wx.navigateBack關(guān)閉當(dāng)前頁面,返回上一頁面或多級頁面??赏ㄟ^getCurrentPages()獲取當(dāng)前的頁面棧,決定需要返回幾層 -
wx.reLaunch關(guān)閉所有頁面,打開到應(yīng)用內(nèi)的某個(gè)頁面
wx.navigateTo和wx.redirectTo的區(qū)別
tapHandle: function (){
wx.navigateTo({
url: '../main/main?name=小明',
})
// wx.redirectTo({
// url: '../footer/footer',
// })
},
onUnload: function(){
console.log("page is unload")
},
onHide: function (event) {
console.log("page is hide")
}
使用 wx.redirectTo 實(shí)現(xiàn)跳轉(zhuǎn),將打印輸出 page is unload,但是不會(huì)輸出page is hide,跳轉(zhuǎn)到的頁面沒有返回按鈕,無法返回到之前的頁面了。
使用wx.navigateTo 實(shí)現(xiàn)跳轉(zhuǎn),將打印輸出 page is hide,但是不輸出 page is unload,頁面左上角出現(xiàn)一個(gè)可以返回到之前頁面的按鈕。
所以總結(jié)出wx.redirectTo是將關(guān)閉當(dāng)前頁面并將頁面卸載,無法返回到之前的頁面,而wx.navigateTo僅僅是隱藏當(dāng)前頁面,還可以再次返回到之前的被隱藏的頁面。
再來考慮一個(gè)問題,當(dāng)wx.navigateTo從一個(gè)頁面跳轉(zhuǎn)到另外一個(gè)頁面后(從A頁面跳轉(zhuǎn)到B頁面),再從B頁面返回到A頁面時(shí),B頁面會(huì)執(zhí)行onHide還是onUnload呢?答案是執(zhí)行B頁面的onUnload函數(shù),也就是說當(dāng)從子頁面返回到父頁面時(shí),子頁面會(huì)被卸載。因?yàn)檫@樣設(shè)計(jì)就不會(huì)造成大量的子頁面殘留在小程序中了。(早期的小程序是不會(huì)卸載的,后來在版本更新的時(shí)候更改的。)
7.1、小程序最多只能有5層頁面
當(dāng)我們使用wx.navigateTo 從父頁面跳轉(zhuǎn)到子頁面后,就形成了2個(gè)頁面層級,可以繼續(xù)在子頁面里使用wx.navigateTo 跳轉(zhuǎn)到子頁面。
但是小程序強(qiáng)制規(guī)定,只允許有最多5層父子頁面,事實(shí)上,太多的頁面將嚴(yán)重影響用戶的產(chǎn)品體驗(yàn),建議頁面最多不要超過3層。
wx.redirectTo不存在這個(gè)問題,因?yàn)楫?dāng)跳轉(zhuǎn)到另外一個(gè)頁面上后,上一個(gè)頁面被強(qiáng)制卸載掉了。
8、小程序的模塊化
如果所有的數(shù)據(jù)都寫在js文件中,這樣會(huì)污染了我們的業(yè)務(wù)層,我們應(yīng)該把這些數(shù)據(jù)分離到一個(gè)單獨(dú)的js文件中,
在項(xiàng)目的根目錄下新建一個(gè)文件夾,命名為data,然后再data目錄下新建一個(gè)js文件,命名為data.js。
// data.js
var postList = [
{
date:'1111',
title:'hahha'
}
]
然后將之前js文件中data里面的數(shù)據(jù)剪切到data.js中。所以我們提取出來的數(shù)據(jù)文件data.js可以看做是小程序的一個(gè)模塊。同時(shí)我們需要在data.js中使用module.exports向外部暴露一個(gè)接口
// data.js
var postList = [
{
data:'1111',
title:'hahha'
}
]
module.exports = {
postList:postList
}
然后在其他js文件中引用這個(gè)data.js模塊,
// 其它js文件
var dataObj = require('../data/data.js')
Page({
data:{
},
onLoad: funciton (){
this.setData({
postList:dataObj.postList
})
}
})
使用require 引用js模塊時(shí),要注意以下幾點(diǎn):
- 被引用的文件一定要帶有拓展名.js,這一點(diǎn)是不同于頁面路徑的。
- path路徑不可以使用絕對路徑,否則會(huì)報(bào)錯(cuò),應(yīng)該使用相對路徑。
- 在JavaScript文件中聲明的變量和函數(shù)只在該文件中有效,不同的文件可以聲明相同名字的變量和函數(shù),不會(huì)相互影響。
9、小程序的模板化
我們通??梢詫⒁恍┕驳摹⒔?jīng)常使用的業(yè)務(wù)邏輯提取成一個(gè)公共的函數(shù),當(dāng)在多個(gè)地方需要使用函數(shù)時(shí),只需要要調(diào)用這個(gè)函數(shù)即可。
事實(shí)上,有一句話是這么描述軟件開發(fā)的:編程世界里遇到的絕大多數(shù)問題都可以用封裝的思想來解決。
// post-item-tpl.wxml
<template name="postItemTpl">
<view>
{{item.date}}
</view>
</template>
模板相關(guān)的內(nèi)容必須包裹在<template></template>標(biāo)簽中,使用name屬性來指定模板的名稱
// post.wxml
// 使用import來引用模板
<import src="./post-item-tpl.wxml">
<block wx:for="{{postList}}" wx:for-item="item" wx:for-idx="idx">
<template is="postItemTpl" data="{{item}}">
</block>
template的is屬性指定要使用哪個(gè)模板,template的data屬性,可以向template傳遞數(shù)據(jù),這里是將wx:for得到的item傳入到template里面,這樣就可以在template內(nèi)部使用這個(gè)item了。
模板的好處是它可以讓多個(gè)調(diào)用方來調(diào)用,不可能要求每個(gè)調(diào)用方都使用同樣的變量名來調(diào)用模板,這種由定義方要求調(diào)用方遵守變量名命名的做法是不合理的。
要解決這個(gè)問題,就必須消除template對于外部變量名的依賴,可以使用拓展運(yùn)算符"…" 展開傳入對象變量來消除這個(gè)問題。
<template name="postItemTpl" data="{{...item}}">
</template>
然后在 post-item-tpl.wxml 中就可以去掉{{}}中的item
// post-item-tpl.wxml
<template name="postItemTpl">
<view>
{{date}}
</view>
</template>
9.1 include與import引用模板的區(qū)別
- import 需要先引入template,然后再使用template,但是include不需要預(yù)先引入template,直接在需要的地方引入模板即可。
- include 模式非常簡單,就是簡單的代碼替換,不存在作用域,也不能像import一樣使用data傳遞變量。
// post.wxml
<block wx:for="{{postList}}" wx:for-item="item" wx:for-idx="idx">
// 使用include來引用模板
<include src="./post-item-tpl.wxml"/>
</block>
9.2 css模塊化
使用@import "post-item-tpl.wxss"引入樣式文件
10、不要在template上注冊事件
template標(biāo)簽僅僅只是一個(gè)占位符,在編譯后,會(huì)被template的模板內(nèi)容替換,所以在template標(biāo)簽上注冊事件是無效的。同理,也不能在block上注冊事件,因?yàn)閎lock也會(huì)在編譯后消失。
解決的辦法就是在template標(biāo)簽的外部增加一個(gè)view組件,將template包裹起來,并將事件注冊在view組件上。
11、頁面間的傳遞參數(shù)
從文章列表頁面跳轉(zhuǎn)到文章詳情頁頁面,需要正確展示被點(diǎn)擊所對應(yīng)的文章內(nèi)容,首先就需要將文章的id號由index頁面?zhèn)鬟f到index-detail頁面,這樣index-detai頁面才能知道,到底要顯示哪篇文章。
所以在這里就涉及到頁面之前的參數(shù)傳遞與通信了,通過使用頁面導(dǎo)航url的query參數(shù)傳遞,就能夠?qū)崿F(xiàn)頁面之間的參數(shù)傳遞。
12、組件自定義屬性名的規(guī)則
- 必須以data-開頭。
- 多個(gè)單詞由連接符 - 鏈接。
- 單詞中最好不要有大寫字母,如果有大寫字母,除第一字母外,其余大寫字母都轉(zhuǎn)化成小寫。
- 在js中獲取自定義屬性時(shí),多個(gè)單詞將被轉(zhuǎn)化為駝峰命名。
13、文章id號流向圖

14、動(dòng)態(tài)設(shè)置導(dǎo)航欄標(biāo)題
前提:在某些情況下,我們希望導(dǎo)航欄的文字可以根據(jù)頁面內(nèi)容的不同而有所變化,比如在文章詳情頁中,我們希望導(dǎo)航欄可以實(shí)時(shí)顯示當(dāng)前文章的標(biāo)題,不同文章顯示不同的標(biāo)題。
小程序提供了wx.setNvigationBarTitle()來動(dòng)態(tài)設(shè)置導(dǎo)航欄標(biāo)題,這個(gè)方法無論是在onLoad或者onShow函數(shù)中調(diào)用,都可以成功的設(shè)置導(dǎo)航欄標(biāo)題,但是建議在onReady函數(shù)中設(shè)置此方法,因?yàn)閛nReady在onShow發(fā)生之后才觸發(fā),onShow將標(biāo)題設(shè)置完畢后,onReady會(huì)重新渲染頁面,并覆蓋導(dǎo)航欄的標(biāo)題。
15、實(shí)現(xiàn)圖片預(yù)覽功能
wx.previewImage只能預(yù)覽位于網(wǎng)絡(luò)中的圖片,無法預(yù)覽本地的圖片。
16、wx:if和hidden控制元素顯示和隱藏
<view hidden="{{falg}}"</view>
<view hidden="{{!falg}}"</view>
<view wx:if="{{falg}}"></view>
wx:if有較高的切換消耗,而hidden有更高的初始消耗(使用hidden話,組件始終會(huì)被渲染,只是簡單的控制顯示和隱藏),因此,在需要頻繁切換的情景的話,使用hidden更好,更合適,在運(yùn)行條件不大可能改變的時(shí)候,使用wx:if比較好。
17、實(shí)現(xiàn)回車發(fā)送評論消息的功能
- bindinput
- bindfocus
- bindblur
- bindconfirm
以上事件都屬于非冒泡事件
- bindinput具有以下特定
- 當(dāng)用戶輸入字符時(shí)觸發(fā)
- 每當(dāng)用戶輸入或者刪除一個(gè)字符時(shí),bindinput事件就會(huì)觸發(fā)一次
- 可以在事件響應(yīng)函數(shù)中使用return 返回一個(gè)字符或者字符串,該字符串將替換input輸入框中的顯示文本
- 非常適合做“即時(shí)搜索”功能
- bindfocus 當(dāng)input組件獲取焦點(diǎn)時(shí)觸發(fā)
- bindblur 當(dāng)input組件失去焦點(diǎn)時(shí)觸發(fā)
- bindconfirm 響應(yīng)真機(jī)上點(diǎn)擊鍵盤“完成”按鈕事件。或者鍵盤上的回車事件,
input輸入值都是在事件對應(yīng)的響應(yīng)函數(shù)中使用event.detail.value
20、解決真機(jī)運(yùn)行時(shí)頁面卡頓問題
給頁面的容器加上-webkit-overflow-scrolling:touch;css屬性
21、雜項(xiàng)知識(shí)
1、小程序提供了一個(gè)全局方法,getAppp(),用于獲取小程序的APP對象,我們可以通過app.globalData來訪問全局變量
2、分享按鈕是頁面行為,而不是應(yīng)用程序的行為,每個(gè)頁面都可以調(diào)用分享API,并設(shè)置分享的參數(shù)。onShareAPPMessage,這個(gè)方法是一個(gè)頁面方法,不是wx.onShareAPPMessage。
3、微信小程序動(dòng)畫
animation.scale(2,2).rotate(45).step().translate.step()
以上是兩個(gè)動(dòng)畫組,它們之間使用step()作為分隔,每個(gè)動(dòng)畫組中的動(dòng)畫方法執(zhí)行順序是:同一組中的動(dòng)畫方法會(huì)同時(shí)執(zhí)行,但動(dòng)畫組必須是先后執(zhí)行。也就是說一組動(dòng)畫先執(zhí)行完成后,后面的動(dòng)畫組中的動(dòng)畫才能執(zhí)行。
可不可以在不同的動(dòng)畫組中設(shè)置不同的動(dòng)畫效果參數(shù)?答案是可以的。每個(gè)step方法都可以接受一個(gè)obiect對象:可以傳入一個(gè)跟wx.createAnimation()一樣的配管.參數(shù),用于指定當(dāng)前組動(dòng)畫的配置。
step接受動(dòng)畫配置
//創(chuàng)建一個(gè)動(dòng)畫實(shí)例,
var animation = wx.createAnimation(!timingFunction: 'ease-in-out
// 設(shè)置動(dòng)畫組(以step()分隔),每個(gè)隊(duì)列,
animation.scale(2, 2).rotate(45).step().translate(30).step(( duration: 1000 )
小程序提供了6類動(dòng)畫方法:
- 樣式opacity, backgroundColor, width. height. top. left. bottom. right.
- 旋轉(zhuǎn)rotate, rotateX. rotateY, rotatez. rotate3d,
- 縮放scale. scaleX. scaleY, scalez. scale3d.
- 偏移 translate, translateX. translateY. translateZ. translate3d.
- 傾斜 skew, skewX. skewY.
- 矩陣變形matrix, matrix3d.
4、電影

5、實(shí)現(xiàn)頁面下拉刷新“ 三部曲”
實(shí)現(xiàn)一個(gè)頁面的刷新需要三步
- 在當(dāng)前頁面json文件中配置enablePullDownRefresh選項(xiàng)為true,打開開關(guān)
- 在當(dāng)前頁面的js中編寫onPullDownRefresh函數(shù),在函數(shù)中完成下拉刷新邏輯
- 編寫完下拉刷新邏輯代碼后,主動(dòng)調(diào)用wx.stopPullDownRefresh函數(shù)停止當(dāng)前頁面的下拉刷新
6、scroll-view組件的注意要點(diǎn)
- 如果scroll-view下排列的多個(gè)子元素是塊級元素(比如view) ,就直接對sctoll-view設(shè)置display:fex和flex-direction:row,不會(huì)使子素首動(dòng)成為水平列,如果不使用y而將容器元素?fù)Q成view,那么設(shè)置display:flex和flex-directionrow是可以使子元素自動(dòng)成水平排列的.
- 如果想讓scroll-view下的view元素水平排列,一種可行的方法是將子元素view設(shè)置為有inline-block或者inline-flex。
- 子元素有可能出現(xiàn)換行的情況,需要在容器上設(shè)置white-space:nowrap;
- 請勿在scroll-view中使用textarea、map、Canvas、video組件
- scoll-into-view的優(yōu)先級高于scroll-top。
- 在滾動(dòng)scroll-view時(shí)會(huì)阻止頁面回彈,所以在scroll-view中滾動(dòng)是無法觸發(fā)onPullDownRefresh的。
- 若要使用下拉刷新,請使用頁面的滾動(dòng)(wx.pageScrollTo),而不是scroll-view,這樣也能通過點(diǎn)擊頂部狀態(tài)欄回到頁面頂部。
22、獲取用戶基本信息
小程序提供一個(gè)wx.getUserInfo()方法來獲取用戶信息,用戶信息分為用戶基本信息和用戶 openId UnionId?;拘畔⑹敲魑牡?,而openId和unionid是加密數(shù)據(jù)。這兩種類型的數(shù)據(jù)都由wx.getUserInfo()方法返回。
在小程序匯中,用戶的基本信息可以輕易獲得,他們是明文的、不加密的。但是openid和unionid是加密的,什么是openid和unionid?可以將openid和unionid理解為用戶在微信應(yīng)用中的id號,他們的區(qū)別是:openid只代表用戶的在某個(gè)微信應(yīng)用下的id號,而unionid是跨應(yīng)用的。同一個(gè)用戶在同一個(gè)開發(fā)者的多個(gè)應(yīng)用里面,unionod是唯一的。
如果開發(fā)者擁有多個(gè)移動(dòng)應(yīng)用、網(wǎng)站應(yīng)用和公眾賬號(包括小程序),可以通過unionid區(qū)分用戶的唯一性,因?yàn)橹灰峭粋€(gè)微信開放平臺(tái)賬號下的移動(dòng)應(yīng)用、網(wǎng)站應(yīng)用和公眾賬號(包括微信小程序),用戶的unionid就是唯一的。也就是同一個(gè)用戶在同一個(gè)微信開放平臺(tái)下的不同應(yīng)用中,unionid是相同的。
所以openid不能跨應(yīng)用,如果要在多應(yīng)用間統(tǒng)一用戶身份,請使用unionid。這里要注意的是,在微信小程序中使用unionid首先需要前往微信開放平臺(tái)綁定小程序。
如果要開發(fā)真實(shí)的項(xiàng)目,一定要考慮各種調(diào)用失敗的情況,并加強(qiáng)安全性。
23、用戶登錄
微信小程序登錄是為了讓開發(fā)者的服務(wù)器獲取用戶的openid以及session_key的令牌。微信小程序提供了wx .login()方法用于用戶登錄。wx.login的方法的主要目的是拿到用戶的openid和用戶本次登錄的session_key。
openid是用戶對于當(dāng)前小程序的身份標(biāo)識(shí)。
session_key是本次用戶登錄的會(huì)話密鑰,通常用來對用戶的通信數(shù)據(jù)進(jìn)行加密。通常用來對用戶的通信數(shù)據(jù)進(jìn)行加密。

要獲取session_key和openId,首先需要在小程序中調(diào)用wx.login,并獲取code;隨后將code發(fā)送到開發(fā)者服務(wù)器并同appid和appsecret一起發(fā)送到微信服務(wù)器中,微信服務(wù)器會(huì)返回我們需要的session_key和openId。
code是一把鑰匙,是得到openid和session_key的關(guān)鍵。code的有效期只有5分鐘,如果在5分鐘之內(nèi)還沒有用code換取openid和session_key,那么就不能在使用了。
code是一把鑰匙,是得到openid和session_key的關(guān)鍵,code有效期只有5分鐘,如果在5分鐘之內(nèi)還沒有用code換取openid和session_key,那么就不能再使用了。
session_key 肯定是有失效期的,在session_key有效期內(nèi),開發(fā)者最好不要重復(fù)調(diào)用wx.login接口,不斷用code換取session_key,而應(yīng)該將session_key保存在服務(wù)器中,等到session_key失效后,再重新獲取新的session_key。
怎么檢查session_key是否失效了呢?小程序提供了wx.checkSession();來校驗(yàn)session_key是否失效,只有在session_key確認(rèn)失效后,才能再次調(diào)用wx.login。
wx.login得到的code只能使用一次,一旦你使用code換取了openid和session_key,這個(gè)code就馬上失效,不能再次使用。當(dāng)然如果5分鐘內(nèi)這個(gè)code還沒有使用,那么也會(huì)失效。
在實(shí)際的項(xiàng)目中,將openid和session_key返回到客戶端是非常危險(xiǎn)的,也完全沒有必要。因?yàn)樾枰褂胦penid和session_key的場景都會(huì)被放到服務(wù)器進(jìn)行的,所以返回到小程序中沒有任何意義,反而會(huì)增加數(shù)據(jù)的泄露風(fēng)險(xiǎn)。
24、用戶信息校驗(yàn)
調(diào)用wx.getUserInfo接口拿到了用戶的明文基本信息數(shù)據(jù)和用戶加條新?lián)?并使用了明文數(shù)據(jù)。下面回顧一下wx.getUserInfo返回的數(shù)據(jù):
- userlnfo 用戶信息對象。
- awData 不包括敏感信息的原始數(shù)據(jù)字符串。
- signature 使用shal( rawData + sessionkey )得到字符串,用于校驗(yàn)用戶信息。
- encryptedData 包括敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù).
- iv加密算法的初始向量。
,我們使用了userlnfo對象,包括userInfo和rawData在內(nèi)的明文數(shù)據(jù)。都可能存在被篡改的風(fēng)險(xiǎn)。如何知道明文數(shù)據(jù)是否被篡改了呢?
這個(gè)時(shí)候rawData和singature就可以發(fā)揮作用了.rawData和signature用于校驗(yàn)用戶數(shù)據(jù)到底有被篡改過(沒有絕對安全的網(wǎng)絡(luò),據(jù)極有可能被抓包或者通過其他萬式 .通常來說,想要實(shí)現(xiàn)這個(gè)校驗(yàn)必在服務(wù)器編碼才能進(jìn)行。這需要小程序?qū)@取的rawData和signature一并提交到服務(wù)器,由服務(wù)器完成校驗(yàn)工作。
校驗(yàn)的基本原理是: rawData是用戶原始明文數(shù)據(jù), signature是使用shal (rawData +sessionkey)得到的字符串。理論上講,如果數(shù)據(jù)沒有被篡改,那么signature等于shal(rawData+ sessionkey);如果rawData或者signature被修改了,那么signature必然不再等于shal (rawData.+ sessionkey).
是否存在signature和rawData同時(shí)被修改的情況呢?理論上是不可能的,因?yàn)閟ession key并不在網(wǎng)絡(luò)上傳輸,篡改者不知道這個(gè)變量,被篡改且校驗(yàn)通過的概率很小。
有可能從signature中 session key論E,這是不可能的。因?yàn)閟hal算法是不可逆的,無法在已知rawDatasignature的情況下推算出session kev,不知道session key就無法通過同時(shí)修改rawData和signature達(dá)到“欺騙校驗(yàn)的目的”。如果知道了session key,只需要修改rawData并重新用session key計(jì)算一下新的shal (rawData+session kev)就又可以讓新的rawData等于新的shal (rawData+session key)了。這樣,開發(fā)者就無法知道rawData是被修改過的。
這也是為什么官方文檔一再強(qiáng)調(diào),不要在網(wǎng)絡(luò)上傳輸session kev,而應(yīng)該將其保存在服務(wù)器上使用,以降低session key被泄露的風(fēng)險(xiǎn)。
session key有點(diǎn)類似于我們在數(shù)據(jù)庫中保存用戶密碼時(shí)所使用的“鹽(salt) "。在數(shù)據(jù)庫保存用戶密碼時(shí),并不是直接將用戶的密碼以明文的方式存放在數(shù)據(jù)庫表中,通常都會(huì)使用SHA-1或者M(jìn)D5算將用戶密碼和salt隨機(jī)字符串拼接在一起,重新計(jì)算一下再存入數(shù)據(jù)庫中。被重新使用SHA-1或MD5算法計(jì)算的用戶密碼誰都不知道是什么,開發(fā)者也只能E對每次登錄時(shí)輸入的密碼和數(shù)據(jù)庫保存的密碼是否一致,判斷是否為合法用戶,卻無法知道密碼到底是什么。

上圖是在服務(wù)器中沒有保存session_key的,因?yàn)槲覀冃枰玫絪ession_key才能進(jìn)行用戶數(shù)據(jù)校驗(yàn),所以上圖的流程圖再一次重復(fù)了用戶的登錄流程。
在實(shí)際的開發(fā)中,用戶登錄在session_key的有效時(shí)間內(nèi)只應(yīng)該執(zhí)行一次,session_key也應(yīng)該保存在服務(wù)器中,其實(shí)小程序只需要使用wx.request將raw.Data和signature發(fā)送到服務(wù)器即可,服務(wù)器無須使用code換取session_key。直接和SHA-1或者M(jìn)D5算法的簽名對比即可。如下圖所示

以上兩個(gè)圖對比可以得出,服務(wù)器保存session_key后整個(gè)流程變得更加簡單,完全不需要在于微信服務(wù)器進(jìn)行交互。
建議開發(fā)者在客戶端使用用戶明文數(shù)據(jù)時(shí)(通過wx.getUserInfo獲得),使用rawData,而不要使用userInfo。因?yàn)閿?shù)據(jù)驗(yàn)證的是rawData有沒有被篡改,而不是驗(yàn)證的是userInfo是否被篡改。所以建議開發(fā)者使用rawData作為用戶的基本信息。
25、解析用戶加密數(shù)據(jù)獲取openId及UnionId
調(diào)用wx.getUserInfor后將返回encryptedData和iv等數(shù)據(jù),encryptedData是包含敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù),iv是用于解密這兩個(gè)數(shù)據(jù),整個(gè)解密的過程和用戶信息校驗(yàn)的流程基本相同,不同的是,我們提交到服務(wù)器的數(shù)據(jù)是encryptedData和iv而不是rawData和signature。
26、微信小程序模板消息
要成功發(fā)送模板消息,必須要有一個(gè)formld的id號,但經(jīng)過測試發(fā)現(xiàn)開發(fā)工具中法獲取formld,也就是說在開發(fā)工具中不能產(chǎn)生模板逍息,只有在真機(jī)中方能拿到formld,formld是發(fā)送模板消息的關(guān)鍵。模板消息是被動(dòng)的,只有用戶本人在小程序中有一定交互行為后,服務(wù)器才能夠向用戶推送模板消息。下面來解釋一下用戶的什么行為,才能讓服務(wù)器可以向小程序推一條模板消息。
(1)用戶在小程序內(nèi)完成過支付行為, 7天內(nèi)可允許開發(fā)者向用戶推送有限制的模板消息(一次支付可下發(fā)一條,多次支付可發(fā)條數(shù)獨(dú)立,互相不影響) 。
-(2)當(dāng)用戶在小程序內(nèi)發(fā)生過提交表單行為且該表單聲明為要發(fā)模板消息時(shí), 7天內(nèi)可許開發(fā)者向用戶推送有限條數(shù)的模板消息(一次提交表單可發(fā)一條,多次提交可發(fā)條數(shù)獨(dú)立,互相不影響)

以上流程是建立在已經(jīng)在小程序中拿到了formid且formid沒有被使用的前提下。發(fā)送模板消息需要知道用戶的openid,獲取openid的流程不是必須的。如果用戶已經(jīng)在小程序中登錄,服務(wù)器也保存了openid和session_key,就沒有必要再使用code換取openid。
發(fā)送模板消息的流程還需要一個(gè)access_token。access_token在微信服務(wù)號或者訂閱號里經(jīng)常被作為令牌使用,access_token也是有失效期的,開發(fā)者應(yīng)該在真實(shí)的項(xiàng)目中像管理session_key一樣管理access_token。獲取access_token同樣需要攜帶微信小程序的appid和appsecret調(diào)用微信服務(wù)器。
流程圖中沒有標(biāo)示出formid的獲取過程,那么formid是怎么來的?
當(dāng)用戶提交表單時(shí),表單提交函數(shù)的event事件對象中將包含一個(gè)formid,這個(gè)formid只能使用一次,有效期為7天,一旦使用formid推送一條模板消息,這個(gè)formid就不可以再次使用。如果還想推送模板消息,就只能等用戶再一次提交表單并產(chǎn)生新的formid。
27、form表單組件
<form report-submit="true" bindsubmit="formSubmit" bindreset="formReset">
<button formType="submit">Submit</button>
<button formType="reset">Reset</button>
</form>
report-submit Boolean 是否返回 formId 用于發(fā)送模板消息
用戶點(diǎn)擊formType類型為submit的button之后,將執(zhí)行bindsubmit屬性所指定的響應(yīng)函數(shù),在響應(yīng)函數(shù)的event事件對象中將可以獲取form下所有表單元素的用戶輸入值。report-submit屬性為true,那么觸發(fā)bindsubmit后,event事件對象中將包含一個(gè)formid,在開發(fā)工具中獲取到的formid的值為 the formid is a mock one[圖片上傳失敗...(image-c6aada-1541424452744)]
這個(gè)并不是一個(gè)可以使用的formid,只有在真機(jī)上才能獲取真實(shí)的formid。
28、微信支付

支付流程看起來比較復(fù)雜,但微信提供了一個(gè)SDK,使用SDK基本上不需要編寫太多支付相關(guān)的代碼。事實(shí)上,在真實(shí)的項(xiàng)目中,支付相關(guān)的代碼復(fù)雜的不是支付本身,而是我們自己的業(yè)務(wù)邏輯。
以下是微信支付的最簡單流程,該流程不包括開發(fā)者自己的業(yè)務(wù)邏輯。
- 首先,小程序客戶端調(diào)用開發(fā)者自己的服務(wù)器,將一系列訂單信息發(fā)送到服務(wù)器,比如商品的id等信息。
- 開發(fā)者服務(wù)器接受被購買商品的信息后,調(diào)用微信服務(wù)器的統(tǒng)一下單API,生成一個(gè)預(yù)付單,并將預(yù)付單信息返回開發(fā)者服務(wù)器。統(tǒng)一下單API需要用戶的openid,如果已經(jīng)在服務(wù)器保存了openid,就不需要獲取用戶的openid了,如果沒有當(dāng)前支付用戶的openid,那么需要在第一步中攜帶code,以方便在這一步中使用code換取用戶的openid。
- 開發(fā)者服務(wù)器需要對預(yù)付單的信息簽名,并將預(yù)付單信息和簽名一起返回小程序客戶端。
- 小程序客戶端在收到預(yù)付單信息及簽名后,再調(diào)用wx.requestPayment,將預(yù)付單信息和簽名一起提交到微信服務(wù)器。
- 微信服務(wù)器會(huì)驗(yàn)證這些預(yù)付單信息,如果驗(yàn)證通過,那么小程序?qū)⒗鹬Ц督缑妫ㄔ陂_發(fā)工具中,這一步首先會(huì)彈出一個(gè)二維碼,開發(fā)者掃描這個(gè)二維碼將在開發(fā)者的微信中拉起支付界面)。
- 支付完成后,微信會(huì)主動(dòng)調(diào)用開發(fā)者服務(wù)器將支付結(jié)果推送到開發(fā)者服務(wù)器中,開發(fā)者可根據(jù)支付結(jié)果處理自己的業(yè)務(wù)邏輯。這一步需要開發(fā)者有自己的外網(wǎng)服務(wù)器,否則微信無法推送通知。
29、真實(shí)的微信小程序登錄狀態(tài)維護(hù)
在session_key的有效時(shí)間內(nèi)(未過期),開發(fā)者應(yīng)該自行維護(hù)session_key而不是重復(fù)獲取。
同時(shí),openid最好不要作為用戶的id被傳遞到小程序客戶端。開發(fā)者應(yīng)該有自己生成令牌作為自己業(yè)務(wù)的用戶id,而不應(yīng)該使用微信的openid。
以下是小程序登錄及登錄狀態(tài)維護(hù)流程圖

在小程序登錄及登錄狀態(tài)維護(hù)流程圖中,獲取到session_key和openid后不要將這兩個(gè)變量發(fā)送回客戶端。此時(shí)應(yīng)該生成一個(gè)令牌(隨機(jī)字符串,也就是圖中描述的3rd_session)作為用戶的session_key和openid的鍵,將令牌(鍵)和session_key、openid(值)保存到服務(wù)器的Redis或者M(jìn)emcache中。同時(shí),這個(gè)自己生成的令牌應(yīng)當(dāng)具有一定的時(shí)效性,時(shí)效性一定要小于30天,開發(fā)者可根據(jù)自己的小程序的安全性要求自行調(diào)整這個(gè)令牌的有效期。開發(fā)者應(yīng)當(dāng)將自己生成的令牌發(fā)動(dòng)到微信小程序并存入小程序的緩存中,每次請求服務(wù)器時(shí)都攜帶這個(gè)令牌,服務(wù)器接收令牌后在Redis中查找用戶真實(shí)的openid和session_key。
以上流程只是一個(gè)指導(dǎo)性的流程,在真實(shí)的項(xiàng)目中還有很多變化和細(xì)節(jié),但在總體思路上應(yīng)當(dāng)遵守上述流程。