小程序踩坑之旅
配置項app.json和page.json
app.json
1.pages: 這個用于設置頁面的路徑,通常訪問的頁面都放在pages文件夾里面,且必須在這個全局配置文件中設置好路徑,否則訪問的時候會報錯找不到該路徑,寫在 pages 字段的第一個頁面就是這個小程序的首頁(打開小程序看到的第一個頁面)
2.window: 全局樣式的配置
*1. 導航欄相關:*可配置導航欄背景顏色、標題顏色,標題內容,可以隱藏導航欄(navigationStyle設置為custom,但需要注意,自定義導航欄仍然保留右上角膠囊,且沒有返回鍵,如果想要實現返回功能,需要自己自定義返回的樣式和功能),該設置對web-view組件無效
2.窗口的背景色,backgroundColor,(即小程序下拉時露出的那一截),另外,僅ios支持的還有backgroundColorTop和backgroundColorBottom,頂部和底部窗口的背景色
3.下拉loading的樣式,只有黑色和白色(dark / light)
4. enablePullDownRefresh,小程序默認頁面是沒有下拉刷新功能的,如果在app.json中配置該項為true,則所有頁面都可以生效。如果只想在單個頁面使用下拉刷新,需要在對應的page.json中進行配置
5.onReachBottomDistance, 上拉加載更多生效時距頁面底部距離,默認為50px
6.屏幕旋轉,pageOrientation, 暫時還沒有用到過,支持 auto / portrait /landscape,默認為portrait豎屏顯示
3.tabbar: tab欄設置
1.位置:支持頂部和底部(通常使用底部tab欄),
2.position(bottom / top) tab列表 list: 文字以及對應的路徑
最少2個,最多5個
pagePath,頁面路徑(必須是pages中定義的路徑)
text, tab上的文字 iconPath, 圖片路徑,(大小限制為40kb,建議尺寸為 81px X 81px,不支持網絡圖片)
selectedIconPath,選中時的圖片路徑。限制同上
樣式相關:color字體顏色,selectedColor選中文字的顏色,backgroundColor是tab欄背景色 ,borderStyle上邊框顏色, custom是否自定義,默認為否, 具體文檔參考: 自定義tabBar;
3.networkTimeout(設置網絡請求的超時時間)
request
connectSocket
uploadFile
5.downloadFile debug, 開啟調試,會輸出Page 的注冊,頁面路由,數據更新,事件觸發等信息,方便查看生命周期的調試
6.navigateToMiniProgramAppIdList, 跳轉到其他小程序時,需要先在app.json中進行聲明
7.usingComponents,如果在app.json中聲明該組件,則全局可以使用該自定義組件,不需要再在page的json中聲明
8. permission,用于授權相關,平時項目中是自己寫的頁面,調用button的open-type屬性喚起授權,其實可以通過插件功能頁來實現,比如授權昵稱,用戶信息功能頁
page.json
頁面的樣式配置優先級比全局配置中的window高。
- 可以設置導航欄背景顏色、導航欄標題顏色,導航欄標題,
- 可以隱藏導航欄,custom 自定義導航欄,只保留右上角膠囊按鈕 窗口的背景色 , 以及ios支持頂部和底部窗口的背景色
- backgroundTextStyle,下拉 loading 的樣式,僅支持 dark / light
- enablePullDownRefresh開啟下拉刷新,這個屬性只控制當前的page頁面可以刷新
- onReachBottomDistance,上拉加載更多生效時距頁面底部距離,默認為50px
- pageOrientation,屏幕旋轉設置
- disableScroll,為 true 則頁面整體不能上下滾動。只在頁面配置中有效,無法在 app.json 中設置
- disableSwipeBack, 禁止頁面右滑手勢返回
- usingComponents, 使用自定義組件,如果沒有在頁面的json中聲明,直接使用組件會報錯
sitemap.json文件(用于微信索引)
默認規則為都默認被索引:{“action”: “allow”, “page”: “*”}
語法區別
wxml文件跟html的區別:
- 沒有div, p, span, 使用view代替div, text代替span
- wx:if的使用同vue中的v-if, 但使用上略有區別,不管在標簽中還是文本上,都是使用雙大括號來表示插值,wx:for=“{{array}}”,
條件渲染有wx:if, wx:elif, wx:else, 使用方式: WEBVIEW - 尤其注意,如果是使用boolean類型的值,不要直接在雙引號內直接寫,也需要寫在雙大括號里面。如 列表渲染wx:for=“{{array}}”,
可直接使用item和index,也可以使用wx:for-item=“idx”,
和wx:for-item="itemName"重新指定index和item的key值,花括號和引號之間如果有空格,將最終被解析成為字符串 - wxml文件中有一個標簽是block, 僅僅是一個包裝元素,不會在頁面中做任何渲染,只接受控制屬性
- wx:key的值只能是字符串或者 “*this”, wx:key="*this"表示for 循環中的 item 本身,但是要保證這個item本身是一個唯一的字符串或者數字, 當key綁定的是對象的時候會報錯
問題思考? - wx:if 和 hidden該怎么合理的判斷使用場景?
- wx:if 是惰性的,只有為true時才會局部渲染,當 wx:if
的條件值切換時,條件塊會在切換時銷毀或重新渲染。而hidden始終會被渲染,只是控制顯示與隱藏所以,如果需要頻繁切換的情景下,用
hidden 更好,如果在運行時條件不大可能改變則 wx:if 較好
組件
基礎組件
組件的公共屬性:
id, class, style, hidden, data-, bind/catch*
視圖容器:
- 可移動: movable-area, movable-view。 其中,movable-view必須在 movable-area組件中,并且必須是直接子節點
- 覆蓋原生組件:cover-view, cover-image,主要用于覆蓋map、video、canvas、camera、live-player、live-pusher等級別高的原生組件。注意,在cover-view中只能嵌套cover-view、cover-image和button
- 可滾動的視圖區域:scroll-view,豎向滾動時,需要給scroll-view一個固定高度
- 滑塊視圖容器: swiper, 其中只能放置swiper-item組件(常用于輪播圖)
- 視圖容器view, 如果想使用hover的樣式,可以指定按下去的樣式類,跟hover相關的屬性:hover-class,hover-stop-propagation, hover-start-time, hover-stay-time
圖標icon,在小程序中可以使用自帶的一些icon圖標,可以設置type、大小、顏色,詳情查看, 小程序icon組件
進度條,progress, 普通的進度條包括動畫等可以直接使用該組件
富文本,rich-txt, (可以使用該組件渲染html內容,傳入html字符串)
文本,text, (如果需要展示多個空格,可以設置space屬性)
按鈕,button,(可以設置disabled屬性進行禁用,防止多次點擊觸發事件)
單選、多選框:radio、radio-goup、checkbox、checkbox-group
富文本編輯器,editor
表單form,支持switch input checkbox slider radio picker 的提交
輸入框input,支持文本、數字、身份證、帶小數點的數字鍵盤等4個類型,password可以顯示密碼類型,(可以控制鍵盤右下角的文字,使用confirm-type屬性控制,包括“發送”、“搜索”、“下一個”、“前往”、“完成”)注意:input無法設置 font-family,使用的是系統字體
滾動選擇器,picker,支持普通、多列、時間、日期、省市選擇器,
嵌入頁面的滾動選擇器, picker-view, picker-view-column
滑動選擇器,slider,類似進度條不過可以拖動
開關,switch
多行輸入框,textarea
導航,navigator,(類似于a標簽,可以使用navigate、redirect、switchTab、reLaunch、navigateBack、exit(退出小程序)等類型)
系統相機,camera,可以用于掃描二維碼
視頻video,(注意,使用video組件在列表中的時候會引起小程序頁面卡頓甚至崩潰,目前項目中的解決方案是使用阿里云上傳視頻的截幀圖片來替換掉video組件,只有點擊圖片后才會播放視頻,查看網上資料說是video的src默認為空,解決了viode自動下載的bug,使用custom-cache="{{false}}"可以解決視頻緩存中卡住的問題,不過還沒有試過)
公眾號關注組件,official-account
承載網頁的容器,web-view,(適用于第三方網站內容的嵌入,注意需要在公眾平臺配置業務域名),避免在鏈接中帶有中文字符,在 iOS中會有打開白屏的問題,建議加一下encodeURIComponent
原生組件的使用限制
原生組件包括:
- camera
- canvas
- input(僅在focus時表現為原生組件)
- live-player
- live-pusher
- map
- textarea
- video
層級: 最高,其他組件無論設置 z-index 為多少,都無法蓋在原生組件上
原生組件不能設置動畫、fixed布局、不能使用overflow: hidden來裁剪原生組件的顯示區域,
事件監聽不能使用 bind:eventname 的寫法,只支持 bindeventname,不支持 catch 和 capture 的事件綁定方式
事件系統
事件處理函數,參數是event,event的事件內容包括:
- type
- timeStamp, 事件生成時的時間戳
- target,觸發事件的源組件
- currentTarget,事件綁定的當前組件
- detail, 自定義事件所攜帶的數據
- touches,一個數組,每個元素為一個 Touch 對象
- changedTouches, 有變化的觸摸點;
dataset:在target或currentTarget的事件對象中,可以通過dataset獲取當前自定義的一些數據,(在JS文件中使用event.currentTarget.dataset時,dataset的屬性會自動轉換字符串,連字符寫法會轉換成駝峰寫法,而大寫字符會自動轉成小寫字符)
mark,類似于dataset,(如果存在同名的 mark ,父節點的 mark 會被子節點覆蓋)
頁面的用戶行為:
- 下拉刷新 onPullDownRefresh
- 上拉觸底 onReachBottom
- 頁面滾動 onPageScroll
- 用戶轉發 onShareAppMessage
小程序中bind事件和catch事件有什么區別? - bind事件綁定不會阻止冒泡事件向上冒泡,
- catch事件綁定可以阻止冒泡事件向上冒泡
target和currentTarget有什么不同?
如上面這個示例中,點擊inner view會觸發handleTap3,同時會冒泡到handleTap2。對于handleTap2這個組件來說,此時收到事件對象的target就是inner的元素,而currentTarget是middle的部分
mark 和 dataset 有什么區別?
- mark 會包含從觸發事件的節點到根節點上所有的 mark: 屬性值;
- dataset 僅包含一個節點的 data- 屬性值
- 節點的 mark 不會做連字符和大小寫轉換
小程序相關機制
小程序框架的邏輯層并非運行在瀏覽器中,因此 JavaScript 在 web 中一些能力都無法使用,如 window,document 等
小程序的運行機制:冷啟動和熱啟動
冷啟動: 首次打開小程序;或小程序銷毀后打開。(冷啟動會觸發app.js中的onLaunch生命周期函數)
熱啟動: 點擊右上角膠囊退出、或者home鍵離開微信、或者左右滑動返回到微信等動作,都是熱啟動,并沒有銷毀小程序,(所以熱啟動不會觸發onLaunch函數,但會觸發onShow函數)
啟動場景
小程序啟動場景分為兩種:
從發現欄、另一個小程序返回、微信支付、首頁下拉小程序欄等為A場景
B場景:打開某個特定頁面:如轉發分享的卡片鏈接
熱啟動場景效果:
- 如果重新進入的場景和退出時的場景都是A場景,則保留原來狀態;例如:停留在“個人中心”頁面,退出后,下拉首頁的小程序列表進入后還是會停留在“個人中心”頁面,(觸發“個人中心”Page的onShow函數,但是不會觸發onLoad函數)
- 如果當前是A場景,上一個是B場景,則清空原來的頁面棧,打開首頁(即執行 wx.reLaunch
到首頁)如分享出去的卡片是“學員列表”頁面,點擊卡片停留后退出,再從首頁下拉的小程序進入,此時會跳轉到首頁 - 不管上一個場景是什么,如果當前是B場景,如點擊小程序的分享卡片,此時會清空原來的頁面棧,重新進入這個分享頁,首先觸發app中的onShow函數,然后觸發這個分享頁的onLoad函數,當然如果app中已經進行了跳轉到其它頁面,則不會再走這個分享頁。
冷啟動場景效果:
冷啟動規則比較簡單,如果冷啟動時是A場景,則進入小程序首頁
如果冷啟動時是B場景,則進入對應的特定頁面
更新機制
- 小程序首先會在后臺有一個最新版本,一般來說會在24小時內下發新版本信息到用戶,靜默更新到新版本(此時用戶是無感知態)
- 在后臺還沒及時更新版本時,每次冷啟動都會檢測是否有更新版本,如果有,會異步下載,同時啟動當前的舊版本包,所以新版本需要下一次的冷啟動才會應用上
- 如果啟動時就想立刻獲取最新版本,需要使用 wx.getUpdateManager
的API,用wx.showModal提示用戶是否重啟應用更新
ES6的支持情況:
- 微信小程序已經支持了絕大部分的 ES6 API,但部分API仍依賴于系統版本而不支持
- String的normalize在ios8,ios9不支持
- 數組的values在ios8和android不支持
- 數組的includes在ios8不支持
- Proxy不支持ios8、ios9和android
注冊小程序
- app.js中注冊小程序,整個小程序只有一個 App 實例,是全部頁面共享的。
- 開發者可以通過 getApp 方法獲取到全局唯一的 App 示例,獲取App上的數據或調用開發者注冊在 App 上的函數
- 在app.js中可以直接使用this.globalData賦值,但在其他頁面需通過getApp()方法獲取到實例后才能使用全局變量globalData
注冊頁面和自定義組件
頁面
- data 是頁面第一次渲染使用的初始數據
- onLoad頁面加載時觸發。一個頁面只會調用一次;
- onShow在頁面顯示/切入前臺時觸發
- onPageScroll,監聽用戶滑動頁面事件,(請避免在 onPageScroll 中過于頻繁的執行 setData
等引起邏輯層-渲染層通信的操作。尤其是每次傳輸大量數據,會影響通信耗時) - onShareAppMessage監聽頁面內的轉發,轉發有button 組件
open-type="share"和右上角轉發兩個方式,(只有Page中定義了onShareAppMessage,右上角才會出現轉發功能) - setData:1.將數據從邏輯層發送到視圖層(異步),同時改變對應的 this.data 的值(同步)(跟react的setState類似,但又不一樣)2.直接修改 this.data 而不調用 this.setData 是無法改變頁面的狀態的,還會造成數據不一致;3.不要把 data 中任何一項的 value 設為 undefined;4.如果需要視圖層更新完畢后再處理事件,可以將事件放在setData的回調函數中;
Page構造器
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() { } })自定義組件
- 頁面使用自定義組件時需要現在page的json文件中聲明usingComponents定義段
- 組件使用頁面的生命周期方法時(即 on 開頭的方法),應該寫在methods定義段中
- behaviors提取所有頁面中公用的代碼段,例如,在所有頁面被創建和銷毀時都要執行同一段代碼,就可以把這段代碼提取到 behaviors
中
- 在 properties 定義段中,屬性名采用駝峰寫法(propertyName);在 wxml中,指定屬性值時則對應使用連字符寫法(component-tag-name property-name=“attr value”),應用于數據綁定時采用駝峰寫法(attr=“”)
- observers數據監聽器,(類似于vue中的watch),具體請參考 數據監聽器,created,組件實例剛被創建,(注意此時不能調用 setData)
- definitionFilter,定義段過濾器,用于自定義組件擴展(如擴展自定義組件一個computed計算屬性功能)
注意:
- 使用 this.data 可以獲取內部數據和屬性值;但直接修改它不會將變更應用到界面上,應使用 setData 修改
- 生命周期函數無法在組件方法中通過 this 訪問到
組件間的通信與事件
- 父傳子:父組件綁定屬性值,子組件通過properties獲取,
- 子組件向父組件傳遞數據:使用事件傳遞
- 父組件訪問子組件: 父組件可以通過 this.selectComponent
方法獲取子組件實例對象,這樣就可以直接訪問組件的任意數據和方法
此處列舉第二種自定義事件傳遞數據的方法:
注意:
- 組件和引用組件的頁面不能使用id選擇器(#a)、屬性選擇器([a])和標簽名選擇器,請改用class選擇器
- 避免使用后代選擇器(.a .b)
- 子元素選擇器(.a>.b)只能用于 view 組件與其子節點之間,用于其他組件可能導致非預期的情況
- app.wxss 中的樣式、組件所在頁面的的樣式對自定義組件無效(除繼承樣式外,如 font 、 color會繼承自組件外)
組件間關系
場景: 如果一個頁面中需要使用到A組件和B組件,而A組件與B組件之間需要通信,此時可以在組件定義時加入relations定義段,如封裝的ul和li組件后,之間需要存在關聯的父子關系
- 必須在兩個組件定義中都加入relations定義,否則不會生效
- type,與目標組件的相對關系,可選的值為 parent 、 child 、 ancestor 、 descendant
- 另一種情況是關聯一類組件(如form與input、checkbox等的關系),需要使用到behavior
使用setData應注意的事項:
- 不要頻繁的去 setData,否則可能導致Android 下用戶在滑動時會感覺到卡頓,或者渲染有出現延時
- 不要每次 setData 都傳遞大量新數據
- 不要在后臺態頁面進行 setData
小程序運行流程
- 微信打開小程序前,會把整個小程序的代碼包下載到本地
- 通過 app.json 的 pages 字段就可以知道你當前小程序的所有頁面路徑
- 寫在 pages 字段的第一個頁面就是這個小程序的首頁(打開小程序看到的第一個頁面),所以微信會把首頁的代碼裝載進來
- 小程序啟動,觸發app.js 定義的 App 實例的
onLaunch回調,啟動時也會觸發onShow函數,或切前臺、從其他頁面返回到這個頁面,都會觸發onShow;onHide監聽小程序切后臺,(如tab切換,navigateTo,離開微信等);
onPageNotFound頁面不存在監聽函數 - 頁面的渲染流程開始,微信客戶端會先根據 page的json文件配置生成一個界面
- 接著裝載這個頁面的 WXML 結構和 WXSS 樣式
- 最后客戶端會裝載 page的js文件
- Page構造器會生成一個頁面,生成頁面的時候小程序框架會把 data 數據和 index.wxml 一起渲染出最終的結構,渲染完界面
- 渲染完界面之后,頁面實例就會收到一個 onLoad 的回調
生命周期函數
小程序里面又三種生命周期:小程序運行的生命周期(在app.js中處理),頁面的生命周期(在page的js文件中處理,或者組件的pageLifetimes觸發的生命周期函數),組件的生命周期
小程序生命周期函數
- onLaunch, 小程序初始化完成時觸發,全局只觸發一次
- onShow,小程序啟動,或從后臺進入前臺顯示時觸發
- onHide,從前臺進入后臺時觸發
- onError,小程序發生腳本錯誤或 API 調用報錯時觸發
- onPageNotFound,小程序要打開的頁面不存在時觸發
onLaunch,onShow參數:
- path: 打開小程序的頁面路徑
- query: 打開小程序的頁面參數query
- scene: 打開小程序的場景值,
- shareTicket: 轉發分享參數
- referrerInfo:當場景為由從另一個小程序或公眾號或App打開時,返回此字段
- referrerInfo.appId: 來源小程序或公眾號或App的 appId
- referrerInfo.extraData: 來源小程序傳過來的數據,scene=1037或1038時支持
頁面生命周期函數
- onLoad,監聽頁面加載
- onShow, 監聽頁面顯示(如tab切換或者離開微信、或navigateTo)
- onReady, 監聽頁面初次渲染完成(一個頁面只會調用一次,代表頁面已經準備妥當)
- onHide, 監聽頁面隱藏, (如 wx.navigateTo 或底部 tab 切換到其他頁面,小程序切入后臺等)
- onUnload, 監聽頁面卸載(如wx.redirectTo或wx.navigateBack到其他頁面時)
組件生命周期函數
- created,在組件實例剛剛被創建時執行,注意此時不能調用 setData
- attached,在組件實例進入頁面節點樹時執行
- ready,在組件布局完成后執行
- moved, 在組件實例被移動到節點樹另一個位置時執行
- detached, 在組件實例被從頁面節點樹移除時執行
注意:
組件中存在一個組件所在頁面的生命周期,定義在pageLifetimes中,其中可用的生命周期包括:
- show, 組件所在的頁面被展示時執行
- hide, 組件所在的頁面被隱藏時執行
- resize, 組件所在的頁面尺寸變化時執行
網絡
- 服務器域名配置,當使用體驗版或者開發版報“服務器開小差”了的錯誤,大部分可能性都是因為沒有打開調試,因為在調試模式下,小程序不會去校驗域名的合法性。
- 小程序只可以跟指定的域名與進行網絡通信,包括普通 HTTPS
請求(wx.request)、上傳文件(wx.uploadFile)、下載文件(wx.downloadFile) 和 WebSocket
通信(wx.connectSocket) - 如果接入第三方網頁,使用web-view組件,需要配置業務域名
- 默認超時時間為60s,超時時間可以在 app.json 或 game.json 中通過 networktimeout 配置
- 并發限制:wx.request、wx.uploadFile、wx.downloadFile 的最大并發限制是 10 個;wx.connectSockt 的最大并發限制是 5 個
- 返回狀態,只要成功接收到服務器返回,無論 statusCode 是多少,都會進入 success 回調
自定義tabBar
用戶可以通過配置app.json 中的 tabBar 項指定 custom 字段為true,來自定義tabBar,不過有些使用地方還需要注意:
- tabBar 的相關配置項仍然需完整聲明,以兼容低版本以及區分哪些是tab頁
- 需要用戶自定義一個組件來渲染 tabBar,推薦用 fixed 在底部的 cover-view + cover-image組件渲染樣式,以保證 tabBar 層級相對較高
- 與 tabBar 樣式相關的接口,如 wx.setTabBarItem 等將失效
- 可以在自定義組件下通過 getTabBar 接口,獲取當前頁面的自定義 tabBar 組件實例
小程序登錄流程
小程序登錄需要調用微信的開放接口,整體流程如下:
- 小程序通過wx.login()獲取一個code
- 調用后端的api,發送code,后端通過這個code來校驗接口,并返回一個自定義登錄態
- 小程序將登錄狀態存入storage
- 之后再訪問服務器發送業務請求就只用后端判斷登錄態查詢openid和session_key返回業務數據
小程序優化操作
- 縮短白屏時間:1.首屏渲染的內容較多,需要集合多份數據進行渲染:此時可以把優先級高的內容做優先展示;2.首屏內容依賴的數據從服務端請求的時間太長:分析數據返回的時間長的原因;3.一次性渲染數據太大或依賴的計算過于復雜:減少渲染的數據量、優化渲染相關數據的算法
- 渲染界面的耗時過長,需要校驗下是否同時渲染的區域太大(例如列表過長)
- 腳本執行時間過長:需要確認并優化腳本的邏輯
- setData調用頻繁:避免無用的頻繁調用,每秒調用setData的次數不超過 20 次,
- setData的數據太大,setData的數據在JSON.stringify后不超過 256KB
- setData一個未綁定的變量
- 開啟 HTTP 緩存控制
- 控制WXML節點數
- 控制圖片大小,圖片寬高都不超過實際顯示寬高的3倍
- 合理控制網絡請求數量
- 控制圖片請求數
- 開啟慣性滾動:wxss中帶有overflow: scroll的元素,在 iOS
下需要設置-webkit-overflow-scrolling: touch樣式 - 避免使用:active偽類來實現點擊態
- 保持圖片大小比例
- 可點擊元素的寬高都不小于 20px
- iphoneX兼容,用以下wxss進行兼容:
- 避免JS異常
- 不使用廢棄接口
- 設置最低基礎庫版本
- 移除不可訪問到的頁面
- 移除大量未使用的樣式
- 及時回收定時器:定時器是全局的,并不是跟頁面綁定的,當小程序從一個頁面路由到另一個頁面之后,前一個頁面定時器應注意手動回收
總結
- 上一篇: 语句摘抄——第14周
- 下一篇: 让工控机通过笔记本的Wifi实现上网