微信小程序的官方開發(fā)工具中,已經(jīng)集成了 babel 插件對 ES6 語法進(jìn)行轉(zhuǎn)換,各種第三方工具自然更少不了了。
所以可以放心的嘗試使用 ES6,體驗新標(biāo)準(zhǔn)帶來的各種便利之處,省下時間后學(xué)習(xí)充電,或者早點下班、鍛煉身體、下廚做個菜,調(diào)節(jié)生活又放松身心,豈不美哉?
那么,在小程序開發(fā)的過程中,有哪些 ES6 特性是可以給我們帶來便利,提高開發(fā)效率的呢?這邊就結(jié)合實例,一一來說一說吧。
做前端開發(fā)的,開始階段基本會遇到 this 與 閉包 帶來的坑————一些異步操作中,回調(diào)函數(shù)中丟失了當(dāng)前函數(shù)的上下文對象,導(dǎo)致異步操作完成后,更新原有上下文失敗。
為了避免這個問題,以前大家都是自己用變量保存一個閉包外部上下文的引用,取的名字可能千奇百怪:
that/_this/$this/self…在異步操作完成后的回調(diào)中,通過調(diào)取這個閉包外層的變量,達(dá)到更新回調(diào)前函數(shù)上下文對象的目的。
ES6 中增加了 箭頭表達(dá)式,效果和匿名函數(shù)相似,但箭頭表達(dá)式更為簡練,且內(nèi)部執(zhí)行時的 this 與外側(cè)一致,不再需要每次都額外增加變量引用了。
微信小程序里,對每個頁面編寫的代碼邏輯,都作為生命周期鉤子函數(shù)(如:onLoad, onShow, onUnload)和自定義函數(shù)(如:各類組件回調(diào)函數(shù))寫在 AppService 內(nèi)。
這兩種函數(shù)內(nèi),this 都指向當(dāng)前 Page 對象,在這些函數(shù)里做的各種異步操作,回調(diào)內(nèi)的 this 基本都應(yīng)該仍然保持為當(dāng)前 Page 對象。在這個情況下,使用箭頭表達(dá)式可以減少重復(fù)的工作、也減少遺漏 this 時出錯的幾率。
// ES5 var net = require('../public/net'); Page({ data: { list: [] }, onShow: function () { var self = this; net.get('/Index/getList', function (res) { res = res || {}; var status = res.status, data = res.data, list = data.list; if(status == 2) { self.setData({list: list}); } }); } }); // ES6 import * as net from '../public/net'; Page({ data: { list: [] }, onShow: function () { net.get('/Index/getList', (res = {}) => { let {status, data: {list}} = res; if (status == 2) { // 此處 this 的指向與 onShow 內(nèi)一致,無需增加 self 變量 this.setData({list}); } }); } });
雖然都說微信小程序 wxml 的 Mustache 語法與 Vue.js 很相似。但據(jù)說是為了分離 UI 線程和 AppService 線程,微信小程序暫時并不支持 {{value | filter}} 的寫法。
這時候可以借助于 ES5 中為數(shù)組對象增加的方法,之前因為瀏覽器兼容性問題,不一定全部能用。如今在移動端了,就盡情用起來吧:
輸出數(shù)據(jù)前,對后臺傳來的列表數(shù)據(jù)做一些預(yù)處理后再顯示時,通常使用 Array.prototype.forEach 和 Array.prototype.map 進(jìn)行相應(yīng)處理;
篩選掉無效數(shù)據(jù),可以使用 Array.prototype.filter。
// js var helpers = { // 判斷是否有時間參數(shù) hasTime: (i) => { return !isNaN(parseInt(i.stamp)); }, // 時間轉(zhuǎn)換處理 parseTime: (i) => { i.time = new Date(i.stamp + '000'); return i; } }; net.get('/Index/getList', (res = {}) => { let {status, data: {list}} = res; this.set({ list: list.filter(helpers.hasTime) // 篩選掉沒有時間戳字段的數(shù)據(jù) .map(helpers.parseTime) // 將時間戳字段轉(zhuǎn)化為 JS 的 Date 對象 }); });
直到寫這篇文章的時候,小程序官方的組件標(biāo)準(zhǔn)也仍然沒有出來。
目前的通常處理方案,一般是通過 template 配合解構(gòu)賦值不同對象的數(shù)據(jù),實現(xiàn)組件各自狀態(tài)、事件處理函數(shù)互相獨立的效果。
如,有兩個 template 都從 data 中綁定數(shù)據(jù)。
< template name="banner"> < view class="banner-wrap"> < view wx:for="{{data}}" class="banner-item"> < !--...--> < /view> < /view> < /template> < template name="comic-list"> < view class="comic-list"> < view wx:for="{{data}}" class="comic-item"> < !--...--> < /view> < /view> < /template>
AppService 中對于這兩個模板創(chuàng)建兩個不同對象,即可管理自身狀態(tài),不用擔(dān)心字段名重復(fù)的問題。
Page({ onLoad: function () { // 加載 Banner 數(shù)據(jù)并顯示 this.loadData('/bannerState/get', (data) => { this.setData({ bannerState: data }); }); // 加載 ComicList 數(shù)據(jù)并顯示 this.loadData('/comicListState/get', (data) => { this.setData({ comicListState: data }); }); }, loadData: function (url, callback) { var data = []; /* 從 url 加載數(shù)據(jù)的邏輯 */ setTimeout(() => { callback({ data: data }); }, 100); } });
頁面內(nèi)渲染模板時,對 bannerState 和 comicListState 字段進(jìn)行解構(gòu)即可。
< template is="banner" data="{{...bannerState}}"/> < template is="comicList" data="{{...comicListState}}"/>
這是個非常重要且實用的特性,基于這個基礎(chǔ)可以封裝出有具有通用邏輯的基類,實現(xiàn)模塊內(nèi)部的邏輯閉環(huán),達(dá)到組件化開發(fā)的效果。
setData() 中的數(shù)據(jù)字段名與變量名一致時,不需要重復(fù)寫兩遍,上面加載數(shù)據(jù)的代碼就可以這樣簡寫:
this.loadData('/bannerState/get', (bannerState) => { this.setData({ bannerState }); });
數(shù)據(jù)字段較多時,效率會快很多。減少了整理和重構(gòu)代碼需要調(diào)整的地方,降低維護(hù)成本。
// 傳統(tǒng)對象字面量 this.setData({ data1: data1, data2: data2, data3: data3, data4: data4, data5: data5 }); // 增強的對象字面量 this.setData({data1, data2, data3, data4, data5});
增強的對象字面量寫法,還包括函數(shù)的簡寫:
// 傳統(tǒng)的對象字面量 var comicState = { onTap: function (e) { // ... }, onScroll: function (e) { // ... } }; // 增強的對象字面量 var comicState = { onTap(e) { // ... }, onScroll(e) { // ... } };
這種簡潔的成員函數(shù)寫法,是不是很像 class 中的函數(shù)聲明?
class ComicState { onTap (e) { // ... } onScroll (e) { // ... } }
使用 ES5 的 prototype 寫法,實現(xiàn)簡單的類繼承也沒太大問題,但涉及到父類函數(shù)調(diào)用等情況,代碼耦合度會變得更高,需要一定經(jīng)驗才能寫出方便維護(hù)的代碼。
通過 ES6 語法來實現(xiàn)類繼承的話,有了統(tǒng)一的標(biāo)準(zhǔn),寫出的類繼承更加直觀,更方便調(diào)整。
class Base { constructor (options, otherArg) { // Do something. } } class ChildType extends Base { constructor (options) { super(options, ChildType); // Do something. } }
使用 for 對數(shù)據(jù)做迭代遍歷時,語句中聲明的 var 型變量名作用域其實提升到了函數(shù)頂部,不同迭代間忘記處理的話,可能會導(dǎo)致數(shù)據(jù)污染。
改為使用 ES6 的 let/const 可避免這一情況,放心使用塊級作用域。
for (let k in data1) { // ... } // ... let k = 0; // ... for (let k in data2) { // ... }
微信小程序使用的 babel 啟用的轉(zhuǎn)碼規(guī)則可能不是最新的,截止目前版本,測試使用以下 ES6 會有問題,需要注意。
// 以下代碼在 babel 的 repl 中能正常處理,在小程序開發(fā)工具內(nèi)會報錯 class TestClass { static MODE = { NORMAL: 1, DISABLED: -1 } } // 輸出:1 console.log(TestClass.MODE.NORMAL);
20170329 更新:新版本開發(fā)工具似乎已經(jīng)完善了這個問題,可以使用下面的 ES6 寫法了:
function Test() { this.a = 1; this.b = 2; } Test.prototype = { c: 3 }; var o = new Test(); // ES5 for (var k in o) { if (k.hasOwnProperty(k)) { console.log(o[k]); } } // 輸出:1 2 // ES6 for (let k of Object.keys(o)) { console.log(o[k]); } // 輸出:1 2
for...of 用于數(shù)組遍歷時,效果與 Array.prototype.forEach 類似,區(qū)別是可以在途中 break 中斷循環(huán),無需每次遍歷整個數(shù)組。
// Array.prototype.forEach comicList.forEach((c) => { // ... }); // for...of for (let c of comicList) { // ... }
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)