強烈推介的(de)幾個(gè)微信小程序開發小技巧,簡單又實用
發表時(shí)間:2021-1-6
發布人(rén):融晨科技
浏覽次數:56
前段時(shí)間在(zài)下開發了(le/liǎo)個(gè)微信小程序,開發過程中總結了(le/liǎo)一些我覺得對我有用的(de)小技巧,提煉出(chū)來(lái),相當于(yú)一個(gè)總結複盤,也(yě)希望可以(yǐ)幫助到(dào)大(dà)家。如果對大(dà)家确實有幫助,别忘了(le/liǎo)點贊哦 :star2: ~
1.03.2006090
v2.12.1
1. 開發中可能遇到(dào)的(de)坑以(yǐ)及 Tips
本來(lái)想寫個(gè)小技巧的(de),結果我總結了(le/liǎo)一堆坑,沒上(shàng)手之(zhī)前完全想象不(bù)到(dào)微信小程序的(de)開發體驗是(shì)如此之(zhī)差、如此之(zhī)爛,從微信開發者工具到(dào)所謂的(de)「全新語言」,都有一種濃濃的(de)半成品的(de) five 即視感,實在(zài)讓我 emmm.... 另外我發現網上(shàng)的(de)小程序文章大(dà)部分都是(shì)如何使用和(hé / huò)如何避坑的(de)實用文,而(ér)不(bù)是(shì)技巧文,這(zhè)也(yě)從側面反映了(le/liǎo)小程序的(de)坑多。
在(zài)微信小程序原生開發過程中,我不(bù)斷發出(chū)這(zhè)樣的(de)疑問「爲(wéi / wèi)什麽堂堂技術人(rén)才多如牛毛的(de)騰訊,會推出(chū)如此 laji」,很多弱智反人(rén)類的(de)地(dì / de)方,在(zài)兩三年前社區就(jiù)已經提出(chū)來(lái),官方回複已經反饋正在(zài)修複中,但幾年過去了(le/liǎo),還是(shì)沒有音信,官方回複仍然是(shì)一句冷冰冰的(de)「已反饋」 :triumph:
- 微信開發者工具經常熱更新不(bù)起作用甚至白屏,重新編譯也(yě)不(bù)行,隻能強行退出(chū)後再次打開;
- 跟上(shàng)一條類似,有時(shí)候一點樣式出(chū)錯,預覽整個(gè)都白屏,調試器裏也(yě)不(bù)說(shuō)哪裏的(de)問題,直接就(jiù)給你棄療不(bù)顯示,重新編譯也(yě)無法解決問題,隻能強行退出(chū)後再次打開;
- 跟上(shàng)一條類似,調試器裏報的(de)錯經常沒什麽用,驢頭不(bù)對馬嘴,讓人(rén)很難定位問題;
- Android 端自定義 Tabbar 在(zài)下拉刷新的(de)時(shí)候,也(yě)會跟着屏幕一起往下移,而(ér)且是(shì)無法繞過的(de) Bug,自定義 Tabbar 樣式都寫好了(le/liǎo)的(de)我又改成自帶的(de) Tabbar 了(le/liǎo)!
-
import
的(de)路徑不(bù)支持絕對路徑,比如你希望引用utils/fetch.js
,在(zài)不(bù)管多深的(de)組件裏面你都要(yào / yāo)慢慢../
點到(dào)根目錄,同樣.wxss
文件@import
導入文件時(shí)也(yě)隻能使用相對路徑,所以(yǐ)就(jiù)會出(chū)現../../../../../../utils/fetch.js
這(zhè)種東西; - 靜态資源路徑不(bù)能有漢字,有漢字就(jiù)無法加載;
-
.wxs
文件不(bù)支持 ES6,隻能使用蹩腳的(de) ES5 寫法; -
.wxml
中隻能引入.wxs
文件不(bù)能引入.js
文件??? - 模闆
{{}}
中連方法都不(bù)能執行,隻能處理簡單的(de)運算如+ - * /
,如果遇到(dào)數據需要(yào / yāo)filter
的(de)場景,需要(yào / yāo)在(zài).js
文件中預先格式化好再一個(gè)個(gè)setData
,比如經常寫的(de)[2,3,4].includes(type)
,居然都跑不(bù)起來(lái)! -
.wxs
文件中無法使用Date
對象,所以(yǐ)不(bù)能new Date()
,隻能使用蹩腳的(de)getDate
方法,正則也(yě)是(shì)一樣,生成正則對象需要(yào / yāo)使用getRegExp
函數getRegExp(pattern[, flags])
; -
.wxs
中可以(yǐ)調用其它.wxs
文件,并且隻能 require 調用.wxs
文件,引入的(de)文件必須使用相對路徑; -
setData
連一個(gè)對象合并都懶得做,如果data: {a: {b: 1, c: 1}}
,那麽setData({a: {b: 2}})
就(jiù)會丢失a.c
的(de)值,真是(shì)讓人(rén)火冒三丈啊,還要(yào / yāo)setData({['a.b': 2]})
這(zhè)樣才行; - IOS 上(shàng)
Date
對象獲取任意時(shí)間參數比如getDay
、getTime
都爲(wéi / wèi) NaN,是(shì)因爲(wéi / wèi) IOS 的(de) Date 構造函數不(bù)支持2018-04-26
這(zhè)種格式的(de)日期,必須轉換爲(wéi / wèi)2018/04/26
這(zhè)種格式才會顯示正常; - 開發版小程序有時(shí)候請求莫名其妙發不(bù)出(chū)去,右上(shàng)角三個(gè)點 enable debug 打開「開發調試」之(zhī)後就(jiù)莫名其妙能發出(chū)去請求了(le/liǎo),在(zài)多部手機上(shàng)都是(shì)這(zhè)樣,不(bù)明真相。
2. 微信請求 Promise 化
2.1 使用現成的(de)庫
安裝 Promise 庫 wx-promise-pro ,記得一定要(yào / yāo)帶 -s
或 --production
,要(yào / yāo)不(bù)然無法構建成功。
npm i -S wx-promise-pro
然後在(zài) app.js
中:
import { promisifyAll } from 'wx-promise-pro'
promisifyAll() // promisify all wx api
App({ ... })
之(zhī)後就(jiù)可以(yǐ)正常使用了(le/liǎo):
wx.pro.showLoading({
title: '加載中',
mask: true
})
.then(() => console.log('in promise ~'))
2.2 自己實現
其實我們可以(yǐ)自己來(lái)實現一個(gè)這(zhè)樣的(de)庫,原理很簡單,以(yǐ)原生 API 的(de) wx.request 爲(wéi / wèi)例:
// 原生 API 使用方式
wx.request({
url: '', // 請求的(de) url
data: {}, // 參數
method: '', // post、get
success: res => {
// 請求成功回調函數,res爲(wéi / wèi)回調參數
},
fail: res => {
// 請求失敗回調函數,res爲(wéi / wèi)回調參數
}
})
如果我們将其 Promise 化,應該的(de)調用方式希望是(shì):
// Promise 化後的(de)期望使用方式
wx.pro.request({
url: '', // 請求的(de) url
data: {}, // 參數
method: '' // post、get
})
.then(res => {
// 請求成功回調函數,res爲(wéi / wèi)回調參數
})
.catch(res => {
// 請求失敗回調函數,res爲(wéi / wèi)回調參數
})
并且 then
函數返回的(de)是(shì)一個(gè) Promise 對象,讓這(zhè)個(gè)函數可以(yǐ)不(bù)斷鏈式調用下去,所以(yǐ)首先需要(yào / yāo) new
出(chū)來(lái)一個(gè) Promise 對象:
function request(opt) {
return new Promise((resolve, reject) => {
wx.request({
...opt,
success: res => { resolve(res)},
fail: res => {reject(res)}
})
})
}
這(zhè)裏代碼我們可以(yǐ)進一步改進,由于(yú) success
、 fail
這(zhè)裏傳入的(de)參數隻是(shì)由 resolve
、 reject
方法執行了(le/liǎo)下,所以(yǐ)可以(yǐ)直接傳入 resolve
、 reject
方法即可。
另外,由于(yú)其他(tā)小程序原生 API 格式一緻,所以(yǐ)我們可以(yǐ)使用柯裏化方法,來(lái)将其他(tā)需要(yào / yāo)進行 Promise 化的(de) API 進行處理:
function promisify(api) {
return (opt = {}) => {
return new Promise((resolve, reject) => {
api({
...opt,
fail: reject,
success: resolve
})
})
}
}
然後,将柯裏化方法執行的(de)結果作爲(wéi / wèi)新的(de) Promise 化的(de) API 挂載到(dào) wx.pro
對象上(shàng):
// 将指定 API 進行 Promise 化
wx.pro.request = promisify(wx.request)
// 使用
wx.pro.request({...})
.then(...)
然後爲(wéi / wèi)了(le/liǎo)方便我們使用其他(tā)方法,可以(yǐ)循環将 wx
對象上(shàng)可以(yǐ)被 Promise 化的(de)方法比如 request
、 scanCode
、 showToast
、 getUserInfo
等一一挂載到(dào) wx.pro
對象上(shàng),使用時(shí)可以(yǐ)直接 wx.pro.xx
,由于(yú)這(zhè)個(gè)方法執行返回的(de)是(shì)一個(gè) Promise 對象,因此可以(yǐ)像其它 Promise 化的(de)對象那樣使用。
事實上(shàng),不(bù)知不(bù)覺,我們就(jiù)自己實現了(le/liǎo) wx-promise-pro
的(de)源碼,這(zhè)個(gè)庫的(de)核心代碼也(yě)就(jiù)是(shì)上(shàng)面那這(zhè)幾行
2.3 在(zài)項目中使用
有了(le/liǎo)上(shàng)面的(de)工具後,我們可以(yǐ)将其使用在(zài)項目中,爲(wéi / wèi)了(le/liǎo)不(bù)在(zài)項目中遍布 wx.request
或 wx.pro.request
這(zhè)裏可以(yǐ)簡單進行封裝,新建兩個(gè)文件如下:
// utils/api/fetch.js 封裝請求方法、請求攔截器
const app = getApp()
const BaseUrl = 'http://172.0.0.1:7300/mock'
const TokenWhiteList = [
'/app/user/get-by-code' // 不(bù)需要(yào / yāo)鑒權的(de)api手動添加到(dào)這(zhè)裏
]
/**
* 設置請求攔截器
* @param params 請求參數
*/
const fetch = (params = {}) => {
// 攔截器邏輯
if (!TokenWhiteList.includes(params.url)) {
params.header = {
'content-type': 'application/json', // 默認值
'token': app.globalData.token || ''
}
}
if (params.url.startsWith('/')) { // 拼接完整URL
params.url = BaseUrl + params.url
}
// 返回promise
return wx.pro.request({ ...params })
.then(({ data: { code, message, data } }) => {
// ... 各種異常情況的(de)邏輯處理
// 與後端約定 code 20000 時(shí)正常返回
if (code === 20000) return Promise.resolve(data)
return Promise.reject(message)
})
}
export { fetch }
然後再将所有 API 封裝到(dào)單獨的(de)文件中集中管理:
// utils/api/apis.js 封裝所有請求 API
import { fetch } from './fetch'
/* 根據微信code獲取用戶信息 */
const appUserGetByCode = ({ code } = {}) => fetch({
url: '/app/user/get-by-code',
data: { code }
})
/* 掃碼登錄 */
const appUserQrLogin = ({ qrCode } = {}) => fetch({
method: 'POST',
url: '/app/user/qr-login',
data: { qrCode }
})
/* 個(gè)人(rén)信息 */
const appUserInfo = () => fetch({
url: '/app/user/info'
})
/* 系統參數獲取,數據字典 */
const appSysParamListByParam = () => fetch({
url: '/app/sys-param/list-by-param'
})
/* 數據字典所有 */
const appSysParamListAll = () => fetch({
url: '/app/sys-param/list-all'
})
export {
appSysParamListAll, // 數據字典所有
appSysParamListByParam, // 系統參數獲取,數據字典
appUserGetByCode, // 根據微信code獲取用戶信息
appUserQrLogin, // 掃碼登錄
appUserInfo // 個(gè)人(rén)信息
}
在(zài)要(yào / yāo)使用 API 的(de)地(dì / de)方就(jiù)可以(yǐ)這(zhè)樣引入:
import * as Api from '../../utils/api/apis.js' // 相對路徑
// 使用方式
Api.appSysParamListAll()
.then(({ dataList }) => this.upData({ sysParamList: dataList }))
.then(() => {
const keyList = this.data.sysParamList.map(T => T.key)
this.upData({
keyList,
formData: { keys: keyList }
})
})
使用方式就(jiù)很舒服,這(zhè)裏使用到(dào)了(le/liǎo) upData,就(jiù)是(shì)下面我要(yào / yāo)介紹的(de)内容,是(shì)在(zài)下非常推介的(de)小程序工具~
3. setState 修改 data 中想修改對象的(de)屬性
在(zài)小程序中, data
是(shì)不(bù)能直接操作的(de),需要(yào / yāo)使用 setData
函數。鑒于(yú)微信小程序開發時(shí) setData
的(de)使用體驗十分蹩腳,我使用了(le/liǎo)個(gè)庫函數 wx-updata
,這(zhè)個(gè)庫函數在(zài)開發的(de)時(shí)候對我很有幫助,這(zhè)裏特意推介給大(dà)家。
3.1 爲(wéi / wèi)什麽要(yào / yāo)使用 wx-updata
你在(zài)使用 setData
的(de)時(shí)候,是(shì)不(bù)是(shì)有時(shí)候覺得很難受,舉個(gè)簡單的(de)例子(zǐ):
// 你的(de) data
data: {
name: '蠟筆小新',
info: { height: 140, color: '黃色' }
}
如果要(yào / yāo)修改 info.height
爲(wéi / wèi) 155,使用 setData
要(yào / yāo)怎麽做呢:
// 這(zhè)樣會把 info 裏其他(tā)屬性整不(bù)見了(le/liǎo)
this.setData({ info: { height: 155 } })
// 你需要(yào / yāo)取出(chū) info 對象,修改後整個(gè) setData
const { info } = this.data
info.height = 155
this.setData({ info })
似乎并不(bù)太複雜,但如果 data
是(shì)個(gè)很大(dà)的(de)對象,要(yào / yāo)把比較深且不(bù)同的(de)對象、數組項挨個(gè)改變:
data: {
name: '蠟筆小新',
info: {
height: 140, color: '黃色',
desc: [{ age: 8 }, '最喜歡大(dà)象之(zhī)歌', '靓仔', { dog: '小白', color: '白色' }]
}
}
比如某個(gè)需求,需要(yào / yāo)把 info.height
改爲(wéi / wèi) 155,同時(shí)改變 info.desc
數組的(de)第 0 項的(de) age
爲(wéi / wèi) 12,第 3 項的(de) color
爲(wéi / wèi)灰色呢?
// 先取出(chū)要(yào / yāo)改變的(de)對象,改變數字後 setData 回去
const { info } = this.data
info.height = 155
info.desc[0].age = 12
info.desc[3].color = '灰色'
this.setData({ info })
// 或者像某些文章裏介紹的(de),這(zhè)樣可讀性差,也(yě)不(bù)太實用
this.setData({
'info.height': 155,
'info.desc[0].age': 12,
'info.desc[3].color': '灰色'
})
上(shàng)面這(zhè)兩種方法,是(shì)我們平常小程序裏經常用的(de),和(hé / huò)其他(tā) Web 端的(de)框架相比,就(jiù)很蹩腳,一種濃濃的(de)半成品感撲面而(ér)來(lái),有沒有這(zhè)樣一個(gè)方法:
this.upData({
info: {
height: 155,
desc: [{ age: 12 }, , , { color: '灰色' }]
}
})
這(zhè)個(gè)方法會幫我們深度改變嵌套對象裏對應的(de)屬性值,跳過數組項裏不(bù)想改變的(de),隻設置我們提供了(le/liǎo)的(de)屬性值、數組項,豈不(bù)是(shì)省略了(le/liǎo)一大(dà)堆蹩腳的(de)代碼,而(ér)且可讀性也(yě)極佳呢。
這(zhè)就(jiù)是(shì)爲(wéi / wèi)什麽我在(zài)上(shàng)線的(de)項目中使用 wx-updata ,而(ér)不(bù)是(shì) setData
wx-updata 的(de)原理其實很簡單,舉個(gè)例子(zǐ):
this.upData({
info: {
height: 155,
desc: [{ age: 12 }]
}
})
// 會被自動轉化爲(wéi / wèi)下面這(zhè)種格式,
// this.setData({
// 'info.height': 155,
// 'info.desc[0].age': 12,
// })
原來(lái)這(zhè)個(gè)轉化工作是(shì)要(yào / yāo)我們自己手動來(lái)做,現在(zài) wx-updata 幫我們做了(le/liǎo),豈不(bù)美哉!
3.2 wx-updata 使用方式
在(zài)一般情況下,我們可以(yǐ)将方法直接挂載到(dào) Page
構造函數上(shàng),這(zhè)樣就(jiù)可以(yǐ)在(zài) Page
實例中像使用 setData
一樣使用 upData
了(le/liǎo):
// app.js 中挂載
import { updataInit } from './miniprogram_npm/wx-updata/index' // 你的(de)庫文件路徑
App({
onLaunch() {
Page = updataInit(Page, { debug: true })
}
})
// 頁面代碼中使用方式
this.upData({
info: { height: 155 },
desc: [{ age: 13 }, '帥哥'],
family: [, , [, , , { color: '灰色' }]]
})
有的(de)框架可能在(zài) Page
對象上(shàng)進行了(le/liǎo)進一步修改,直接替換 Page
的(de)方式可能就(jiù)不(bù)太好了(le/liǎo), wx-updata
同樣暴露了(le/liǎo)工具方法,用戶可以(yǐ)在(zài)頁面代碼中直接使用工具方法進行處理:
// 頁面代碼中
import { objToPath } from './miniprogram_npm/wx-updata/index' // 你的(de)庫文件路徑
Page({
data: { a: { b: 2}, c: [3,4,5]},
// 自己封裝一下
upData(data) {
return this.setData(objToPath(data))
},
// 你的(de)方法中或生命周期函數
yourMethod() {
this.upData({ a: { b: 7}, c: [8,,9]})
}
})
針對修改數組指定項的(de)時(shí)候,可能存在(zài)的(de)跳過數組空位的(de)情況,wx-updata 提供了(le/liǎo) Empty 的(de) Symbol 類型替位符,還有數組的(de)對象路徑方式,感興趣可以(yǐ)看看 wx-updata 的(de) 文檔 。
另外,使用了(le/liǎo) wx-updata 也(yě)還可以(yǐ)使用原來(lái)的(de) setData,特别是(shì)有時(shí)候要(yào / yāo)清空數組時(shí),靈活使用,可以(yǐ)獲得更好的(de)小程序開發體驗,祝大(dà)家小程序開發愉快
4. 使用 scss 寫樣式
4.1 Webstorm 配置方法
關于(yú)蹩腳的(de) .wxss
樣式,我使用 webstorm 的(de) file watcher 工具把 scss 文件監聽改動并實時(shí)編譯成 .wxss
文件,感覺比較好用,這(zhè)裏給大(dà)家分享一下我的(de)配置:
然後記得在(zài) .gitignore
文件中加入要(yào / yāo)忽略的(de)樣式:
*.scss
*.wxss.map
這(zhè)樣在(zài)上(shàng)傳到(dào) git 的(de)時(shí)候,就(jiù)不(bù)會上(shàng)傳 scss 文件了(le/liǎo)~ 當然如果你的(de)團隊成員需要(yào / yāo) scss 的(de)話,還是(shì)建議 git 上(shàng)傳的(de)時(shí)候也(yě)加上(shàng) scss 文件。
這(zhè)樣設置之(zhī)後,一個(gè)組件在(zài)本地(dì / de)的(de)會是(shì)下面這(zhè)樣
其中我們需要(yào / yāo)關注的(de)就(jiù)是(shì) .js
、 .json
、 .scss
、 .wxml
文件,另外的(de)文件 .wxss
會在(zài)你改動 .scss
文件之(zhī)後自動生成并更新,而(ér) .wxss.map
是(shì)插件自動生成的(de)映射關系,不(bù)用管。
如果不(bù)是(shì)使用 webstorm,可以(yǐ)直接執行命令 sass --watch index.scss:index.wxss -s expanded
,命令行如果關閉,sass 命令就(jiù)不(bù)會監聽文件的(de)變動然後編譯,所以(yǐ)最好用編輯器的(de)插件。
同理,也(yě)可以(yǐ)使用 less、stylus 等預編譯語言。
4.2 Visual Studio Code 配置方法
萬能的(de) VSC 當然也(yě)可以(yǐ)做到(dào)這(zhè)個(gè)功能,搜索并下載插件 easy sass
,然後在(zài) setting.json
中修改/增加配置:
"easysass.formats": [
{
"format": "expanded",
"extension": ".wxss"
},
{
"format": "compressed",
"extension": ".min.wxss"
}
]
上(shàng)面 expanded
是(shì)編譯生成的(de) .wxss
文件,下面 compressed
是(shì)壓縮之(zhī)後的(de) .wxss
樣式文件,下面這(zhè)個(gè)用不(bù)到(dào)可以(yǐ)把下面這(zhè)個(gè)配置去掉,然後在(zài) .gitignore
文件中加入要(yào / yāo)忽略的(de)中間樣式:
*.scss
當然也(yě)可以(yǐ)不(bù)添加,如果你的(de)同事也(yě)是(shì)實用 scss 來(lái)開發小程序的(de)話,其他(tā)跟上(shàng)面一樣,至此你就(jiù)可以(yǐ)在(zài)小程序開發中快樂使用 scss 了(le/liǎo)~
5. 使用 iconfont 圖标字體
在(zài) Web 開發中 iconfont 可謂是(shì)最常用的(de)靈活圖标字體工具了(le/liǎo),這(zhè)裏介紹一下如何在(zài)微信小程序中引入 iconfont 圖标。
首先找到(dào)你想使用的(de)圖标們,點擊購物車之(zhī)後下載到(dào)本地(dì / de)。
下載到(dào)本地(dì / de)是(shì)一個(gè)壓縮包,解壓縮之(zhī)後将 iconfont.css
文件複制到(dào)微信小程序的(de) styles
文件夾中 (在(zài)下的(de)習慣,也(yě)可以(yǐ)放到(dào)你想放的(de)地(dì / de)方比如 fonts
),将後綴改爲(wéi / wèi) .wxss
在(zài) app.wxss
中引入樣式:
@import "styles/iconfont.wxss";
然後在(zài) .wxml
中就(jiù)可以(yǐ)使用剛剛你添加的(de)圖标了(le/liǎo),Web 使用 i
标簽,小程序中使用 text
标簽:
<text class="iconfont icon-my-edit" style="color: blue"></text>
如果後面要(yào / yāo)加新的(de)圖标,要(yào / yāo)下載新的(de) iconfont.css
的(de)文件到(dào)本地(dì / de)重命名并覆蓋,重新走一遍這(zhè)個(gè)流程。
當然,如果你使用的(de)樣式庫提供的(de)一些 icon 能滿足你的(de)要(yào / yāo)求,那更好,就(jiù)不(bù)用引入外部圖标字體文件了(le/liǎo),不(bù)過大(dà)部分情況下是(shì)不(bù)滿足的(de)