在 2016 年學 JavaScript 是一種什麼樣的體驗?

方應杭發表於2016-10-07

嘿,我最近接到一個 Web 專案,不過老實說,我這兩年沒怎麼接觸 Web 程式設計,聽說 Web 技術已經發生了一些變化。聽說你是這裡對新技術最瞭解的 Web 開發工程師?

準確地說,我是一名「前端工程師」。不過你算是找對人了。我對今年的技術別提多熟了,前端視覺化、音樂播放器、能踢足球的無人機,你儘管問吧。我剛去 JS 大會和 React 大會逛了一圈,沒有什麼新技術是我不知道的。

厲害。是這樣的,我要開發一個網頁,用來展示使用者的最新動態。我想我應該通過後端介面獲取資料,然後用一個 table 來展示資料,使用者可以對資料進行排序。如果伺服器上的資料變化了,我還需要更新這個 table 。我的思路是用 jQuery 來做。

可別用 jQuery !現在哪還有人用 jQuery 。現在是 2016 年了,你絕對應該用 React 。

哦,好吧, React 是什麼?

React 是一個非常厲害的庫, Facebook 的牛人寫的。它能讓頁面更可控,效能極高,而且使用起來很簡單。

聽起來確實不錯。我能用 React 展示伺服器傳來的資料嗎?

當然可以,你只需要新增兩個依賴,一個是 React ,一個是 React DOM

額,等下,為什麼是兩個庫?

React 是我說的庫, React DOM 是用來操作 DOM 的。因為這些 DOM 是用 JSX 寫的,所以需要一個專門的庫來操作。

JSX ? JSX 是什麼?

JSX 是對 JS 的擴充套件,它看起來跟 XML 差不多,可以用來寫 HTML ,你可以認為 JSX 是一種更優雅的 HTML 寫法。

為什麼不用 HTML 了……?

現在可是 2016 年啊,沒有直接寫 HTML 的。

對哦。好吧,加了這兩個依賴,是不是就可以開始用 React 了?

不行哦。你需要新增 Babel ,然後才能用 React 。

Babel 是另一個庫?

嗯, Babel 是一個轉譯工具, Babel 能把你寫的 JS 轉譯成任意版本的 JS 。你不一定非要用 Babel ,但是如果你不用的話,你就只能寫 ES5 的語法了。你知道的,現在是 2016 年,你怎麼能不使用 ES2016+ 的語法呢? ES2016+ 多麼酷啊。

ES5 是啥? ES2016+ 又是啥?我有點暈。

ES5 就是 ECMAScript 5 。大部分人都會使用 ES5 ,因為大部分瀏覽器都支援 ES5 。

ECMAScript 是啥……

你曉得的, JS 是 1995 年誕生的,而 JS 的標準是 1999 制定出來的。那時候 JavaScript 還叫做 Livescript ,只能執行在網景的瀏覽器裡。那時真是混亂的年代,現在好了,我們有了 JS 的 7 個版本的規範。

7 個版本?那 ES5 和 ES2016+ 是?

分別是第 5 個版本和第 7 個版本。

誒,那第六個版本呢?

你說的是 ES6 。每個版本都是上一個版本的超集,所以你直接使用最新的 ES2016+ 就好了。

對哦。為什麼不用 ES6 呢?

好吧,你可以用 ES6 ,但是你就用不到 async 和 await 這麼酷的語法了。用 ES2016+ 比較好。用 ES6 的話你就只能用 generator 來控制非同步任務流了。

不知道你在說什麼……你說了太多我聽不懂的名詞了。我只是想從伺服器取點資料,我以前用 jQuery 挺好的,從 CDN 引入 jQuery ,我就能用 AJAX 獲取資料了,現在不能這樣做嗎?

大哥,都 2016 年了,沒人用 jQuery 好嗎。所有人都知道用 jQuery 只會造出「義大利麵條」一樣的程式碼(不可維護)

好吧,所以我現在要載入三個庫才能獲取並展示資料。

