Underscore.js 是一個由 Jeremy Ashkenas 開發的 JavaScript 庫,它提供了許多作為開發者的我們在開發 Web 專案所需要的實用功能。
它使程式碼變得更加易讀:
1 2 |
_.isEmpty({}); // true |
它使程式碼更容易編寫:
1 2 |
_.flatten([[0, 1], [2, 3], [4, 5]]); // [0, 1, 2, 3, 4, 5] |
它提供了原生所沒有的方法:
1 2 |
_.range(5); // [0, 1, 2, 3, 4] |
它自身甚至可以作為模板引擎:
1 2 |
_.template('<p><%= text %></p>', {text: 'SitePoint Rocks!'}); // <p>SitePoint Rocks!</p> |
Underscore 是一個輕量級的庫(minify和gzip壓縮後只有5.7 kb),並且被許多知名專案所用,如:
現在我們開始更具體,更深入的討論它的主要功能。
重要部分
在本教程中,我要強調 Underscored 的三個最常用的方法:
我將分別解釋它們是如何使用,然後用它們一起編寫一個 Demo,Demo 你可以找到在教程最後找到。與以往一樣,這個 Demo 的程式碼會放在 Github 上。
如果你想跟著例子練習,你需要一份 Underscore 庫。你可以從,比如你所喜愛的 CDN 處獲取:
1 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> |
一路走來如果發現自己需要幫助,或者你想要了解更多,不要忘了還有內容更廣泛的 Underscore官方文件。它還有一個龐大而活躍的社群,意味著很容易得到其他人幫助。
_.each
:寫看得懂的迴圈
任何一個專案都會有類似於此片段的程式碼:
1 2 3 4 |
var artists = ['Pharrel Williams', 'Led Zeppelin', 'Rolling Stones']; for(var i = 0; i < artists.length; i++) { console.log('artist: ' + artists[i]); } |
Underscore 能夠使你用更具可讀性的方式編寫像這樣的程式碼,:
1 2 3 4 |
var artists = ['Pharrel Williams', 'Led Zeppelin', 'Rolling Stones']; _.each(artists, function(artist, index, artists) { console.log('artist: ' + artist); }); |
很贊不是嗎?_.each()
接受兩個引數:
- 需要遍歷的 Array(或者Object)
- 回撥函式
對於 Array 中每一個元素(文件中稱為 iteratee
),_.each()都將呼叫回撥函式。在回撥函式中我們可以訪問三個引數:
- 當前迭代的陣列元素(
artist
)的值。例如,對於上面的程式碼段,在第一次迭代我們會得到“Pharrel Williams”這個值 - 當前迭代的序號(索引),在我們的這個例子下,是從0到2的數
- 被迭代的陣列本身(
artists
)
正如你所看到這樣,程式碼更易讀,我們不需要像之前提到的for
迴圈那樣使用artist[i]
就可以輕鬆訪問陣列中的各個元素。
See the Pen _.each by SitePoint (@SitePoint) on CodePen.
_.template: 簡單直白
由於單頁應用的興起,一個可靠的前端模板引擎已經成為工作棧中基本的需要。
Underscore 提供了一個模板引擎,這對於那些熟悉如 PHP 或 Ruby on Rails 語言的人來說可能非常熟悉。
從我們上面的程式碼片段繼續,我們來演示_.template()
是如何工作的。新增如下幾行程式碼
1 2 3 4 5 6 7 8 9 10 11 12 |
var artists = ['Led Zeppelin', 'ACDC', 'Rolling Stones'], artistTemplate = _.template('<li><%= artist %></li>'), content = ''; _.each(artists, function(artist, index, artists) { content += artistTemplate({ artist: artist }); }); var container = document.createElement('ol'); container.innerHTML = content; document.body.appendChild(container); |
在這裡,我們用一個字串引數來呼叫_.template()
函式,這個引數包括用分隔符包含的一些資料(<%= artist%>
)。當以這種方式呼叫_.template()
的時候,會返回一個可複用的函式。
來呼叫我們的新函式artistTemplate()
,傳遞文字作物件為引數,會返回我們最初傳遞到_.template()
的字串,但是其中的自由變數會被引數中相應的值替代。在這個例子下<%=artist%>
會被引數中artist
的值所取代。
Underscore 的模板引擎不僅可以替換值,而且模板內部的指令碼也可以被執行。只需一處修改,這一段程式碼就可以如虎添翼。
1 2 3 4 5 6 |
var artists = ['Led Zeppelin', 'ACDC', 'Rolling Stones'], artistTemplate = _.template( '<% _.each(artists, function(artist, index, artists) { %>' + '<li><%= artist %></li>' + '<% }); %>' ), content = artistTemplate({ artists: artists }); var container = document.createElement('ol'); container.innerHTML = content; document.body.appendChild(container); |
我們已經把對_.each()
的呼叫整合進模板,這使我們改變模板的呼叫方式。既然我們現在在_.template()函式中進行迭代,我們可以把整個的artists
陣列傳入artistTemplate()
(之前傳遞的是單個artist
)。此程式碼輸出結果與前面的例子相同。
當需要_.template()
來eval
其中的 JavaScript 程式碼時,需要用<%%>
來包圍程式碼,而不是<%=%>
。
既然呼叫由_.template
生成的模板和呼叫一個函式效果相同,我們可以用<%%>
標籤把程式碼進一步改良。我們對artists
設計一個外層模板,並且由它來呼叫為每個元素設計的內層模板,這樣子模板就是可複用的。
See the Pen _.template() by SitePoint (@SitePoint) on CodePen.
最後,來看看_.filter()
函式。
_.filter():只需要一個布林函式
_.filter()
接收一個陣列和一個回撥函式作為引數。然後它會對陣列中的每個元素呼叫回撥函式,然後返回一個新陣列,其中包含每個回撥函式執行後返回真值的元素。
回撥函式也像_.each()
一樣接受三個引數:當前被遍歷的元素值,被遍歷值的索引,和陣列本身。
為了說得更明白,我們對程式碼做幾處修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var artists = ['Led Zeppelin', 'ACDC', 'Rolling Stones'], artistTemplate = _.template( '<% _.each(artists, function(artist, index, artists) { %>' + '<li><%= artist %></li>' + '<% }); %>' ), content = artistTemplate({ artists: _.filter(artists, function(artist, index, artists) { return artist === 'ACDC'; }) }); var container = document.createElement('ol'); container.innerHTML = content; document.body.appendChild(container); |
你可能已經猜到了,在模板中只會收到['ACDC']
作為陣列引數。這是我們目前的 demo 。
See the Pen _.filter() by SitePoint (@SitePoint) on CodePen.
說了那麼多。我們來做一些更有意義的東西。
Demo應用
不要忘了這個 Demo 的程式碼可以在Github 上找到。
我們將建立一個小程式,它呼叫其他 API,展示所拉取的資訊,並允許使用者過濾顯示。為此,我們將使用:
具體來說,這個程式將會從Spotify獲取一些藝人資訊,並通過使用Underscore的_.template,_.each和_.filter來展示,使用者可以根據流派來縮小展示範圍。
要做到這一點,我們將程式碼分為三個不同的模組:
_isAwesome.Config
:儲存整個程式中將用到的資訊。
_isAwesome.Template
:負責模板的編譯
_isAwesome
:主模組,負責響應使用者操作並更新介面。
以上都遵循模組模式。
Config模組
該模組包含了要使用的模板的ID,還有用於查詢的 API 地址,還包括我們想從 Spotify 的查詢的藝人ID。通過這種方式,我們可以只需要在陣列中新增元素就可以查詢更多藝人的資訊。
Template模組
該模組通過呼叫Config模組
中的getTemplates()
來編譯模板。
主模組
該模組負責向Config模組
取得的 URL 傳送 Ajax 請求,並且使用我們從Template模組
中取得的模板渲染頁面。 除此之外,這個模組也負責響應使用者操作而過濾相應內容。
兩個過濾器和模板都會嵌入在 HTML 中。
為了實現過濾,我們將依賴HTML 5的data屬性和jQuery的data介面。這是為了方便起見,但你如果想著如何用原生實現的話,瀏覽器對這個介面的支援非常好。
這是用來做過濾按鈕的程式碼:
1 |
<button class="btn btn-default sized" data-filter-field="genres" data-filter-value="album rock" data-action="filter">Album Rock</button> |
這是用來傳給過濾函式的物件的例子:
1 2 3 4 5 |
{ action: 'filter', field: 'genres', value: 'rock' } |
我們把模板放在標籤內,將其作為index.html
的一部分。但是為了防止它被執行,需要將其type屬性
設定為text/javascript
以外的值。統一起見我們設定為underscore/template
。
我們將有兩個模板。第一個是藝人列表的模板,第二個是藝人單獨顯示時的模板。如上面所說,我們將其稱之為嵌入式模板。我們會從一個模板中('item-list')
呼叫另一個模板('item-tpl')
。
然後,在該檔案的底部,我們會引入所需的庫和這三個指令碼。此外,為了使其更具視覺吸引力,我們還在header里加入了一些基本樣式。 And that’s it. 就是這樣。
See the Pen Underscore Awesomeness by SitePoint (@SitePoint) on CodePen.
總結
如我演示,Underscore 用起來十分賞心悅目,你可以寫出乾淨,可讀性強,並且容易維護的程式碼。
還有更多的內容可以加入我們這個 demo 裡(比如用_.pluck()
來動態生成模板),但我覺得作為入門這些已經足夠。
你呢,你是否曾經用過 Underscore ,你是否會願意嘗試,你是否嘗試過其他功能相似的替代品(比如loadsh),評論告訴我吧。