微笑小程式的相關知識
現在的前端工程師職責越來越重要,很多新的技術都是從前端領域分離出來,微信小程式就是一個很好的前端技術的實踐。開發微信小程式前,總覺得神祕面紗不可及,但經過前端團隊一個月辛苦奮戰,微信小程式從此不再陌生,而變得熟悉和可控。現在,小程式終於上線了,我也終於有時間來分享一下開發過程中遇到的問題。
0、開發過程中需要遵守的兩條原則
①專案整體容量小於等於2M;②專案頁面棧容量5級
官方宣告:為了不讓使用者在使用小程式時造成困擾,我們規定頁面路徑只能是五層,請儘量避免多層級的互動方式。
其他注意事項:
一、App() 必須在 app.js 中註冊,且不能註冊多個。
二、不要在定義於 App() 內的函式中呼叫 getApp() ,使用 this 就可以拿到 app 例項。
三、不要在 onLaunch 的時候呼叫 getCurrentPages(),此時 page 還沒有生成。
四、通過 getApp() 獲取例項之後,不要私自呼叫生命週期函式。
五、為了方便開發者減少配置項,我們規定描述頁面的這四個檔案必須具有相同的路徑與檔名。
1、專案搭建過程
對於經驗豐富的程式設計師來說,專案搭建(前端框架搭建)其實是最沒有技術含量的工作,但專案配置可是最有含金量的工作。具體專案搭建流程請參考微信小程式官方教程 ,此處略過。若果是團隊協作開發,需要將專案放在GitHub上的步驟是,先搭建專案,後放入GitHub本地倉庫後上傳。
因為,微信小程式在建立過程中,如果選擇的本地資料夾是個空資料夾,開發者工具會提示,是否需要建立一個 quick start 專案。選擇“是”,開發者工具會幫助我們在開發目錄裡生成一個簡單的 demo。如果資料夾不為空,則不會生成demo。
2、專案目錄結構和功能說明
小程式包含一個描述整體程式的 app 和多個描述各自頁面的 page,專案框架搭建成功後,可以看到微信小程式的目錄結構非常簡單:根目錄結構是,一個pages資料夾,同級目錄還有三個檔案(app.js、app.json、app.wxss),pages資料夾存放所有頁面。
對實際專案結構目錄進行改造:
在根目錄下建立images資料夾用來存放圖片;
建立utils資料夾用來存放公共js檔案,比如,表單驗證函式庫(還可以包含時間格式化模組formatTime,域名配置模組domainConfig,省市區三級內容模組city):
在utils資料夾下面新建validater.js檔案,var validater = {...some function}建立物件封裝一系列函式,最後匯出module.exports.validater = validater;
在根目錄指令碼檔案app.js中載入,letvalidater = require('utils/validater.js'); || import { validater } from 'utils/validater';兩種方式均可,並在專案的整個生命週期函式上註冊App({validater: validater});。(node的module遵循CommonJS規範,requirejs遵循AMD,seajs遵循CMD)
在實際頁面進行呼叫,getApp().validater.isEmail(value),對具體value值進行處理
當然,專案的整體配置可根據專案要求進行靈活搭配,目錄結構和功能可依據需求進行定製。
3、每一個單頁面都是由一個資料夾和四個檔案組成
資料夾名稱是該單頁面的名稱,首字母大小寫均可,內容由JS、JSON,WXML和WXSS四個檔案組成,檔案功能可參考微信小程式具體說明。
注意:為了方便開發者減少配置項,我們規定描述頁面的這四個檔案必須具有相同的路徑與檔名。
4、頁面棧原理
在小程式官方文件 API章節中的導航目錄中,封裝了5種導航方式,分別為wx.navigateTo、wx.redirectTo、wx.switchTab、wx.navigateBack、wx.reLaunch。由於微信的頁面路徑深度最多是五層,因此在用選擇當行方式很重要。因為,微信小程式的左上角有返回按鈕,返回按鈕的意思是回退到上一個頁面,但當導航跳轉方式選擇錯誤,第一,會導致返回的不是上一個頁面;第二,導航跳轉無法載入,由於5級頁面棧容量已經飽和。
wx.navigateTo(OBJECT)保留當前頁面,跳轉到應用內的某個頁面,該方法會往頁面棧中增加一條記錄;
wx.redirectTo(OBJECT)關閉當前頁面,跳轉到應用內的某個頁面,該方法會不會增加頁面棧記錄,保持頁面棧原始內容;
wx.reLaunch(OBJECT)基礎庫 1.1.0 開始支援,低版本需做相容處理,關閉所有頁面,開啟到應用內的某個頁面,該方法會清空頁面棧全部記錄;
wx.switchTab(OBJECT)跳轉到 tabBar 頁面,並關閉其他所有非 tabBar 頁面,該方法和和頁面棧無關;
wx.navigateBack(OBJECT)關閉當前頁面,返回上一頁面或多級頁面,該方法會刪除頁面棧中一條記錄;
檢視頁面棧容量的方法,以及各頁面中儲存的資料:可通過 getCurrentPages() 獲取當前的頁面棧資訊,
let arr = getCurrentPages();//頁面棧資料陣列,儲存頁面棧中頁面js檔案中data物件
arr[arr.length - 1]可以獲取到當前頁面的相關資訊;
比如,獲取(重置)上一個頁面的中的某個引數,let previousThis = arr[arr.length - 2],previousThis.data.contactorList可以拿到資料,previousThis.data.setData({contactorList:[]})可以重置資料。
頁面棧陣列物件getCurrentPages()中包含的資訊量非常大,有效利用這個物件對於開發效率事半功倍。
5、頁面傳參和引數回顯
由於小程式開發沒有組建的概念,因此可以理解為SPA單頁面應用開發,對於一款產品,只要頁面的基本功能相同,就可以複用。因此,一款再複雜的產品,不同的單頁面也不超過十幾個。在小程式開發過程中,複用頁面是最常用的方案。比如,A頁面可以實現某個功能,這時C頁面、D頁面和F頁面都需要用A頁面的功能,就可以把A頁面當做一個模板來使用,A頁面接收父級頁面傳遞來的引數,進行處理後,可以傳遞給父級頁面需要的資料。
頁面之間的通訊:
A頁面跳轉到B頁面,通過URL拼接傳遞引數常用的兩種方案:
方案一:A頁面通過新增點選事件跳轉傳參,wx.navigateTo({url:'./B/index?parameter01=one¶meter02=two'}),B頁面接收A頁面的引數,onLaunch:function(options){console.log(options.parameter01)}。A頁面通過URL地址問號?後拼接的引數,可以在B頁面的 onLaunch函式 和 onShow函式 的形參中options物件中獲取。
注意事項:通過URL拼接的方式傳遞引數的型別是物件或者陣列,在傳遞的時候應當使用JSON.stringify(obj || array)進行json資料編碼,然後在拿到資料時應當使用JSON.parse(obj || array)進行資料解碼。
方案二:A頁面通過導航元件navigator傳遞引數,<view><navigator url="./B/index?title=navigate">跳轉到新頁面</navigator></view>,B頁面接收引數童方案一。
getCurrenPage獲得A中的引數,可做回
不通過URL拼接傳遞引數的方案:通過原生陣列物件 getCurrentPages() 獲取頁面棧資訊,從而拿到需要的引數。方案具體步驟如下:
例項:A頁面 data物件 中的一個 address引數 是用來儲存使用者的住址資訊並顯示在A頁面的具體位置,當用戶點選A頁面的地址輸入框時會跳轉到儲存地址列表的B頁面,使用者可點選直接選擇獲取,選擇成功會跳轉到A頁面,並顯示使用者的選項。這個互動功能涉及到兩個知識點:
第一,B頁面給A頁面引數賦值的方案
var arr=getCurrentPages(),獲取頁面棧陣列;
var previousThis=arr[arr.length - 2],獲取A頁面的this指標;
previousThis.data.address='new address',給A頁面的資料賦值;
第二,在A頁面中操作B頁面的顯示內容
previousThis.setData({address:previousThis.data.address}),可在B頁面返回A頁面之前,讓A頁面正確顯示出使用者的操作內容。(可以將上述的previousThis.data.address='new address'和previousThis.setData({address:previousThis.data.address})步驟合二為一成previousThis.setData({address:'new address'}))
在實際專案的開發過程中,一個頁面顯示的所有內容,往往是從介面直接拿到返回的資料顯示出來,在B頁面進行操作的時候,也是和後臺介面通訊,對資料庫內容進行增加和刪除,因此在做回顯處理時,不同過頁面棧資訊的方式操作回顯內容,而是通過重新請求介面的方式來重新整理並顯示最新資料。
因此,通過wx.navigateBack(OBJECT)從B頁面返回A頁面後,保證A頁面顯示的是最新資料,需要在A頁面中做特殊處理,A頁面中所有從後臺介面拿到的作為顯示的資料,進行wx.request({url: 'test.php',data: {},header: {},success: function(res) {}})資料請求的方法必須放在onShow: function(options) {// Do something when show.}中,這樣才能保證wx.navigateBack(OBJECT)執行後從B頁面返回A頁面後,A頁面會重新請求資料,並顯示出來。
6、css樣式
專案根目錄下的app.wxss檔案是小程式公共樣式表,樣式重置、樣式初始化和公共樣式可以放在這個檔案中,這裡面的樣式屬於全域性樣式,作用於任何一個頁面(即,在其他頁面中不需要匯入)。
同時,每個單頁面都有自己依賴的樣式檔案,對於可複用的單頁面的樣式檔案,如B頁面可以複用A頁面的樣式檔案,可以在B頁面的wxss樣式檔案頭部匯入A頁面的樣式檔案,方式如下:@import '../A/A.wxss';
7、底部標籤導航的設定
補充頁面棧知識:微信小程式框架以棧的形式維護了當前所有頁面,當發生路由切換的時候,頁面棧的表現如下:初始化-新頁面入棧;開啟新頁面新頁面入棧;頁面重定向-當前頁面出棧-新頁面入棧;頁面返回-頁面不斷出棧-直到目標返回頁-新頁面入棧;Tab 切換-頁面全部出棧-只留下新的 Tab 頁面;重載入-頁面全部出棧-只留下新的頁面。
底部Tab導航的配置在app.json中tabBar物件進行設定,例項如下:
"tabBar": {
"selectedColor": "#00B4FF",//tab 上的文字選中時的顏色
"list": [//tab 的列表,詳見 list 屬性說明,最少2個、最多5個 tab
{
"pagePath": "pages/index/index",//頁面路徑,必須在 pages 中先定義
"text": "首頁",
"color": "",//tab 上的文字預設顏色
"iconPath": "image/1.png",
"selectedIconPath": "image/1_hover.png"
},
{
"pagePath": "pages/message/index",
"text": "訊息",
"iconPath": "image/2.png",
"selectedIconPath": "image/2_hover.png"
},
{
"pagePath": "pages/my/index",
"text": "我的",
"iconPath": "image/3.png",
"selectedIconPath": "image/3_hover.png"
}
]
},
8、HTML5標籤的自定義屬性data-*
在html標籤中加入自定義屬性data-*用於儲存頁面的自定義資料,然後在元素繫結的方法中可以獲取資料
注意事項:屬性名不能包含大寫字母,在 data- 後必須至少有一個字元;該屬性可以是任何字串;自定義屬性字首 "data-" 會被客戶端忽略。
具體應用:
場景一:
<View wx:for="{{contactorList}}" wx:key="unique">
<View bindtap="reEditor" data-info="{{item}}" data-reeditIndex="{{index}}"></View>
</View>
reEditor(e){
let info = e.currentTarget.dataset.info;//js中獲取繫結的資料
let index = e.currentTarget.dataset.index;//js中獲取繫結的資料
}
微信小程式的 列表渲染 很常用,對一個數組的資料重複渲染出該元件集合,需求:對陣列的每一個數據需要繫結事件同時獲取 資料 和 索引值,此時,可以用自定義屬性data-*在html元素上繫結資料,在函式中的e物件中獲取繫結的資料。
注意:繫結的函式bindtap必須和data-*繫結的資料在同一個html元素上繫結,不然無法獲取資料
場景二:
通過獲取 html元素物件 屬性的方法getAttribute()獲取繫結的資料,也可以通過 獲取元素節點後 用 HTML5自定義屬性物件Dataset 獲得需要的資料document.getElementById('owl').dataset.animal-type
function showDetails(animal)
{
var animalType = animal.getAttribute("data-animal-type");
alert("The " + animal.innerHTML + " is a " + animalType + ".");
}
<ul>
<li onclick="showDetails(this)" id="owl" data-animal-type="bird">Owl</li>
<li onclick="showDetails(this)" id="salmon" data-animal-type="fish">Salmon</li>
</ul>
9、資料動態顯示
微信的資料都儲存在js檔案中的data物件中,改變資料有兩種方式:this.data.key = value;這種方法不會觸發二次渲染;this.setData({key:value});這種方法可以觸發二次渲染;因此,對於任何需要顯示的資料或元素,發生變化時需要用第二種方法。
10、注意事項
原型psd圖的尺寸在書寫wxss樣式檔案時,按照1px寫成2rpx的方式
<Text>標籤巢狀<View>標籤後,<View>標籤中的任何內容都不會顯示出來
11、專案整體 資料介面 和封裝 公共函式 物件配置
專案整體配置可以在app.js的App({})方法中配置,App() 函式用來註冊一個小程式。接受一個 object 引數,其指定小程式的生命週期函式等。
App({
HOST: ,//主機域名
loginCode: ,//使用者身份驗證碼
validater: ,//正則校驗物件
formatTime:,//日期時間物件
onLaunch: function (e) {},
onShow: function (e) {},
onHide: function (e) {},
onError: function (e) {},
});
配置好以上檔案後,在進行資料請求時的形式:
wx.request({url: getApp().HOST + '/interface/name',header: {},data: { key:value },method: 'GET',
success: function(res) {},
fail: function(res) {}
})
在進行方法呼叫時的形式:getApp().formatTime.time(str);將 時間戳 轉換為 08:30 的格式函式
12、表單元件input應用
基礎應用,一個input元件繫結一個函式:
<input bindinput="input" value="{{inputValue}}" placeholder="" />
input: function(e) {
this.setData({
inputValue: e.detail.value
})
},
多輸入應用,一個input元件繫結一個函式看起來很繁瑣,可以採用多個input元件繫結一個函式的方案:
<input bindinput="inputFn" value="{{info.name}}" data-key="name"/>
<input bindinput="inputFn" value="{{info.age}}" data-key="age"/>
<input bindinput="inputFn" value="{{info.address}}" data-key="address"/>
<input bindinput="inputFn" value="{{info.mobile}}" data-key="mobile"/>
inputFn(e){
var key = e.target.dataset.key;
var value = e.detail.value
this.data.info[key] = value;
this.setData({
info: this.data.info
})
}
13、圖片的上傳和下載顯示
前後端開發下載顯示圖片的方案:
圖片的html容器:
<view wx:for="{{imageArr}}" wx:key="item.id">
<image class="slide-image" mode="scaleToFill" src="{{Item}}" data-index="{{index}}"></image>
</view>
圖片資料的請求處理:
wx.request({
url: url,
success(res){
let data = res.data.imageArr;//返回的圖片url沒有域名,只有相對路徑,需要做域名拼接處理["/1.jpg","/2.jpg","/3.jpg"]
for(let i = 0; i < data.length; i++) {data[i] = 'http://img.wanshaobo.com' + data[i];}
this.setData({imageArr: data});//圖片顯示操作
}
})
圖片的本地顯示和網路上傳方案:
圖片的html容器,HTML結構的理解可以參考下面的例項圖片:
<view wx:for="{{imageArr}}" wx:key="id">
<image mode="scaleToFill" src="{{item.imgUrl}}"></image>
<icon type="clear" size="16" bindtap="deleteImg"></icon>
</view>
<view bindtap="uploadImg"><image src="../plus.png"></image></view>
圖片的新增顯示和網路上傳:
第一步,點選加號圖示向頁面新增並顯示圖片,呼叫微信小程式API-媒體-圖片-wx.chooseImage(OBJECT)從本地相簿選擇圖片或使用相機拍照
wx.chooseImage({
count: 9 - imageArr.length,
success: function (res) {
//res.tempFilePaths圖片的本地檔案路徑列表:["wxfile://tmp_1.png","wxfile://tmp_2.png","wxfile://tmp_3.png"]
//res.tempFiles圖片的本地檔案列表,每一項是一個 File 物件:[{path:"wxfile://tmp_1.png",size:1021},{path:"wxfile://tmp_2.png",size:21},{path:"wxfile://tmp_3.png",size:103}]
this.data.imageArr.push(res.tempFilePaths)
that.setData({imageArr:this.data.imageArr});
}
})
第二步,呼叫微信小程式API-網路-上傳、下載-wx.uploadFile(OBJECT)
將本地資源上傳到開發者伺服器。如頁面通過 wx.chooseImage 介面獲取到一個本地資源的臨時檔案路徑後,可通過此介面將本地資源上傳到指定伺服器。客戶端發起一個 HTTPS POST 請求,其中 content-type 為 multipart/form-data 。
上傳圖片到自己圖片伺服器後的成功回撥函式會返回一個物件,該物件是儲存圖片的伺服器返回的資料,包含了該圖片的URL地址,這個URL地址就是以後拿到該圖片的唯一URL路徑。
imageArr.forEach((item,index)=>{
wx.uploadFile({
url: 'https://wsb-file.wanshaobo.com/file/simpleUpload',
filePath: item,
name: 'file',
header: { 'content-type': 'multipart/form-data' },
success: function (res) {
//res.data:"{"msg":"上傳成功","code":200,"filePath":"/group1/M00/0A/F1/wKgGS1lfUYeAfnPLAAATCakKLos829.png"}"
//res.errMsg:"uploadFile:ok"
this.data.imgsArr.push(JSON.parse(res.data).filePath)//圖片成功上傳返回的URL路徑陣列,不包含主機名
this.data.imgsStr = this.data.imgsArr.join(',')//儲存在資料庫中屬於該使用者的圖片URL陣列拼接的字串
}
})
})
樣式效果例項如下圖:
14、獲取使用者地理位置名稱的方案
比如:北京市東城區和平西橋58號
需要用的的API介面:
API-開放介面-設定-wx.getSetting(OBJECT)//獲取使用者當前設定,成功回撥res.authSetting ={
scope.userInfo": true,//使用者資訊
"scope.userLocation": true//地理位置
"scope.address": true//通訊地址
"scope.record": true//錄音功能
"scope.writePhotosAlbum": true//儲存到相簿
}
API-開放介面-授權-wx.authorize(OBJECT)
API-位置-獲取位置-wx.chooseLocation(OBJECT)
具體程式碼分析:第一步,通過 wx.getSetting 查詢使用者是否授權了 "scope.userLocation" 這個 scope;第二步,通過 wx.authorize 介面開啟‘地理位置’授權介面對userLocation進行授權;第三步,通過 wx.chooseLocation 介面開啟地圖選擇位置
wx.getSetting({
success(res) {
if (!res['scope.userLocation']) {
wx.authorize({
scope: 'scope.userLocation',
success() {
wx.chooseLocation({success: function (res) {res.address;res.longitude;res.longitude;});
}
})
}
}
})
15、自定義頁面的滾動選擇器
需求,如下圖所示,實現邏輯,第一步,對頁面元素進行堆疊排列,頁面正文內容z-index:0;半透明蒙層z-index=1,寬高佔據滿屏;滾動選擇器z-index=2:第二步,對選擇器樣式進行設計,需要用到<picker-view><picker-view-column></picker-view-column></picker-view>元件;第三步,對透明蒙層區域、取消按鈕、確定按鈕新增事件處理函式。
微信小程式的原生滾動選擇器僅有三種類型,普通選擇器,時間選擇器,日期選擇器,但現在的選擇器需求是:年月日時分秒。
HTML結構如下:
<view class="mask" bindtap="clickMask"></view>
<view class="datePicker">
<view class="pickerBtn"><text bindtap="cancel">取消</text><text bindtap="confirm">確定</text></view>
<picker-view value="{{dateValue}}" bindchange="dateChange">
<picker-view-column><view wx:for="{{years}}">{{item}}年</view></picker-view-column>
<picker-view-column><view wx:for="{{months}}">{{item}}月</view></picker-view-column>
<picker-view-column><view wx:for="{{days}}">{{item}}日</view></picker-view-column>
<picker-view-column><view wx:for="{{hours}}" >{{item}}時</view></picker-view-column>
<picker-view-column><view wx:for="{{minutes}}">{{item}}分</view></picker-view-column>
<picker-view-column><view wx:for="{{seconds}}">{{item}}秒</view></picker-view-column>
</picker-view>
</view>
資料繫結方案:
資料初始化,定義初始化顯示的資料是當前的日期時間:
this.setData({dateValue: ['0',date.getMonth(),date.getDate()-1,date.getHours(),date.getMinutes()]})
年份列表顯示當前年份到後三十年,對於具體月份顯示的天數列表需要做特殊處理,1-3-5-7-8-10-12每月31天,4-6-9-11每月30天,2月的天數最為特殊,閏年2月份為29天,平年2月份為28天,每月天數處理如下:
var date = new Date();
var year = date.getFullYear();//獲取當前年份
var month = date.getMonth() + 1;//獲取當前月份
var days ;//定義當月的天數;
if(month == 2){
days= ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0) ? 29 : 28;//閏年29天,還是平年
}else if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12){
days= 31;//月份為:1,3,5,7,8,10,12 時,大月.天數為31
}else{
days= 30;//其他月份,小月,天數為30.
}
for (let i = 1 ; i <= days; i++) {
daysArr.push(i)
}
月份列表滾動選擇器發生變化時,天數選擇器的天數陣列也需要做響應式實時變化,當滾動選擇,value 改變時觸發 change 事件,event.detail = {value: value};value為陣列,表示 picker-view 內的 picker-view-column 當前選擇的是第幾項(下標從 0 開始):
dateChange(e){//月份發生變化時需要改變響應的天數
var date = new Date(),days = []
var year = date.getFullYear() + e.detail.value[0];//獲取年份
var month = e.detail.value[1] + 1;//獲取月份
var days ;//定義當月的天數;
if(month == 2){
days= ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0) ? 29 : 28;//閏年29天,還是平年
}else if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12){
days= 31;//月份為:1,3,5,7,8,10,12 時,大月.天數為31
}else{
days= 30;//其他月份,小月,天數為30.
}
this.setData({days: days})
var dateArr = e.detail.value
this.setData({
year: this.data.years[dateArr[0]],
month: this.data.months[dateArr[1]],
day: this.data.days[dateArr[2]],
hour: this.data.hours[dateArr[3]],
minute: this.data.minutes[dateArr[4]]
})
}
---------------------
作者:FEBruce
來源:CSDN
原文:https://blog.csdn.net/wanshaobo888/article/details/74452402
版權宣告:本文為博主原創文章,轉載請附上博文連結!