對的,其實你可以用「模組管理器」把這三個庫「打包」成一個檔案。

哦,什麼是模組管理器……

不同平臺的模組管理器不同啦。前端的模組管理器一般指管理 AMD 或者 CommonJS 模組的東西。

好……吧,什麼是 AMD 和 CommonJS ?

是兩個定義。我們有很多方式來描述 JS 中多個庫或類的互動方式,比如 exports 和 requires 。你可以按照 AMD 或者 CommonJS 的 API 來書寫 JS ,然後用 Browserify 將它們打包。

聽起來很有道理。不過,什麼是 Browserify ?

是一個工具,用來將 CommonJS 形式的 JS 檔案打包起來,放到瀏覽器裡執行。用 npm 倉庫的人發明了 CommonJS 。

npm 倉庫是什麼……

是一個公開的倉庫,用於放置可依賴的模組。

就像一個 CDN 麼?

不太一樣。它更像是一個資料庫,每個人都能在上面釋出程式碼,也能下載上面的程式碼。你可以在開發的時候將這些程式碼下載到本地來使用,必要的時候也能上傳到 CDN 。

聽起來像是 Bower !

是的,不過現在是 2016 年了,沒有人用 Bower 了……

好吧,我知道了,所以我應該用 npm 來安裝依賴。

對的。我舉個例子吧,如果你要使用 React ,你直接用 npm 安裝 React ,然後在程式碼裡匯入 React 就可以了。大部分 JS 庫都能這麼安裝。

嗯, Angular 也可以。

Angular 是 2015 年的事情了。不過今年 Angular 還沒死,還有 VueJS 和 RxJS 等等,你想學一學麼?

還是用 React 吧。我剛才已經學了夠多東西了。所以我用 npm 安裝 React 然後用 Browerify 來打包就好了?

是的。

這麼做看起來有點過於複雜啊。

確實。這就是為什麼你應該使用 Grunt 、 Gulp 或者 Broccoli 這樣的任務管理工具,它們能自動執行 Browserify 。不對,你現在可以用 Mimosa 。

你在說什麼……

任務管理工具。不過我們現在已經不用了。去年我們還在用,後來改成了 Makefiles ,但是現在我們用的都是 Webpack 。

我以為只有 C/C++ 專案才會用 Makefiles 。

是的,不過顯然我們做 Web 開發的,喜歡先把事情搞複雜,然後迴歸到最樸素的狀態。每年我們都是這麼搞的。你就看著吧,過不了兩年,我們就可以在網頁上寫彙編了。

唉,你剛才說的 Webpack 是什麼?

另一種模組管理工具,同時也是一個任務管理工具。你可以認為它是 Browserify 的加強版。

哦,好吧,為什麼 Webpack 是加強版?

額,可能並沒有加強吧。 Webpack 告訴你應該如何管理你的依賴, Webpack 允許你使用不同的模組管理器,不只是 CommonJS ,甚至支援 ES6 模組。

這都是哪跟哪啊,我都被繞暈了。

大家都被繞暈了,不過等 SystemJS 出來了就好了。

天吶,又一個 JS 庫,這是什麼鬼?

呵呵,不像 Browserify 和 Webpack 1.x , SystemJS 是一個動態的模組載入器。

等下,剛才不是說應該把所有依賴打包成一個檔案嗎?

話是這麼說,但是等 HTTP/2 普及之後,不打包反而更好。

那為什麼我們不直接在頁面裡新增 React 的三個依賴檔案呢?

不行。你可以從 CDN 載入這些檔案,但是你還是要在本地用 Babel 轉譯。

唉,這麼鹺?

是的,你不能在生產環境上執行 babel ,你應該在釋出到生產環境之前,執行一系列的任務,包括壓縮、混淆、內聯化 CSS 、延遲載入 script ……

我懂了我懂了。既然我不能直接用 CDN ,那麼我應該怎麼做?

我會考慮用 Webpack + SystemJS + Babel 來轉譯 Typescript 。

