讀 《HTML5 揭祕》有感

jump_jump發表於2020-04-04

最近在補一些 HTML 的書籍,偶爾讀到這本書,雖然這本書已經是10年以前的書籍了,不過其中有些有趣的知識點與觀點被我提取了出來。

標準建立與技術實現衝突

作者在開始就提出了 Mozilla 開發人員關於標準與實現之間的衝突的一個觀點:

一份技術規範和他的具體實現必須要做到步調一致。實現先於規範完成不是什麼好事情,因為人們會開始依賴這些已實現的細節,這樣會對規範造成制約。然而,你也不希望在規範已經完成時還沒有任何相關的具體實現與實踐經驗,因為這樣規範就得不到任何反饋。這裡面如果存在著無法避免的衝突,而我們也需要硬著頭皮去克服了。

事實上,對於前端開發而言,具體實現早於技術規範的制定已經是一種常態了。但與其他領域不同的是:前端的新標準非必要條件下是不可以破壞之前的實現。

在這裡,我可以舉幾個相容性的例子:

大家可能都使用過 String.prototype.includes 來判斷一個字串是否包含在另一個字串中。但是實際上,在 Firefox 18 - 39中,這個方法的名稱叫 contains()。由於在Firefox 17上,一些使用 MooTools 1.2的網站會崩潰掉。當年由於各個框架為了能夠更簡單的使用函式,在各自的程式碼庫中修改內建物件的 prototype,同時框架也考慮到了未來標準可能會實現,為了相容以後的標準,他們對 prototype 進行判斷,然後如果物件當前 prototype 上沒有函式實現,就使用當前自己定製的函式,如果有函式實現的話,就使用瀏覽器所提供的函式。雖然他們考慮到了相容標準,但是他們卻沒能考慮到標準是會發生改變的。可能在若干年後,標準實現後,函式已經與當前大相徑庭。所使用的程式碼就會發生錯誤。所以,無奈之下,該函式被重新命名為 includes() 。事實上我們可以看出,contains 命名要比 includes 好得多。

在最新的提案之一就是在類中新增私有變數方法,標準將使用 # 符號來表示類的私有變數。

class Count {
  #a = 1;
  getCount() {
    return this.#a  
  }
}
const count1 = new Count()

count1.getCount()
// 1

console.log(count1)
// Uncaught SyntaxError: Private field '#a' must be declared in an enclosing class
複製程式碼

emmm...,美醜大家自行鑑別。也正是因為前端之前沒有所謂的私有變數,所以大家都會“約定” _ 就是私有變數,但是事實上,任何約定都只會防君子不防小人。一定會有大量的程式碼直接進行呼叫。一旦瀏覽器支援後,必然會影響大量網頁。 所以我們也只能硬著頭皮去克服了。

受到影響的不僅僅是 JavaScript,同時也有 Css。 Css 變數為了能夠在 Sass(變數用了 $ ) 和 Less(變數用了 @) 中使用,也是不得不去使用 --。可以看到這樣進行 Css 變數定義也是不那麼美觀的。

:root {
  --main-bg-color: brown;
}
複製程式碼

交付出東西的人才是贏家

該書也討論了為什麼會存在 讀 《HTML5 揭祕》有感 這個元素標籤。為什麼它是 img,而不是 include,image(事實上,貌似 image 元素標籤也是存在在瀏覽器中的,可以看這一篇 blog Having fun with 讀 《HTML5 揭祕》有感)?

答案以 93 年一群大佬的精彩的對話為主線,其中可以看到一些真知灼見,也有一些前瞻性很強的言論。眾口難調是必然的。很多時候,協議的制定本身就不是一個技術問題,很難有對錯而言。但是為什麼一定是 讀 《HTML5 揭祕》有感,答案很簡單,因為提議者馬克·安德里森在對話後直接釋出了程式碼來處理這個元素。

這並不是說給出程式碼實現的就一定是贏家,但是這是贏家的必要條件,不是嗎?討論當然重要,是一種思想的交流,但是當我們不能從道理上說服別人的時候,就只能用其他的方案來表明自己的態度。

過於超前就會死亡

該書也討論了當年 WHAT 小組和 W3C HTML 小組之間對於 HTML 發展的不同思考和見解,開始時候,兩個小組之間各自為政,無視對方存在。WHAT 小組針對 Web 表單和新的特性進行工作,而另一組忙於制定 XHTML 2.0 版本,但可惜的事情是: 沒有瀏覽器為之提供實現。

