如今的前端工程師職責愈來愈重要,不少新的技術都是從前端領域分離出來,微信小程序就是一個很好的前端技術的實踐。開發微信小程序前,總以爲神祕面紗不可及,但通過前端團隊一個月辛苦奮戰,微信小程序今後再也不陌生,而變得熟悉和可控。如今,小程序終於上線了,我也終於有時間來分享一下開發過程當中遇到的問題。php
0、開發過程當中須要遵照的兩條原則css
①項目總體容量小於等於2M;②項目頁面棧容量5級html
官方聲明:爲了避免讓用戶在使用小程序時形成困擾,咱們規定頁面路徑只能是五層,請儘可能避免多層級的交互方式。前端
其餘注意事項:node
1、App() 必須在 app.js 中註冊,且不能註冊多個。git
2、不要在定義於 App() 內的函數中調用 getApp() ,使用 this 就能夠拿到 app 實例。程序員
3、不要在 onLaunch 的時候調用 getCurrentPages(),此時 page 尚未生成。數據庫
4、經過 getApp() 獲取實例以後,不要私自調用生命週期函數。json
5、爲了方便開發者減小配置項,咱們規定描述頁面的這四個文件必須具備相同的路徑與文件名。小程序
一、項目搭建過程
對於經驗豐富的程序員來講,項目搭建(前端框架搭建)實際上是最沒有技術含量的工做,但項目配置但是最有含金量的工做。具體項目搭建流程請參考微信小程序官方教程 ,此處略過。若果是團隊協做開發,須要將項目放在GitHub上的步驟是,先搭建項目,後放入GitHub本地倉庫後上傳。
由於,微信小程序在建立過程當中,若是選擇的本地文件夾是個空文件夾,開發者工具會提示,是否須要建立一個 quick start 項目。選擇「是」,開發者工具會幫助咱們在開發目錄裏生成一個簡單的 demo。若是文件夾不爲空,則不會生成demo。
二、項目目錄結構和功能說明
小程序包含一個描述總體程序的 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值進行處理
固然,項目的總體配置可根據項目要求進行靈活搭配,目錄結構和功能可依據需求進行定製。
三、每個單頁面都是由一個文件夾和四個文件組成
文件夾名稱是該單頁面的名稱,首字母大小寫都可,內容由JS、JSON,WXML和WXSS四個文件組成,文件功能可參考微信小程序具體說明。
注意:爲了方便開發者減小配置項,咱們規定描述頁面的這四個文件必須具備相同的路徑與文件名。
四、頁面棧原理
在小程序官方文檔 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()中包含的信息量很是大,有效利用這個對象對於開發效率事半功倍。
五、頁面傳參和參數回顯
因爲小程序開發沒有組建的概念,所以能夠理解爲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頁面會從新請求數據,並顯示出來。
六、css樣式
項目根目錄下的app.wxss文件是小程序公共樣式表,樣式重置、樣式初始化和公共樣式能夠放在這個文件中,這裏面的樣式屬於全局樣式,做用於任何一個頁面(即,在其餘頁面中不須要導入)。
同時,每一個單頁面都有本身依賴的樣式文件,對於可複用的單頁面的樣式文件,如B頁面能夠複用A頁面的樣式文件,能夠在B頁面的wxss樣式文件頭部導入A頁面的樣式文件,方式以下:@import '../A/A.wxss';
七、底部標籤導航的設置
補充頁面棧知識:微信小程序框架以棧的形式維護了當前全部頁面,當發生路由切換的時候,頁面棧的表現以下:初始化-新頁面入棧;打開新頁面新頁面入棧;頁面重定向-當前頁面出棧-新頁面入棧;頁面返回-頁面不斷出棧-直到目標返回頁-新頁面入棧;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"
}
]
},
八、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>
九、數據動態顯示
微信的數據都存儲在js文件中的data對象中,改變數據有兩種方式:this.data.key = value;這種方法不會觸發二次渲染;this.setData({key:value});這種方法能夠觸發二次渲染;所以,對於任何須要顯示的數據或元素,發生變化時須要用第二種方法。
十、注意事項
原型psd圖的尺寸在書寫wxss樣式文件時,按照1px寫成2rpx的方式
<Text>標籤嵌套<View>標籤後,<View>標籤中的任何內容都不會顯示出來
十一、項目總體 數據接口 和封裝 公共函數 對象配置
項目總體配置能夠在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 的格式函數
十二、表單組件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
})
}
1三、圖片的上傳和下載顯示
先後端開發下載顯示圖片的方案:
圖片的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數組拼接的字符串
}
})
})
樣式效果實例以下圖:
1四、獲取用戶地理位置名稱的方案
好比:北京市東城區和平西橋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;});
}
})
}
}
})
1五、自定義頁面的滾動選擇器
需求,以下圖所示,實現邏輯,第一步,對頁面元素進行堆疊排列,頁面正文內容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