Typescript ?我們不是在說 JavaScript 嗎?!

Typescript 也是 JavaScript 呀,它比 JS 更好用,是 JS 的超集,它是基於 ES6 的,就是我們剛才談論的 ES6 ,你還記得吧。

ES2016+ 已經是 ES6 的超集了,怎麼又冒出來一個 Typescript ?

是這樣的, Typescript 能讓我們寫出「強型別」的 JS ,從而減少執行時的錯誤。 2016 年,我們應該讓 JS 支援強型別了。

顯然 Typescript 可以做到。

Flow 也可以做到,區別是 Typescript 需要編譯,而 Flow 只是檢查語法。

唉, Flow 是?

是一個靜態型別檢查器,就是 Facebook 的人寫的。使用 OCaml 寫的,函數語言程式設計很叼的。

OCaml ?函數語言程式設計?

如今大牛都用這些東西,都 2016 年了,你懂的,函數語言程式設計、高階函式、柯里化、純函式這些概念。

不知道你在說什麼。

一開始大家都不知道。這麼說吧,你只需要知道函數語言程式設計比物件導向程式設計厲害, 2016 年我們就指著函數語言程式設計了。

等下,我大學裡學過物件導向程式設計,當時我覺得它還不錯。

Java 在被 Oracle 買下來之前也挺不錯啊。我的意思是,物件導向以前是不錯,現在依然有人用它,但是現在所有人都發覺狀態變換是很難維護的,所以大家都開始用「不可變物件」和函數語言程式設計了。 Haskell 的人已經用這套東西用了很久了,不過幸運的是 Web 開發領域裡有 Ramda 這樣的庫,讓我們用 JS 就可以進行函數語言程式設計了。

你剛剛是不是又丟擲了幾個名詞? Ramnda 又是什麼?

不是 Ramnda ,是 Ramda ,跟 Lambda 表示式有點像。是 David Chambers 寫的庫。

誰?

David Chambers ,大神一個。 blablabla

我不得不打斷你一下了。這些東西看起來都不錯,但是我覺得它們都太複雜,而且沒必要。我只是想獲取資料然後展示,我很確定這種情況下我不需要掌握這些知識。

回到 React 吧,用 React 我怎麼從伺服器獲取資料?

額, React 沒有提供這個功能,你只能用 React 展示資料。

服了啊。那我怎麼獲取資料?

你用 Fetch API 就可以了。

啥玩意?這個 API 的名字很爛啊。

我也覺得是啊。 Fetch API 是瀏覽器提供的非同步請求介面。

哦,那不就是 AJAX 。

AJAX 只是使用 XMLHttpRequest 物件,但是 Fetch API 可以讓你用 Promise 風格來發起非同步請求,幫你擺脫「回撥地獄」。

回撥地獄?

是的,每次你發起一個非同步請求,就得等待它響應。這時你就得在函式裡使用一個函式,這種巢狀呼叫就是回撥地獄。

好吧。 Promise 解決了這個問題麼?

是的。用 Promise 來管理回撥,你就可以寫出更易讀的程式碼,更容易測試的程式碼。甚至可以同時發起多個請求,然後等待它們全部返回。

Fetch 也能做到嗎?

是的。但前提是你的使用者使用了新版的瀏覽器,不然的話你就需要加一個 Fetch 的 「 polyfill 」,或者使用 Request 、 Bluebird 或者 Axios 這些庫。

天吶我到底需要多少個庫?

這是 JS ,同一件事情有上千個庫在做。我們瞭解庫,而且我們有最好的庫,我們有海量的庫,要什麼有什麼。

你剛才說的幾個庫都是幹什麼的?

這幾個庫操作 XMLHttpRequest 然後返回 Promise 物件。

好像 jQuery 的 ajax 方法做的是同樣的事吧……

