在正式開發支付寶小程式之前,我們要做一些準備工作,比如說封裝一下config檔案、request請求、api介面集中定義,自定義的工具指令碼等。文末還有彩蛋哈~
第一步:在支付寶開發者工具中新增支付寶小程式。
第二步:在小程式檔案的根目錄建立utils目錄。
建立自定義的相關js檔案(或者直接從以前定義好的專案中拷貝過來),如apis.js、config.js、tools.js、http.js、tools.sjs
1.小程式中呼叫介面的集中展示,加上備註資訊,這樣在後臺檢視的時候方便一些吧。
//apis.js //此處為封裝的請求方法 import { getJSON } from './http' const apis = { // 使用者登入 userLogin(params, method = 'GET') { return getJSON('/api/login?r=' + new Date().getTime(), params, method) }, } module.exports = apis
2.配置檔案,開發的時候難免遇到介面地址切換開發環境、測試環境、生產環境等各種環境,我們事先配置好檔案,在切換的時候直接修改環境引數即可。
// 後臺不同環境 const ENV = { SIT: 'sit', UAT: 'uat', PRD: 'prd' }; //後臺環境對應的介面目錄 const BUILD_INFO = { sit: { api: 'http://127.0.0.1:8080', domain: '' }, uat: { api: 'https://**.com', domain: 'https://**.com' }, prd: { api: 'https://**.com', domain: 'https://**.com' } } // 小程式APP_id const APP_ID = { sit: '', uat: '', prd: '' } // 不同環境的版本 const VERSION = { sit: '1.2.1', uat: '1.0.1', prd: '1.0.16' } //實際使用的後臺環境 const env = ENV.PRD; const config = { ENV: env, API_URL: BUILD_INFO[env].api, APP_ID: APP_ID[env], DOMAIN_URL: BUILD_INFO[env].domain, VERSION: VERSION[env] }; module.exports = config;
3.封裝一些常用的工具函式以便我們提升開發效率。比如小程式中的頁面跳轉一般情況下要繫結事件,然後定義事件方法,在其中定義跳轉的頁面,此處我封裝了一個go2page的方法,這樣就不用在小程式頁面的指令碼中定義各種跳轉事件了。不過就是需要在引用go2page的地方新增引數到data中。
在使用go2page方法進行頁面跳轉後,我們需要從query中提取引數。使用下面的curPagePath方法即可。
const tools = { isObject(obj) { if (Object.prototype.toString.call(obj) === '[object Object]') { return true } return false }, isDefine(str) { if (str === 0) { return true } if (str == '' || (!str)) { return false } return true }, isIDCard(str) { const reg = /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/ return reg.test(str) }, isPhone(str) { const myreg = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/ return myreg.test(str) }, isFullName(str) { const reg = /^[\u4E00-\u9FA5\uf900-\ufa2d·s]{2,20}$/ return reg.test(str) }, getUrlParam(href) { const indexWen = href.indexOf('?') const indexJing = href.indexOf('#') const theRequest = {} if (indexWen === -1) { return theRequest } if (indexJing < indexWen) { // 如果#號在問好前面 href = href.substring(indexWen + 1) } else { href = href.substring(indexWen + 1, indexJing) } const strArr = href.split('&') for (let i = 0; i < strArr.length; i++) { theRequest[strArr[i].split('=')[0]] = unescape(strArr[i].split('=')[1]) } return theRequest }, stringifyParam(param = {}) { let paramString = '' for (let key in param) { if (paramString === '') { paramString += key + '=' + param[key] } else { paramString += '&' + key + '=' + param[key] } } return paramString }, // 生成隨機碼 getRandom(num) { const char = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' const result = [] for (let i = 0; i < num - 1; i++) { const index = Math.random() * char.length - 1 const value = char.charAt(index) result.push(value) } return result.join('') }, desensitizing(str = '', from = 0, length = 0, replaceStr = '*') { // 隱藏某些字串 if (str.length === 0) { return str } // 變成陣列 const strArr = str.split('') // 更改陣列 strArr.splice(from, length, replaceStr.repeat(length)) return strArr.join('') }, htmlEncode(str = '') { if (str === '') { return str } let s = str.replace(/&/g, '&') s = s.replace(/</g, '<') s = s.replace(/>/g, '>') s = s.replace(/\'/g, ''') s = s.replace(/\"/g, '"') return s }, getDay(day) { var today = new Date(); var targetday_milliseconds = today.getTime() + 1000 * 60 * 60 * 24 * day; today.setTime(targetday_milliseconds); //注意,這行是關鍵程式碼 var tYear = today.getFullYear(); var tMonth = today.getMonth(); var tDate = today.getDate(); tMonth = this.doHandleMonth(tMonth + 1); tDate = this.doHandleMonth(tDate); return tYear + "-" + tMonth + "-" + tDate; }, doHandleMonth(month) { var m = month; if (month.toString().length == 1) { m = "0" + month; } return m; }, getWeek(dateString) { var dateArray = dateString.split("-"); var date = new Date(dateArray[0], parseInt(dateArray[1] - 1), dateArray[2]); return "周" + "日一二三四五六".charAt(date.getDay()); }, getUserInfo() { my.getStorage({ key: 'userInfo', success: function (res) { console.log('獲取快取中使用者資訊', res) return res.data }, fail: function (res) { console.log('獲取快取中使用者資訊失敗', res) return {} } }); }, checkToken(callback, param = {}) { let that = this var app = getApp() if (app.globalData.userId) { callback.call(this, param); } else { setTimeout(function () { param = Object.assign(param, app.globalData) that.checkToken(callback, param); }, 200); } }, go2page(e) { const { dataset } = e.target let params = [] for (let key in dataset) { if (key != 'url') { params.push(key + '=' + dataset[key]) } } if (!this.isDefine(e.target.dataset.url)) { my.showToast({ type: 'success', content: '建設中,敬請期待', duration: 3000, success: () => { }, }); } else { let url = e.target.dataset.url if (params.length > 0) { url = url + '?' + params.join('&') } my.navigateTo({ url: url }); } }, round(str) { if(!str){return ''} return str.toFixed(2) }, curPagePath(query){ let pages = getCurrentPages(); let currPage = null; // console.log(pages) 的到一個陣列 if (pages.length) { // 獲取當前頁面的物件(上邊所獲得的陣列中最後一項就是當前頁面的物件) currPage = pages[pages.length - 1]; } // 獲取當前頁面的路由 let route = currPage.route let params = [] if(query){ for (let key in query) { params.push(key + '=' + query[key]) } } if(params.length>0){ route += '?' + params.join('&') } console.log('當前頁面路徑==>',route) }, getTimeList(start, end, step,restStart,restEnd,selectDate) { var curDate = new Date(), curFullYear = curDate.getFullYear(), curMonth = curDate.getMonth() + 1, curDay = curDate.getDate() curMonth = curMonth < 10 ? '0' + curMonth : curMonth curDay = curDay < 10 ? '0' + curDay : curDay var ymd = curFullYear + '/' + curMonth + '/' + curDay selectDate = selectDate.replace(/-/g,'/') console.log('selectDate',selectDate) var startTime = new Date(ymd + ' ' + start).getTime(), endTime = new Date(ymd + ' ' + end).getTime(), restStartTime = new Date(ymd + ' ' + restStart).getTime(), restEndTime = new Date(ymd + ' ' + restEnd).getTime() var returnArr = [] while (startTime + step * 60 * 1000 <= endTime) { // 中途休息的時間階段 if(startTime + step * 60 * 1000>restStartTime && startTime + step * 60 * 1000<=restEndTime){ startTime += step * 60 * 1000 continue } // 如果時間小於當前時間則跳過顯示 if(startTime + step * 60 * 1000<new Date().getTime() && ymd == selectDate){ startTime += step * 60 * 1000 continue } returnArr.push(this.getTime(startTime) + '-' + this.getTime(startTime+step*60*1000)) startTime += step * 60 * 1000 } return returnArr }, getTime(timestamp) { var date = new Date(timestamp);//時間戳為10位需*1000,時間戳為13位的話不需乘1000 var Y = date.getFullYear() + '-'; var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'; var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '; var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'; var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ''; var s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); return h + m; }, isIphoneX(){ var u = navigator.userAgent; var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); if (isIOS) { if (screen.height == 812 && screen.width == 375) { return true; } else { return false; } } } } module.exports = tools;
4.常用的請求方法,根據實際專案中約定的介面規範進行修改。
import config from './config'; function isApiSuccess(result) { const status = result.success || result.stat || result.status || ''; // 介面狀態欄位 if (status == true || status == '000' || status === 'ok' || status === 'success' || status === 'succeed') { return true; } else { return false; } } export function getJSON(url, params, method = 'GET', mockSetting = {}) { let path = ''; const { on = false, mockData = {} } = mockSetting; if (url.indexOf('http') > -1 || url.indexOf('https') > -1) { path = url; } else { path = config.API_URL + url; } return new Promise((resolve, reject) => { my.showLoading(); if (on) { console.log('-------返回 mock 資料-------', url, params, mockData); if (isApiSuccess(mockData)) { resolve(mockData); } else { reject(mockData); } my.hideLoading(); return; } let token = params.token let headers = { "Content-Type": "application/json", } if (params.headers) { headers = Object.assign(headers, params.headers) delete params.headers } if (token) { headers['Authorization'] = 'Bearer ' + token delete params.token } if (method.toLocaleLowerCase() != 'get') { headers['content-type'] = 'application/json' } if (method.toLocaleLowerCase() == 'delete') { headers['content-type'] = 'application/x-www-form-urlencoded' } my.request({ url: path, method: method, data: params, dataType: 'json', headers: headers, success: (result) => { // console.log(`--返回資料--介面地址=>${url},結果=>${JSON.stringify(result)}`); my.hideLoading(); if (isApiSuccess(result.data)) { resolve(result.data); } else { reject(result.data); } }, fail: (err) => { console.log('-------返回錯誤-------', url, err); my.hideLoading(); if (err.status == 401) { console.log('未登入許可權') } my.showToast({ type: 'fail', content: '請求異常,請稍後再試!' }); reject(err); }, }); }); }
5.在頁面中引用方法時需要用到sjs檔案。
const desensitizing = (str,from,length,replaceStr = '*')=>{ if (str.length === 0) { return str } // 變成陣列 const strArr = str.split('') let replace_str = '' for(var i=0;i<length;i++){ replace_str += replaceStr } // 更改陣列 strArr.splice(from, length, replace_str) return strArr.join('') } const repeat = (length,repeatStr='*') =>{ var str = '' for(let i=0;i<length;i++){ str += repeatStr } return str } const desensitizing2 = (str,type,isdes=false,replaceStr = '*')=>{ if(!isdes){ return str } if(str == '' || !str){ return '' } switch(type){ case 'name': str = str.replace(getRegExp('·','g'),replaceStr); if(str.length == 1){ return replaceStr } if(str.length>=2){ if(str.length>=5){ return repeat(4,replaceStr) + str[str.length-1] } return desensitizing(str,0,str.length-1) } return replaceStr; break; case 'sfz': return desensitizing(str,1,str.length-2) break; case 'phone': return desensitizing(str,3,6) break; } } const substring = (str,from,length=0)=>{ if(str.length == 0){ return '' } return str.substring(from,from+length) } const isDefine = (str)=>{ if (str === '' || !str) { return false } return true } const round = (str,len=2)=>{ if(!str){return ''} let point_index = str.lastIndexOf('.') if(len == 0){ return str.substring(0,1) } if(str.length > len + point_index + 1){ return str.substring(0,len + point_index + 1) } return str } const str2date = (str,separater='-') => { if(str === '' || !str || str.length<8) { return '' } return str.substring(0,4) + separater + str.substring(4,6) + separater + str.substring(6,8) } export default { desensitizing, substring, isDefine, round, desensitizing2, str2date }
以上準備工作完成之後,需要引用一下支付寶小程式的ui元件,我用的是mini-ali-ui。
這下終於可以開發愉快地寫頁面了吧。此時此刻你在想什麼呢。
作為一個經常使用mac、linux系統的程式猿,想借助於開發工具中的控制檯進行頁面建立,怎麼辦呢?
這時候要用到node的功能了,在根目錄下新增package.json
{ "dependencies": { "iconv-lite": "^0.6.3" }, "scripts": { "page": "node scripts/page" } }
然後在根目錄新增scripts目錄,在scripts目錄中新增page.js
示例程式碼如下:此程式僅支援 “pages/分組名稱/頁面名稱”這種結構的頁面建立,若需要減小層級或增加層級自行修改哈。
/** * pages頁面快速生成指令碼 * 用法:npm run tep `檔名` * npm run page product/ProductClass */ const fs = require('fs'); var iconv = require('iconv-lite'); const dirName = process.argv[2]; const cover = process.argv[3]; console.log(dirName) const dirNameArr = dirName.split('/') const folder = dirNameArr[0] const fileName = dirNameArr[1] const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1); console.log(folder, fileName, capPirName, __dirname) if (!dirName) { console.log('資料夾名稱不能為空!'); console.log('示例:npm run page product/ProductClas'); process.exit(0); } var curPath = __dirname.substring(0, __dirname.lastIndexOf('/')) console.log('curPath==', curPath) function loadjson(filepath) { var data; try { var jsondata = iconv.decode(fs.readFileSync(curPath + '/' + filepath, "binary"), "utf8"); data = JSON.parse(jsondata); } catch (err) { console.log(err); } return data; } function savejson(filepath, data) { var datastr = JSON.stringify(data, null, 4); if (datastr) { try { fs.writeFileSync(curPath + '/' + filepath, datastr); } catch (err) { } } } //model模板 const wxmlTemp = ` <view> </view> ` const jsTemp = ` import tools from '/utils/tools' import { getJSON } from '/utils/http' import apis from '/utils/apis' const app = getApp() Page({ data: { }, onLoad: function (options) { tools.curPagePath(options) }, onShow: function () { }, go2page(e){ tools.go2page(e) } }) ` const wxssTemp = ` ` const jsonTemp = ` { "usingComponents": {} } ` if (!fs.existsSync(`pages/${folder}`)) { fs.mkdirSync(`pages/${folder}`) } if (!fs.existsSync(`pages/${folder}/${fileName}`)) { fs.mkdirSync(`pages/${folder}/${fileName}`) } else { // 如果資料夾存在則說明頁面已建立,不能再操作了,否則會覆蓋已有頁面 if (cover && cover != 1) { process.exit(0); } } process.chdir(`pages/${folder}/${fileName}`); // cd $1 fs.writeFileSync(`${fileName}.axml`, wxmlTemp) fs.writeFileSync(`${fileName}.json`, jsonTemp) fs.writeFileSync(`${fileName}.acss`, wxssTemp) fs.writeFileSync(`${fileName}.js`, jsTemp) var jsonPath = 'app.json' var appJson = loadjson(jsonPath) if (appJson) { var pages = appJson.pages if (pages.indexOf(`pages/${folder}/${fileName}/${fileName}`) == -1) { pages.push(`pages/${folder}/${fileName}/${fileName}`) } savejson(jsonPath, appJson) } process.exit(0);
終於寫完了,以上是我個人在開發支付寶小程式時的經驗總結,僅供參考。不喜可以直接跳過~~~