寫給還沒開始閱讀本文的讀者,本文是對《2016 年裡做前端是怎樣一種體驗》的回覆。和其他人的回覆不同,這篇文章包含了一款app 的完整程式碼,這款 app 與之前問到的那款類似。
問:
嗨,我拿到了一個新的web專案,但是老實說,我已經有幾年沒怎麼敲過web程式碼了,而且我讀了些文章,發現這幾年web開發好像光景大變。你是走在最前面的web開發人員,對吧?
答:
我覺得可以這麼說。
問:
很酷啊。我需要建立一個能夠反映使用者最新活動的頁面,所以我僅需從REST終端獲取資料,然後在某種過濾表中進行展示,並且當伺服器發生變化時及時更新資料即可。我在想是不是可以用jQuery來獲取和展示資料呢?我知道有更新的框架,但是這些框架我越瞭解反而越困惑。
答:
jQuery難道不是幾年前導致你不做web開發的原因嗎?
問:
嗯,我覺得自己沒做對,搞不清楚為什麼自己的app總是狀態詭異,也許你能幫我更好地梳理組織jQuery程式碼,這樣就不會總是麻煩重重了。
答:
誰都會遇到這種事,為了應對變化多端的事件,用jQuery時會改變DOM結構,有的時候事件的處理順序與我們所想的大不相同,所以對於如何進入一種特定的狀態,你絕對會感到大惑不解。
問:
你不會想說服我,讓我重返web開發之路吧。
答:
等一等,聽我說完。有了現代web框架,你的程式碼僅需反映資料狀態是如何對映到web網頁的,這就一下子沒那麼難懂了。
問:
好的讓我來考慮一下……難道不是每次資料一發生變化就重繪一次網頁嗎?我猜這樣也能講得通,我的使用者都呈現在桌面上,所以這沒什麼大不了的,但是聽上去這樣會導致移動瀏覽器執行速度極慢。
答:
並不需每次都重繪網頁,現代框架非常智慧,它能夠理清DOM哪一部分發生了變化,然後只處理這一部分。
問:
這挺有意思的。那我應該選用哪一種框架呢?使用的重頭是React, Angular and Ember,對吧?
答:
它們都很好用,如果你想在Handlebars寫前端邏輯,用Ember;如果你想用HTML屬性寫前端邏輯,用Angular或Vue;如果你想用Javascript寫前端邏輯,用React,Mithril or Inferno。
問:
我猜一般會用Javascript,但是難道React不用其他的嗎……像JSX?
答:
JSX僅是Javascript的一種語法擴充套件,它可以讓你使用HTML標籤,從而免於因為寫程式碼而生成DOM元素。
問:
只用JavaScript開發有什麼問題呢?
答:
其實沒什麼大不了的問題,實際上Mithril的檔案都是Javascript,我也才發現給一直做HTML/CSS的人提供JSX程式碼時,獲得的反饋要比給他們純Javascript程式碼時要好得多。
問:
純Javascript?我很高興我並不是唯一一個對JSX沒有完全適應的人。你說的都讓我想試一下Mithril了,Mithril很流行嗎?
答:
它太流行了,不會突然消逝,但是和更大的框架相比,它的流行程度還相差甚遠。我最近實際上正在用Ember寫一個非常霸氣帶感的web app。但是考慮到Ember隱藏了一些特定的、我希望你在加速開發的過程中能夠直接看見的東西,所以我會很高興向你展示如何使用Mithril來執行app。
問:
太好了!幾小時後我們建立的時候,你能給我展示一下如何建立所有的庫、scaffolding和boilerplate程式碼嗎?現在哪種模組打包工具更好用呢,webpack還是browserify?我不得不承認,安裝過程是現代web開發當中最讓我有壓力的。
答:
目前這些你都可以全部跳過,一旦你對現代web開發的主體有了一定的認識和感覺,你僅僅複製一下我做的就可以了,除了babel和rollup之外也沒什麼了。搭建系統真的只是設計一個現代web app工程中很小的一部分。
問:
全部跳過?但我想讓我的web app實際正常執行。
答:
你可以讓它正常執行,我向你展示一下。我們現在用Mithril寫你的app程式碼,你說它是一張過濾表,對吧?我們來把planets.html做成一張planets的過濾表。
1 2 3 4 5 6 7 8 9 |
<!DOCTYPE html> <html> <body> <div id="app">Loading...</div> <script src="https://unpkg.com/mithril/mithril.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/jsx" src="planets.jsx"></script> </body> </html> |
問:
好吧,你來告訴我Mithril 是什麼。另一個庫?Babel是什麼?
答:
Babel讓你使用一些瀏覽器不支援的現代Javascript語法,你不是非用它不可,但它能讓你不用去管瀏覽器不支援什麼,只管敲程式碼。
問:
哦等等,實際上我讀到過這些內容,在瀏覽器中執行轉譯器不是不好嗎?
答:
是不好,轉譯器會增加顯著的延遲,但是為了學習,用轉譯器有什麼不行的呢?過後是很容易再改的。現在我們通過建立app的狀態,開始寫planets.jsx檔案。
問:
要告訴你的是,我20分鐘後要去開會,從我所讀到的來看,你一開始說“狀態”,那麼事情就要變複雜了。也許我們下次可以應該探討Redux、Flux等。
答:
你寫app用到它們的時候,我們可以聊聊。對於這個app,你只需要兩個變數:planets陣列和一個過濾函式。
1 2 3 4 5 6 |
'use strict'; /** @jsx m */ let planets; let planetFilter = planet => true; |
問:
等等,變數不是應該設為var,並且不能設為let嗎?
答:
它們是一樣的,除非let像C或Java中的變數一樣有塊級作用域,沒有什麼hoisting。
問:
這樣確實已經有一段時間了,我都已經忘了hoisting了。
答:
你可以繼續進行,也不用管它,給可能需要再指定的變數定義為let,給其餘的定義為const就行了。
問:
你說第二個是過濾函式,箭頭是否只是老式函式宣告的一種簡寫呢?
答:
是的,箭頭函式和老式帶bind(this)函式的語法幾乎一樣。
問:
哦是的,我記得你曾經過新增bind(this)幫我通查詢並修復過漏洞,我想我會喜歡這些箭頭函式的。
答:
我打賭你會的,現在我們寫一下你app的頂層元件。
1 2 3 4 5 6 7 8 9 10 |
class PlanetApp { view() { return ( <section> <PlanetFilters /> <PlanetTable planets={planets} /> </section> ); } } |
問:
那個一定是新的ES6類語法,我喜歡它的外觀形式,但我不確定HTML和Javascript混在一起會怎麼樣。
答:
不要將JSX看作混雜進Javascript的HTML,它和hyperscript,也就是建立HTML元素的Javascript是等價的。有很重要的一點要理解:它所編譯的Javascript不會生成字串;它產生的是元素的實際結構,比如說如果你的標籤是不平衡的,就不會進行編譯。
問:
好吧,我需要點時間來看看我是否會喜歡它。接下來,你能給我展示一下PlanetTable元件嗎?
答:
當然,這個的確是你app的核心。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class PlanetTable { view() { return ( <table> <tr> <th>Name</th> <th>Composition</th> <th>Moons</th> </tr> {planets.filter(planetFilter).map(planet => <PlanetRow planet={planet} />)} </table> ); } } |
多數情況下它的內容只是靜態的,但你寫的這一行簡潔地描述了你app要乾的事,要用到planets的一個陣列,進行過濾,僅顯示應該顯示的,而且被過濾的陣列會對映到HTML表中的行上。
問:
哦,我想我現在搞懂了!JSX語法只是Javascript的一種表現形式,所以我可以隨心所欲地操控它,我猜PlanetRow 元件會變得非常簡單,對嗎?
答:
是的,多虧了解構賦值,它可能會比你想象的更加簡單。
1 2 3 4 5 6 7 8 9 10 11 12 |
class PlanetRow { view(vnode) { const { composition, name, moons } = vnode.attrs.planet; return ( <tr> <td>{name}</td> <td>{composition}</td> <td>{moons}</td> </tr> ); } } |
問:
OK,所以我猜你就是用vnode.attrs.planet來獲取傳入的planet屬性的,只需寫一行,帶個等號,就行了,所以一定……哇,解構賦值,這麼長時間你都跑哪兒去了?
答:
我給你說,Javascript要比過去變得有意思得多啦。我在這給你展示一下,甚至當你僅考慮簡潔性這一點時,箭頭函式都非常好用。
問:
好的,我知道你講的情況了,它們都是過濾函式,但我打賭所牽扯的事件處理器不可能那麼簡潔。
答:
就那麼簡潔,就是有點抽象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class PlanetFilter { view(vnode) { const { key, func } = vnode.attrs; return ( <label> <input type="radio" name="filter" onchange={filterHandler(func)} /> {key} </label> ); } } function filterHandler(filterFunction) { return function(event) { if (event.target.checked) { planetFilter = filterFunction; } }; } |
答:
但你需要去開會,又想看它的效果。既然你提到,你需要從伺服器中獲取資料,那我來把一些資料扔到一個單獨的planets.json檔案中去。而且現在我們只要通過程式碼就能獲取資料,然後把它們存到方便app獲取的地方,進而積累組成頂級元件。瞧,能用了。
1 2 3 4 |
m.request({url: 'planets.json'}).then(data => { planets = data; m.mount(document.getElementById('app'), PlanetApp); }); |
問:
真的嗎,這就完了?哇,去年的時候還感覺難得讓人望而卻步!我得趕緊跑著撤了,但我確實真的對重拾Javascript充滿期待,太感謝啦!
答:
當然,任何時候都歡迎找我探討!
衷心感謝Biagio Azzarelli, Ben Chauvette, Garrick Cheung and Patrik Johnson對這篇文章的草稿給予的反饋意見。