向Modern JavaScript 轉型

blank

向Modern JavaScript 轉型

背景

自2011年browserify誕生開始,到我們現在更為廣泛應用的webpack / rollup / parcel等構建工具的普及,構建及工程化已經變成了前端開發者的開發生態中不可或缺的部分。日常開發中,我們已經習慣跟隨ESMAScript或TypeScript的新特性,這些特性往往具有更簡單的語法、更高的執行效率,比較典型的例子就是ES6中class :

classPerson{constructor(name){this.name=name;}greeting(){return`Hi, this is ${this.name}.`;}}classManextendsPerson{}

但如果要用構建函數和原型鏈去實現兩個類的聲明並實現繼承,我相信大多數人會崩潰:

functionPerson(name){this.name=name;}Person.prototype.greeting=function(){return`Hi, this is${this.name}.`;}functionMan(name){Persona.call(this,name);}Man.prototype=Object.assign(Person.prototype);Man.prototype.constructor=Man;

問題在於,這些提效的新特性並不能100%的運行在所有的瀏覽器中,比如上個時代的噩夢- IE8.在這樣的背景之下,就誕生了6to5這樣的編譯器,來幫助我們把開發時的ES6的語法編譯成ES5的語法,從而執行在瀏覽器中,當然6to5也跟隨我們一起進化,變成了現在的babel .

babel也不是萬能膏藥,雖然幫助我們解決了語法的問題,但也帶來了一些副作用,比如編譯後的代碼通常都會插入一些內置的函數或者Pollyfill,這也就造成了代碼體積的驟增,上文中我們class Person的源碼實現總共155B ,使用babel編譯成ES5的代碼後,體積變成了2.7K !

從我們認為的JavaScript 下一代的标准的開始-- ECMAScript 2015到現在已經過去5年多的時間了,ECMAScript每年都會不斷的推進語言標準的更新,而瀏覽器廠商除了參與標準的指定,也在不斷的跟進著新的語言標准在瀏覽器中的原生實現的支持。不知道你有沒有註意到,在2021年的今天, class的瀏覽器支持率已經達到了95% ,所有的主流瀏覽器都已支持class特性:

blank

類似於class這類特性,如果瀏覽器已經支持了,但我們還是把編譯後的體積更大、執行更慢的ES5代碼交給瀏覽器,不管是構建、存儲、傳輸還是執行,無疑都是一種浪費。而這就關係到我們這篇文章的主題: Modern JavaScript .

什麼是Modern JavaScript

Modern JavaScript指的不是哪一個特定版本的ECMAScript標準,而是指的一系列已經被現代瀏覽器所支持的特性的集合。

我們常說的現代瀏覽器包括:Chrome, Edge, Firefox, Safari, 它們佔了瀏覽器市場份額的90% 以上,除此之外,還有一些基於跟上述瀏覽器相同渲染引擎的瀏覽器實現,比如UC, QQ 等也佔了5% 左右的份額。這也就意味著,我們廣泛使用的一些特性已經得到了95% 的支持,主要包括:

  • class
  • 箭頭函數arrow function
  • 構造器generator
  • 塊級作用域let / const
  • 解構destructuring
  • Rest參數rest and spread parameters
  • 對像簡寫object shorthand
  • Async函數async / await

Modern JavaScript 不是一個固定的特性集合,它是動態跟隨我們所定義的現代瀏覽器的的支持度的。就目前而言, ES2017是最接近Modern JavaScript的標準。

Legacy JavaScript

對應Modern JavaScript, 我們編譯完的ES5 的結果就可以稱為Legacy JavaScript, 它是我們向瀏覽器兼容器委屈求全的結果。

在現代瀏覽器中,這種轉換是得不償失:我們通過編譯讓我們的代碼支持度從95% 上升到了98%, 然而這卻給我們帶來了20% 的代碼體積的上升,同時代碼執行效率會變得更低。另外,在node_modules hell的加成下,這個影響可能是指數級的。

如何使用

  1. 瀏覽器

<script type="module" />

現代瀏覽器支持通過<script type="module" />直接在瀏覽器中ES6+的代碼,因此我們可以通過這種方式來為現代瀏覽器加載Modern JavaScript,而小部分的老瀏覽器則由fallback邏輯兜底,加載執行Legacy JavaScript.

<scripttype="module"src="https://cdn/modern.js"/><scriptnomodulesrc="https://cdn/ledacy.js"/>

2. NPM

{ "exports": "./modern.js" }

[email protected]版本中,引入了package.json中的exports字段,來增強原先僅能通過main聲明的包入口的功能。

{"main":"./index.js","exports":{".":"./index.js","./submodule":{"import":"./submodule/index.js","require":"./submodule/index.cjs"}}}

通過上面的入口聲明我們可以實現對外暴露默認入口以及submodule的入口,並且submodule可以根據是require還是import提供不同的實現:

importpkgfrom'pkg';importsubmodulefrom'pkg/submodule';constsubmodule=require('pkg/submodule');

exports除了提供了多入口以及條件入口等強大的功能外,也可以作為提供了Modern JavaScript實現的依據,因為[email protected]已經支持ES2019了,所以在構建時如果檢測到node_modules中的模塊提供了exports entry,可以為它們單獨構建我們的Modern JavaScript Bundle.

3. Bundle

通過上述瀏覽器和NPM 包的對Modern JavaScript 特性的支持,我們已經解決了出口的問題,並且在不支持的場景下也都可以fallback 到Legacy JavaScript 來執行。

接下來只需要資源構建的問題即可,我們需要提供滿足95% 的現代瀏覽器可執行的Modern JavaScript Bundle 以及fallback 剩餘的老瀏覽器的Legacy JavaScript Bundle.

通過babel配合不同的browserslist targets就可以簡單的達成目標。當然也有一些現成的插件可以直接使用,比如optimize-plugin , babel-esm-plugin或者babel-preset-modern-browsers等。

而在構建模式上,我們可以選擇從源碼分別構建出Modern Bundle 和Legacy Bundle:

blank

亦或是先從源碼構建出Modern Bundle, 然後從Modern Bundle 再構建出Legacy Bundle:

blank

圖片來源: web.dev/publish-modern-

Modern JavaScript 其實更像是一種理念,而不是一種技術,通過Modern JavaScript 可以讓開發者、用戶享受到技術進步帶來的福利,更甚者,Modern JavaScript 所帶來的存儲、傳輸、執行上的提升還能為地球節能減排。

擺脫ES5 的禁錮,向Modern JavaScript 轉型!

相關

* Transitioning to modern JavaScript

* Publish, ship, and install modern JavaScript for faster applications

* Serve modern code to modern browsers for faster page loads

* Bringing Modern JavaScript to Libraries

What do you think?

Written by marketer

blank

SEE Conf 2021 如期而至,體驗科技極緻美

blank

Apache ECharts 5 震撼發布:五大模塊,十五項新特性全面升級!