本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。
購書連結 https://www.tenlong.com.tw/products/9789864344130
讓我們再次重新認識 JavaScript!
鐵人賽寫到今天也有二十幾篇了,相信一路跟著系列文看到這裡的你,對 JavaScript 這門語言應該也有一定程度的認識了吧? 那麼在系列文的最後幾篇,就按照我最初的計畫,來與各位聊聊「JavaScript 的現在與未來: ES20XX、前端框架與開發生態圈」。
「每 18 至 24 個月,前端都會難一倍」
每次提及前端框架的時候,我總喜歡拿這個當開場白。當然也有人持相反意見,我覺得兩者說法其實是一體兩面,並不衝突。
是變得簡單還是越來越難,我認爲要看對於技術的掌握度以及專案的規模來決定。
如果只能用一句話來解釋,我會說前端框架/工具庫的發展方向,實際上是「讓簡單的專案變得複雜,讓複雜專案的開發變得單純」。
在我們探討前端工具的演進之前,應該要先理解一個問題:「為什麼我們需要這些前端框架/工具庫」?
讓我們把時空背景拉回到十幾年前,那個還沒有 JavaScript 工具庫的年代。
在那個時期,各家瀏覽器對 JavaScript 的各種實作可以說是五花八門,其中最典型的例子就是「事件處理」。
在早期的 IE (也沒有那麼早,指 IE9 以前),事件綁定就分為 W3C DOM 標準的 addEventListener()
以及 IE 獨有的 attachEvent()
。
如果不依賴工具庫,光是要處理事件就要先做判斷:
function addEvent(ev, elem, func) {
if (elem.addEventListener){
// W3C DOM
elem.addEventListener(ev, func, false);
}
else if (elem.attachEvent) {
// IE DOM
elem.attachEvent("on" + ev, func);
}
else {
// No much to do
elem[ev] = func;
}
}
更不用說一個要加 on
,另一個不用,如果同個事件綁定多個處理器,甚至連執行先後順序也不一樣。
而透過 jQuery 處理事件只需要一個 $(...).bind()
或 $(...).on()
就足夠。
除了解決跨瀏覽器的問題之外,另一個痛點就是對於 DOM 的控制。
假設今天想要用 Javascript 讀取網頁中某個單選按鈕 (radio button),在沒有 document.querySelector
可以用的時代,來看看要怎麼做:
var checkedValue = null;
var elements = document.getElementsByTagName('input');
for (var n = 0; n < elements.length; n++){
if (elements[n].type === "radio" &&
elements[n].name == "radioField" &&
elements[n].checked){
checkedValue = elements[n].value;
}
}
像這樣,我們必須先找出所有的 <input>
標籤,然後透過 for
一個一個檢查它的 type
以及 name
,最後才把對應的 value
取出。
要是透過 jQuery 來查找,我們甚至只需要一行:
var checkValue = $('[name="radioField"]:checked').val();
就可以做到一樣的效果。
當然這篇文章並不是要教各位怎麼使用 jQuery,而是想要表達一個觀念:「框架、工具都是因應某個需要被解決的問題而生」。
而在不同的時間點,就會有不同的問題需要被解決。
大致上來說,我會將前端框架/工具庫分做這幾種:
像大家都很熟悉的 jQuery、 YUI (我們懷念它)、或是早期的 PrototypeJS (不是 JavaScript 原生的那個 prototype)、Dojo 等都屬於這一類型。
最近幾年才進入前端領域的朋友,可能很難想像當年 jQuery 提出可以讓開發者直接以 CSS Selector 來操作 DOM 的做法,可以說是潮到出水,簡直是屌翻天。當然 jQuery 會紅到現在其實也不只這點,還有各種其他因素就是。
此類工具主打的就是對 DOM 的操作變得更簡便。 優點就是上手容易,操作直覺,我拿到 DOM 之後就直接對它做事。 早期大約十幾年前的主流工具庫多屬於這種類型。
這類的工具庫通常屬於是對前一個「以操作 DOM 為主」的再擴充。
主要想解決的目標的是把包裝好的各種圖形化介面直接拿來使用。
像 Bootstrap、jQuery UI、Kendo UI 等這種已經幫你把幾個常用的圖形化介面都封裝好,開發者只需要準備好容器的節點,幾乎只要一初始化就可以直接使用。
當然這種工具也是兩面刃,優點是裝了就可以直接用,但是如果要高度客製化、擴充跟改寫,如果函式庫本身沒有提供相關 api 給開發者,那麼改起來恐怕比自己刻還累。
像知名的 mustache.js 、node 的 jade (pug) 等都屬於這種類型。
此類工具想要解決的問題在於將 JavaScript (程式邏輯) 與 HTML (介面) 拆分得更乾淨。
舉例來說,今天我們從後端 api 取得某個 JSON:
{
"header": "Colors",
"items": [
{"name": "red", "first": true, "url": "#Red"},
{"name": "green", "link": true, "url": "#Green"},
{"name": "blue", "link": true, "url": "#Blue"}
],
"empty": false
}
如果我們希望可以渲染出這樣的 HTML:
<h1>Colors</h1>
<li><strong>red</strong></li>
<li><a href="#Green">green</a></li>
<li><a href="#Blue">blue</a></li>
按照傳統做法我們就得自己寫迴圈,去拼裝 HTML 對吧?
就算用 jQuery 也要寫成這樣:
$.get('...', function(data){
var htmlStr = [];
htmlStr.push(['<h1>'+ data.header +'</h1>']);
for( var i = 0; i < data.items.length; i++ ){
if( data.items[i].first ){
htmlStr.push(['<li><strong>'+ data.items[i].name +'</strong></li>']);
}
else{
htmlStr.push(['<li><a href="'data.items[i].url'">'+ data.items[i].name +'</a></li>']);
}
}
$(...).append( htmlStr.join('') );
})
但 Mustache 可以讓你自訂網頁模版:
<h1>{{header}}</h1>
{{#bug}}
{{/bug}}
{{#items}}
{{#first}}
<li><strong>{{name}}</strong></li>
{{/first}}
{{#link}}
<li><a href="{{url}}">{{name}}</a></li>
{{/link}}
{{/items}}
{{#empty}}
<p>The list is empty.</p>
{{/empty}}
我們只要把資料 (JSON) 準備好,然後送進模板,就會生成對應的 HTML。 就整體架構面來說,這樣拆分的結果更方便我們透過 JavaScript 來建構較具規模的應用程式,同時專案的可讀性與可維護性也大大增強。
現在我們把時間軸稍微往後推一些,時間來到了 2010 年前後。 由於過去不少前輩的努力,這個時候的 JavaScript 早就已經不是那個只能拿來寫寫特效啊,跑馬燈,或是簡單驗證的東西了。
此時開發者們發現,當網站的應用程式越來越複雜的時候,即使我們已經把 HTML、CSS 以及 JavaScrpipt 都拆開維護了,在大型系統的開發架構上仍然還是覺得少了些什麼。
於是在這個時期,各種從其他語言借鑒的特性大量被實作,MVC、MVP、MVVM ...等等框架在前端的領域可以說是蓬勃發展。
比較早期像是 Backbone.js,算是我第一個接觸的前端 MVC 框架。 它搭配了 Underscore.js 及 jQuery,開發者可以透過 RESTful interface 來跟它的 Model 及 Collection 結合操作,當資料被改變了之後,就會觸發 View 去更新,作出對應的變化,比較可惜的是當時還沒有實作 data-binding 的部分。
真正集大成可以算是 EmberJS、AngularJS (v1) 以及 Knockout.js 時期,這幾套前端框架各有所長,但最驚豔的部分是 View 和 Model 可以直接來做 binding,這在過去很習慣直接以操作 DOM 為開發手段的我們,其實是個相當大的突破。 除此之外,像是 directive、template 以及 routing 的整合,也為開發者帶來相當大的便利。
然而這類「大而全」的工具在當時的時空背景下,還要顧慮到對舊環境的支援,所帶來的代價就是效能問題。
於是,後來的故事相信大家都知道了,React 的崛起,Angular2 捲土重來,然後是 Vue 的三分天下。
像這種類型的前端工具,都算是目前的主流框架種類。
除了前面介紹的框架/工具,另外也還有其他針對特殊目的所開發的:
單元測試:
像 Jasmine、Mocha、QUnit 等就是專門作為測試使用的工具。
處理視覺化為主 (主要是處理 SVG 或 Canvas):
比較知名的像是 D3.js、p5.js 以及 Highcharts 都是專門用來處理資料圖表使用。
webGL 系則有老牌的 Three.js,最近超火熱的 PIXI.js 也都屬於此類的工具。
純工具庫:
單純用來作為提供 JavaScript 的增強工具庫,像是 Underscore、Lodash 等。
我並不打算在這裡一一比較各個框架,但是我們把鏡頭拉遠從過去的軌跡來看技術的發展,其實不難發現所謂後來的新技術,都是站在前人的肩膀上繼續向前走。 如同我一開始所說,框架、工具都是因應問題而生。 當新的標準、新的技術出現之後,過去曾經的問題被解決了,未來一定也會有新的挑戰等著我們去面對。
扣掉這篇距離鐵人完賽還有三天可以寫,就讓我留點篇幅在後面介紹吧 XD (打開 PS4 外出取材中)