Web平臺能從Node.js學到什麼

Websites發表於2015-12-28

Will Binns-Smith是一位熱愛JavaScript的全棧工程師,喜歡通過技術來解決實際問題。他開發了Bonobos.com的前端購物車功能。Will喜歡與設計師一對一工作,將PC網站轉換為針對更小的觸控裝置的站點。近日,Will撰寫了一篇文章,談到了Node.js有哪些做法和特性值得Web平臺學習。

作為一名Web開發者,我們會非常感激jQuery之類的庫,因為他們消除了底層平臺的各種不一致與笨拙的情況。曾幾何時,構建一個XMLHTTPRequest物件需要好幾行程式碼,但現在只需一行$.ajax呼叫即可;通過jQuery與DOM互動很少需要我們針對特定的平臺採取一些非常規手段,因為這一切jQuery都幫我們做好了。

使用過Python或是Java語言的開發者都知道這些語言自帶了標準庫。在瀏覽器端,jQuery就是這樣的標準庫,此外還有諸如underscore之類的工具集。就像大多數標準庫一樣,我們所編寫的程式碼都會嚴重依賴於他們。當越來越多的程式碼開始依賴這些APIs時,我們就很難在不破壞既有Web的情況下向前邁進了。不要妄圖為了相容性而包含進這些庫的多個版本,他們常常會有30KB的大小,而且預設情況下會向全域性的window物件寫入。

過去,我認為這是軟體開發不可避免的一個問題。但現在一切都變了,至少對於我來說是這樣的,因為Node.js生態圈出現了。

小模組與組合的出現

Node是一個構建在Chrome V8引擎之上的JavaScript執行時,它幾乎沒有多少標準庫。相反,其他生態圈中的那些標準庫都不在其核心平臺中,而是通過npm獲取的。

在npm中,小模組已經成為了標準,像是substack和Sindre Sorhus這樣的使用者分別已經發布了685和760個模組,這些模組都遵循著UNIX一次只做一件事,並將這件事做好的原則。像是array-union(返回兩個陣列的並集)與svg-create-element(提供了用於在DOM中建立SVG的優秀API)這樣的模組都是非常小的,看起來應該與語言或是平臺一同釋出。

Sindre甚至還有一個名為negative-zero的模組,它只是判斷一個值是否是-0,實現只有一行程式碼。看起來為這樣簡單的功能建立一個包有些極端,因為使用者可以在自己的程式碼中實現這樣的功能,不過通過這種方式,我們可以集中修改程式碼,而不必重複實現細節。Sindre對此有個很詳盡的介紹,感興趣的讀者可以看看。

甚至連非常流行的Express Web框架也只提供了Web應用的核心功能而已。與諸如Ruby on Rails或是Django這樣的大型框架不同(本身帶有模板、ORMs、csrf防護,以及其他特性),Express本身只提供了託管靜態檔案的中介軟體。相反,應用開發者可以自由使用他們喜歡的這些特性的實現,並將其組合起來建立應用。隨著想法的不斷改進,很多中介軟體包建立又消亡,一些發展起來了,另一些則消失了。這就是Node的哲學。

因此,小模組(以及由這些模組所構成的應用)會擁有非常龐大的依賴圖(比如說,Bitbucket的前端包含了1000多個JavaScript模組,其中一些是內部模組,另外一些則來自於npm)。

這些模組最棒的一點就是他們並沒有緊密繫結到平臺上:他們不會受到標準庫的影響,藉助於語義版本化,他們可以對其API進行迭代而不會對依賴他們的使用者造成困擾。

流介紹

Node包含了流,它是非同步流動資料的一個抽象,常常用於連線和轉換I/O源。Node對流的初始實現(隨Node 0.4釋出)並不完善,使用不當會導致資料丟失。為了解決這一問題,Node 0.10對流進行了修正(也叫做Streams2)。不過,Streams2並不是對流模式的一個簡單迭代,實際上在Node 0.10釋出前經歷了很多的變化。在釋出時,它與執行在Node 0.8上的應用相容。

這怎麼可能呢?Streams2源自於readable-stream模組,一開始它就是Isaac Schlueter在2012年7月釋出的獨立模組,這距離2013年3月它隨著Node 0.10的釋出已經相隔很長時間了。它經歷了多次迭代,在這個過程中API與功能也不斷成熟,Node社群發現它非常適合於作為流的實現。

時至今日,readable-stream的最新實現也是作為使用者模組在npm中維護的,可用於Node 0.8及之前的版本中。很多使用者都喜歡使用使用者模組而不是繫結的模組,這樣可以實現生態圈的相容性。

一系列不幸的APIs

與此相反,JavaScript與Web平臺中現有的APIs都是很難改變的。對JavaScript語言的迭代變更(不能有任何的向後不相容變化以防止現有系統出現問題)必須要通過新增新特性來實現。比如說,在發現Mutation Events API的效能問題後,人們引入了Mutation Observers來解決這個問題;廢棄WebSQL,擁抱更加底層但使用起來卻略顯笨重的IndexedDB;逐步淘汰Application Cache,擁抱更加底層但更通用的ServiceWorker。Object.observe是ES2016的一個提案,通過它可以觀測物件的屬性,不過在React的單向資料流逐步流行起來併為主流所採用後,Object.observe提案則被撤回了。

對這些感到困惑麼?我們不應該期待著一下子就將事情做對,不過我們需要一個平臺,通過這個平臺可以試錯,然後逐步向好的方向迭代。

平臺演化

可擴充套件的Web宣言的支持者們希望Web能夠像Node那樣提供彈性的使用者試錯空間。其使命是讓平臺提供儘可能多的底層構建塊,這樣瀏覽器之外的庫就可以自由嘗試,從而避免正式的標準化流程所經歷的代價高昂且冗長的過程。其中一種這樣的底層原語就是Web Components的APIs,它向開發者提供了通過JavaScript來建立動態自定義元素與屬性的能力,這一切都是通過封裝來實現的。一個庫無論大小都實現了對話方塊功能,不過API的迭代可以交給使用者來完成,一開始無需放在平臺內部實現。藉助於底層原語,使用者可以通過小模組的形式自由探索更高層次的抽象。換句話說,我們不再需要AppCache了。

幸好,我們已經看到了這種做法的好處。我們無需再陷入諸如AppCache這種實際使用很少的特性了。相反,我們有了一個Promises A+ Specification的標準化實現,npm中的Q已經證明了這一點,並且在年初已經成為了ES2015的一部分。WHATWG也在制訂流的規範,在很大程度上它受到了來源於Node的流的演化的影響。

就像平臺的其他方面一樣,這些新標準是很難改變的,不過幸運的是,其想法與APIs都是通過使用者來實現的,這一切都要歸功於Node!

相關文章