XHTML 是和 HTML 不相容的,這意味者不但瀏覽器開發者要做大量的工作,還需要讓前端開發者一下子切換到 XML,完全書寫良好的規範 — 這是行不通的。而 WHAT 小組採用寬容的錯誤處理,把重點放在新特性上。

無論從觀念還是網際網路產品,過於超前的結局其實都不是太好。當然,這從側面也印證了一個道理,選擇比努力更重要的不是一句空話。

難以消失的瀏覽器相容技術

最後,我們來聊一下技術吧。

新的功能已經到來,但是我們要等到什麼時候才可以採用它?這個問題不但出現在 10 年前,同時也會在現在。優秀的開發者總是希望使用最新的特性來提升使用者體驗。當然了,我們現在可以依賴 Can I use 來判斷瀏覽器支援情況。

很早之前,我們用 來處理不使用指令碼的網頁。

再然後,我們通過瀏覽器特性檢測來進行相容處理。例如 Modernizr 庫來檢查瀏覽器是否支援 HTML5 以及 css3。利用 polyfill 來升級不支援特性的瀏覽器。

即使在今天,這些問題依然沒有被解決。當然我們利用更加先進的技術來支援罷了。

Polyfill.io 根據不同的瀏覽器確立不同的 polyfill

如果進行過前端開發,就不可能沒有使用過 polyfill。polyfill你可以理解為“膩子”,就是裝修的時候,可以把缺損的地方填充抹平。針對於各個瀏覽器的把差異化抹平。由於各個瀏覽器版本不同,所需要的 polyfill 也不同,

Polyfill.io是一項服務,可通過選擇性地填充瀏覽器所需的內容來減少 Web 開發的煩惱。Polyfill.io讀取每個請求的User-Agent 標頭,並返回適合於請求瀏覽器的polyfill。

如果是最新的瀏覽器且具有 Array.prototype.filter

https://polyfill.io/v3/polyfill.min.js?features=Array.prototype.filter

/* Disable minification (remove `.min` from URL path) for more info */
複製程式碼

但是如果當前瀏覽器沒有此函式,就會在 正文下面新增有關的 polyfill。

國內的阿里巴巴也搭建了一個服務,可以考慮使用,網址為 polyfill.alicdn.com/polyfill.mi…

type='module' 輔助打包與部署 es2015+ 程式碼

使用新的 DOM API,可以有條件地載入 polyfill,因為可以在執行時檢測。但是,使用新的 JavaScript 語法,這會非常棘手,因為任何未知的語法都會導致解析錯誤,然後所有程式碼都不會執行。

該問題的解決方法是

<script type="module">。
複製程式碼

早在 2017 年,我便知道 type=module 可以直接在瀏覽器原生支援模組的功能。具體可以參考 JavaScript modules 模組 以及 ECMAScript modules in browsers。但是當時感覺只是這個功能很強大,並沒有對這個功能產生什麼解讀。但是卻沒有想到可以利用該功能識別你的瀏覽器是否支援 ES2015。

每個支援 type="module" 的瀏覽器都支援你所熟知的大部分 ES2015+ 語法!!!!!

例如

  • async await 函式原生支援
  • 箭頭函式 原生支援
  • Promises Map Set 等語法原生支援

因此,利用該特性,完全可以去做優雅降級。在支援 type=module 提供所屬的 js,而在 不支援的情況下 提供另一個js。具體可以參考 Phillip Walton 精彩的博文,這裡也有翻譯版本 【譯】如何在生產環境中部署ES2015+

如果當前專案已經開始從 webpack 陣營轉到 Vue CLI 陣營的話,那麼恭喜你,上述解決方案已經被內建到 Vue CLI 當中去了。只需要使用如下指令,專案便會產生兩個版本的包。

vue-cli-service build --modern
複製程式碼

具體可以參考 Vue CLI 現代模式

不得不說的是: 目前也有開發者把 ESM module 已經作為主流瀏覽器的功能來思考。還提供了 10 倍打包速度的 Snowpack。雖然目前距離生產環境還有一定差距,不過如果下次需要開發小型個人專案,我會嘗試使用該工具。

鼓勵一下

如果你覺得這篇文章不錯,希望可以給與我一些鼓勵,在我的 github 部落格下幫忙 star 一下。

部落格地址

參考資料

ECMAScript modules in browsers

Vue CLI 現代模式

【譯】如何在生產環境中部署ES2015+

相關文章