從 2016 年起我們就不用 jQuery 了。用 Fetch ,大不了加個 Polyfill ,要不然用 Bluebird 、 Request 或者 Axios 都行。然後用 await 和 async 管理 Promise ,這樣才能控制好非同步任務。

這是你第三次說 await 了,那是什麼東西?

await 能讓你攔住一個非同步呼叫,讓你更好地控制非同步返回的資料,大大增強了程式碼的可讀性。 await 非常好用,你只需要在 Babel 裡新增 stage-3 配置,或者新增 syntax-async-functions 和 transform-async-to-generator 外掛就可以了。

聽起來像是瘋了。

沒瘋。為了使用 await ,把 Typescript 編譯之後再用 Babel 轉譯一道的人才是瘋了。

啥玩意? Typescript 不支援 await ?

下個版本就支援了。

我已經無話可說了。

你看其實很簡單。用 Typescript 寫程式碼,用 Fetch 發起非同步請求,所有程式碼編譯成 ES6 ,然後用上 Babel 的 stage-3 配置項,把 ES6 轉譯成 ES5 。所有程式碼用 SystemJS 載入。如果你用不了 Fetch ,就加個 polyfill ,或者用 Bluebird 、 Request 或者 Axios ,這樣你就可以用 await 來處理 Promise 了。

看了我們對於「簡單」的理解是不同的。好吧,有了這些,我終於可以獲取資料然後用 React 展示資料了,對吧?

你的網頁需要處理狀態變更嗎?

唔,不用吧。我只是想展示資料。

那就好,不然我就得跟你解釋 Flux ,以及 Flux 的一些實現,比如 Flummox 、 Alt 、 Fluxible 。不過說真的你應該用 Redux 。

你說的這些我就當耳旁風了。再說一次,我只想展示資料。

這樣啊,如果你只是想展示資料,其實你不需要 React 。你只需要一個模板引擎。

你逗我呢?

我只是告訴你你可以用什麼技術。

別說了,真的。

我想說,即使只是用一個模板引擎,我還是會用 Typescript + SystemJS + Babel 的。

我只是想在頁面上展示資料,你就告訴我用哪個模板引擎就好了。

有很多,你用過哪一個?

額,太久沒用了,不記得了。

jTemplates 、 jQote 還是 Pure

額,不記得,還有別的麼?

Transparency? JSRender? MarkupJS? KnockoutJS? 這一個支援雙向繫結。

還有嗎?

PlatesJS? jQuery-tmpl? Handlebars? 還有些人在用。

有點像。有哪些跟最後一個比較像的?

Mustache, underscore? 我記得連 Lodash 都有一個模板引擎,不過這是 2014 年的事情了。

額,也許是再新一點的庫?

Jade? DustJS?

沒用過

DotJS? EJS?

沒用過。

Nunjucks? ECT?

沒用過。記不起來了,要是你的話,你用哪個?

我應該會用 ES6 原生的模板字串

我猜猜,只有 ES6 支援。

對的。

需要用 Babel

對的。

需要用 npm 安裝

對的。

需要用 Browserify 或者 Webpack ,或者 SystemJS

對的。

如果沒用 Webpack 的話,我還需要一個任務管理工具。

對的。

但是由於我要用函數語言程式設計和強型別語言,所以我首先要用上 Typescript 或者 Flow 。

對的。

如果我要用 await ,那我就必須用 Babel 轉譯。

對的。

然後我就能用上 Fetch 、 Promise 和各種炫酷的東西。

嗯,別忘了加上 Fetch 的 Polyfill ,因為 Safari 不支援 Fetch 。

你猜怎麼著,我們就聊到這吧。我不做了,我不做 Web 了,我也不想再碰 JS 了。

沒事,過不了幾年,我們都會用 Elm 或者 WebAssembly 了。

我要回後端去了,我受不這些變動、版本更新、編譯和轉譯了, JS 社群如果覺得有人能跟上它的腳步,那這個社群就是瘋了。

我理解你。我建議你去 Python 社群。

為什麼?

聽說過 Python 3 嗎?

相關文章