前段時間在下開發了個微信小程序,開發過程當中總結了一些我以爲對我有用的小技巧,提煉出來,至關於一個總結覆盤,也但願能夠幫助到你們。若是對你們確實有幫助,別忘了點贊哦 🌟 ~javascript
1.03.2006090
(2020-06-19)v2.12.1
(2020-08-04)原本想寫個小技巧的,結果我總結了一堆坑,沒上手以前徹底想象不到微信小程序的開發體驗是如此之差、如此之爛,從微信開發者工具到所謂的「全新語言」,都有一種濃濃的半成品的 five 即視感,實在讓我 emmm.... 另外我發現網上的小程序文章大部分都是如何使用和如何避坑的實用文,而不是技巧文,這也從側面反映了小程序的坑多。css
在微信小程序原生開發過程當中,我不斷髮出這樣的疑問「爲何堂堂技術人才多如牛毛的騰訊,會推出如此 laji」,不少弱智反人類的地方,在兩三年前社區就已經提出來,官方回覆已經反饋正在修復中,但幾年過去了,仍是沒有音信,官方回覆仍然是一句冷冰冰的「已反饋」 😤html
import
的路徑不支持絕對路徑,好比你但願引用 utils/fetch.js
,在無論多深的組件裏面你都要慢慢 ../
點到根目錄,一樣 .wxss
文件 @import
導入文件時也只能使用相對路徑,因此就會出現 ../../../../../../utils/fetch.js
這種東西;.wxs
文件不支持 ES6,只能使用蹩腳的 ES5 寫法;.wxml
中只能引入 .wxs
文件不能引入 .js
文件???{{}}
中連方法都不能執行,只能處理簡單的運算如 + - * /
,若是遇到數據須要 filter
的場景,須要在 .js
文件中預先格式化好再一個個 setData
,好比常常寫的 [2,3,4].includes(type)
,竟然都跑不起來!.wxs
文件中沒法使用 Date
對象,因此不能 new Date()
,只能使用蹩腳的 getDate
方法,正則也是同樣,生成正則對象須要使用 getRegExp
函數 getRegExp(pattern[, flags])
;.wxs
中能夠調用其它 .wxs
文件,而且只能 require 調用 .wxs
文件,引入的文件必須使用相對路徑;setData
連一個對象合併都懶得作,若是 data: {a: {b: 1, c: 1}}
,那麼 setData({a: {b: 2}})
就會丟失 a.c
的值,真是讓人火冒三丈啊,還要 setData({['a.b': 2]})
這樣才行;Date
對象獲取任意時間參數好比 getDay
、getTime
都爲 NaN,是由於 IOS 的 Date 構造函數不支持 2018-04-26
這種格式的日期,必須轉換爲 2018/04/26
這種格式纔會顯示正常;安裝 Promise 庫 wx-promise-pro,記得必定要帶 -s
或 --production
,要否則沒法構建成功。前端
npm i -S wx-promise-pro
而後在 app.js
中:java
import { promisifyAll } from 'wx-promise-pro' promisifyAll() // promisify all wx api App({ ... })
以後就能夠正常使用了:git
wx.pro.showLoading({ title: '加載中', mask: true }) .then(() => console.log('in promise ~'))
其實咱們能夠本身來實現一個這樣的庫,原理很簡單,以原生 API 的 wx.request 爲例:github
// 原生 API 使用方式 wx.request({ url: '', // 請求的 url data: {}, // 參數 method: '', // post、get success: res => { // 請求成功回調函數,res爲回調參數 }, fail: res => { // 請求失敗回調函數,res爲回調參數 } })
若是咱們將其 Promise 化,應該的調用方式但願是:web
// Promise 化後的指望使用方式 wx.pro.request({ url: '', // 請求的 url data: {}, // 參數 method: '' // post、get }) .then(res => { // 請求成功回調函數,res爲回調參數 }) .catch(res => { // 請求失敗回調函數,res爲回調參數 })
而且 then
函數返回的是一個 Promise 對象,讓這個函數能夠不斷鏈式調用下去,因此首先須要 new
出來一個 Promise 對象:npm
function request(opt) { return new Promise((resolve, reject) => { wx.request({ ...opt, success: res => { resolve(res)}, fail: res => {reject(res)} }) }) }
這裏代碼咱們能夠進一步改進,因爲 success
、fail
這裏傳入的參數只是由 resolve
、reject
方法執行了下,因此能夠直接傳入 resolve
、reject
方法便可。json
另外,因爲其餘小程序原生 API 格式一致,因此咱們可使用柯里化方法,來將其餘須要進行 Promise 化的 API 進行處理:
function promisify(api) { return (opt = {}) => { return new Promise((resolve, reject) => { api({ ...opt, fail: reject, success: resolve }) }) } }
而後,將柯里化方法執行的結果做爲新的 Promise 化的 API 掛載到 wx.pro
對象上:
// 將指定 API 進行 Promise 化 wx.pro.request = promisify(wx.request) // 使用 wx.pro.request({...}) .then(...)
而後爲了方便咱們使用其餘方法,能夠循環將 wx
對象上能夠被 Promise 化的方法好比 request
、scanCode
、showToast
、getUserInfo
等一一掛載到 wx.pro
對象上,使用時能夠直接 wx.pro.xx
,因爲這個方法執行返回的是一個 Promise 對象,所以能夠像其它 Promise 化的對象那樣使用。
事實上,不知不覺,咱們就本身實現了 wx-promise-pro
的源碼,這個庫的核心代碼也就是上面那這幾行 🥳
有了上面的工具後,咱們能夠將其使用在項目中,爲了避免在項目中遍及 wx.request
或 wx.pro.request
這裏能夠簡單進行封裝,新建兩個文件以下:
// utils/api/fetch.js 封裝請求方法、請求攔截器 const app = getApp() const BaseUrl = 'http://172.0.0.1:7300/mock' const TokenWhiteList = [ '/app/user/get-by-code' // 不須要鑑權的api手動添加到這裏 ] /** * 設置請求攔截器 * @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 } }) => { // ... 各類異常狀況的邏輯處理 // 與後端約定 code 20000 時正常返回 if (code === 20000) return Promise.resolve(data) return Promise.reject(message) }) } export { fetch }
而後再將全部 API 封裝到單獨的文件中集中管理:
// 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 } }) /* 我的信息 */ 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 // 我的信息 }
在要使用 API 的地方就能夠這樣引入:
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 } }) })
使用方式就很舒服,這裏使用到了 upData,就是下面我要介紹的內容,是在下很是推介的小程序工具~ 🥳
在小程序中,data
是不能直接操做的,須要使用 setData
函數。鑑於微信小程序開發時 setData
的使用體驗十分蹩腳,我使用了個庫函數 wx-updata
,這個庫函數在開發的時候對我頗有幫助,這裏特地推介給你們。
你在使用 setData
的時候,是否是有時候以爲很難受,舉個簡單的例子:
// 你的 data data: { name: '蠟筆小新', info: { height: 140, color: '黃色' } }
若是要修改 info.height
爲 155,使用 setData
要怎麼作呢:
// 這樣會把 info 裏其餘屬性整不見了 this.setData({ info: { height: 155 } }) // 你須要取出 info 對象,修改後整個 setData const { info } = this.data info.height = 155 this.setData({ info })
彷佛並不太複雜,但若是 data
是個很大的對象,要把比較深且不一樣的對象、數組項挨個改變:
data: { name: '蠟筆小新', info: { height: 140, color: '黃色', desc: [{ age: 8 }, '最喜歡大象之歌', '靚仔', { dog: '小白', color: '白色' }] } }
好比某個需求,須要把 info.height
改成 155,同時改變 info.desc
數組的第 0 項的 age
爲 12,第 3 項的 color
爲灰色呢?
// 先取出要改變的對象,改變數字後 setData 回去 const { info } = this.data info.height = 155 info.desc[0].age = 12 info.desc[3].color = '灰色' this.setData({ info }) // 或者像某些文章裏介紹的,這樣可讀性差,也不太實用 this.setData({ 'info.height': 155, 'info.desc[0].age': 12, 'info.desc[3].color': '灰色' })
上面這兩種方法,是咱們日常小程序裏常常用的,和其餘 Web 端的框架相比,就很蹩腳,一種濃濃的半成品感撲面而來,有沒有這樣一個方法:
this.upData({ info: { height: 155, desc: [{ age: 12 }, , , { color: '灰色' }] } })
這個方法會幫咱們深度改變嵌套對象裏對應的屬性值,跳過數組項裏不想改變的,只設置咱們提供了的屬性值、數組項,豈不是省略了一大堆蹩腳的代碼,並且可讀性也極佳呢。
這就是爲何我在上線的項目中使用 wx-updata,而不是 setData
wx-updata 的原理其實很簡單,舉個例子:
this.upData({ info: { height: 155, desc: [{ age: 12 }] } }) // 會被自動轉化爲下面這種格式, // this.setData({ // 'info.height': 155, // 'info.desc[0].age': 12, // })
原來這個轉化工做是要咱們本身手動來作,如今 wx-updata 幫咱們作了,豈不美哉!
在通常狀況下,咱們能夠將方法直接掛載到 Page
構造函數上,這樣就能夠在 Page
實例中像使用 setData
同樣使用 upData
了:
// app.js 中掛載 import { updataInit } from './miniprogram_npm/wx-updata/index' // 你的庫文件路徑 App({ onLaunch() { Page = updataInit(Page, { debug: true }) } }) // 頁面代碼中使用方式 this.upData({ info: { height: 155 }, desc: [{ age: 13 }, '帥哥'], family: [, , [, , , { color: '灰色' }]] })
有的框架可能在 Page
對象上進行了進一步修改,直接替換 Page
的方式可能就不太好了,wx-updata
一樣暴露了工具方法,用戶能夠在頁面代碼中直接使用工具方法進行處理:
// 頁面代碼中 import { objToPath } from './miniprogram_npm/wx-updata/index' // 你的庫文件路徑 Page({ data: { a: { b: 2}, c: [3,4,5]}, // 本身封裝一下 upData(data) { return this.setData(objToPath(data)) }, // 你的方法中或生命週期函數 yourMethod() { this.upData({ a: { b: 7}, c: [8,,9]}) } })
針對修改數組指定項的時候,可能存在的跳過數組空位的狀況,wx-updata 提供了 Empty 的 Symbol 類型替位符,還有數組的對象路徑方式,感興趣能夠看看 wx-updata 的文檔,也能夠參考 <開發微信小程序,我爲何放棄 setData,使用 upData> 這篇介紹文章。
另外,使用了 wx-updata 也還可使用原來的 setData,特別是有時候要清空數組時,靈活使用,能夠得到更好的小程序開發體驗,祝你們小程序開發愉快 🤣
關於蹩腳的 .wxss
樣式,我使用 webstorm 的 file watcher 工具把 scss 文件監聽改動並實時編譯成 .wxss
文件,感受比較好用,這裏給你們分享一下個人配置:
而後記得在 .gitignore
文件中加入要忽略的樣式:
*.scss *.wxss.map
這樣在上傳到 git 的時候,就不會上傳 scss 文件了~ 固然若是你的團隊成員須要 scss 的話,仍是建議 git 上傳的時候也加上 scss 文件。
這樣設置以後,一個組件在本地的會是下面這樣
其中咱們須要關注的就是 .js
、.json
、.scss
、.wxml
文件,另外的文件 .wxss
會在你改動 .scss
文件以後自動生成並更新,而 .wxss.map
是插件自動生成的映射關係,不用管。
若是不是使用 webstorm,能夠直接執行命令 sass --watch index.scss:index.wxss -s expanded
,命令行若是關閉,sass 命令就不會監聽文件的變更而後編譯,因此最好用編輯器的插件。
同理,也可使用 less、stylus 等預編譯語言。
萬能的 VSC 固然也能夠作到這個功能,搜索並下載插件 easy sass
,而後在 setting.json
中修改/增長配置:
"easysass.formats": [ { "format": "expanded", "extension": ".wxss" }, { "format": "compressed", "extension": ".min.wxss" } ]
上面 expanded
是編譯生成的 .wxss
文件,下面 compressed
是壓縮以後的 .wxss
樣式文件,下面這個用不到能夠把下面這個配置去掉,而後在 .gitignore
文件中加入要忽略的中間樣式:
*.scss
固然也能夠不添加,若是你的同事也是實用 scss 來開發小程序的話,其餘跟上面同樣,至此你就能夠在小程序開發中快樂使用 scss 了~
在 Web 開發中 iconfont 可謂是最經常使用的靈活圖標字體工具了,這裏介紹一下如何在微信小程序中引入 iconfont 圖標。
首先找到你想使用的圖標們,點擊購物車以後下載到本地。
下載到本地是一個壓縮包,解壓縮以後將 iconfont.css
文件複製到微信小程序的 styles
文件夾中 (在下的習慣,也能夠放到你想放的地方好比 fonts
),將後綴改成 .wxss
在 app.wxss
中引入樣式:
@import "styles/iconfont.wxss";
而後在 .wxml
中就可使用剛剛你添加的圖標了,Web 使用 i
標籤,小程序中使用 text
標籤:
<text class="iconfont icon-my-edit" style="color: blue"></text>
若是後面要加新的圖標,要下載新的 iconfont.css
的文件到本地重命名並覆蓋,從新走一遍這個流程。
固然,若是你使用的樣式庫提供的一些 icon 能知足你的要求,那更好,就不用引入外部圖標字體文件了,不過大部分狀況下是不知足的 🤣
網上的帖子大多深淺不一,甚至有些先後矛盾,在下的文章都是學習過程當中的總結,若是發現錯誤,歡迎留言指出,若是本文幫助到了你,別忘了點贊支持一下哦(收藏不點贊,都是耍流氓 🤣)~
參考文檔:
PS:本人博客地址 Github - SHERlocked93/blog,也歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~
另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~