ESM Import與Bundleless

前言

隨著前端模塊化的發(fā)展,在nodejs領(lǐng)域的模塊化主要分為了common.js,umd,es module三大種類(lèi)。其中umd是可以兼容瀏覽器運(yùn)行的,common.js只能在nodejs環(huán)境運(yùn)行,es module是屬于未來(lái)的一種前端模塊化,能夠同時(shí)滿(mǎn)足服務(wù)端和瀏覽器端的代碼編寫(xiě)。es module也是現(xiàn)在前端工程師寫(xiě)代碼最常用的模式。模塊化的寫(xiě)法大家也不會(huì)陌生,也是未來(lái)重要的發(fā)展方向。

寫(xiě)法如下:

import fs from 'fs';

有別于common.js的寫(xiě)法:

const fs = require('fs');
兼容性情況

其實(shí)瀏覽器也支持esm import了,但是兼容性情況不容樂(lè)觀。基于lukeed/dimport的兼容方案,其實(shí)我們?cè)诖蟛糠值臑g覽器都可以大膽使用該屬性。瀏覽器的esm import把我們帶進(jìn)一個(gè)全新的時(shí)代(服務(wù)端模塊和瀏覽器模塊同步的時(shí)代),被稱(chēng)為Bundle Free時(shí)代(或者可以被稱(chēng)為Bundleless)。結(jié)合Bundle Free可以減少不必要的代碼打包,充分利用瀏覽器的每一分性能。

<!-- dimport 兼容方案 -->
<!-- Load the "module" version on browsers that can support it. -->
<script type="module" src="https://unpkg.com/dimport?module" data-main="/bundle.js"></script>
 
<!-- Load the "nomodule" version on older browsers – acts as fallback! -->
<script type="nomodule" src="https://unpkg.com/dimport/nomodule" data-main="/bundle.js"></script>

ESM Import

其實(shí),最早將ESM Import引入到前端頁(yè)面開(kāi)發(fā)的是Polymer/lit-html,它將模版模塊化,直接帶到瀏覽器。然后激發(fā)了preact的調(diào)整,基于Tagged_templates創(chuàng)造出developit/htm語(yǔ)法,這樣一來(lái),JSX的語(yǔ)法直接能在瀏覽器使用,當(dāng)然也部分失去渲染函數(shù)的魅力。用JS字符串模版新特性替代了JSX,卻能讓preact直接能在瀏覽器運(yùn)行,當(dāng)時(shí)掀起了一定的影響。

<!DOCTYPE html>
<html lang="en">
  <title>htm Demo</title>
  <script type="module">
    import { html, Component, render } from 'https://unpkg.com/htm/preact/standalone.module.js';

    class App extends Component {
      addTodo() {
        const { todos = [] } = this.state;
        this.setState({ todos: todos.concat(`Item ${todos.length}`) });
      }
      render({ page }, { todos = [] }) {
        return html`
          <div class="app">
            <${Header} name="ToDo's (${page})" />
            <ul>
              ${todos.map(todo => html`
                <li key="${todo}">${todo}</li>
              `)}
            </ul>
            <button onClick=${() => this.addTodo()}>Add Todo</button>
            <${Footer}>footer content here<//>
          </div>
        `;
      }
    }

    const Header = ({ name }) => html`<h1>${name} List</h1>`

    const Footer = props => html`<footer ...${props} />`

    render(html`<${App} page="All" />`, document.body);
  </script>
</html>

其中瀏覽器端的esm是可以支持相對(duì)路徑和遠(yuǎn)端https路徑的,前提是所有靜態(tài)資源要在服務(wù)當(dāng)中,不能以文件路徑引入,例如file://開(kāi)頭的資源,這也是對(duì)腳本資源的安全考慮。

伴隨著Chrome的占有率的提高,以及移動(dòng)端瀏覽器的統(tǒng)一和成熟,其實(shí)以現(xiàn)代瀏覽器對(duì)esm的支持情況,意味著未來(lái)的前端框架漸漸往瀏覽器原生靠近。前端工程化將我們帶到了打包和編譯的階段,js被壓縮到一個(gè)文件當(dāng)中,變量都是混淆的,但是打包編譯速度和打包的體積永遠(yuǎn)是我們離不開(kāi)的話題。webpack的發(fā)展已經(jīng)進(jìn)入到瓶頸期,項(xiàng)目構(gòu)建的消耗已經(jīng)受限于電腦配置和性能。

ESM Import的到來(lái)意味著JS資源可以直接被瀏覽器應(yīng)用,它也會(huì)越來(lái)越碎,有很多不常變動(dòng)的資源可以外部引入。伴隨著多資源的并行引用,http2.0剛好解決資源并發(fā)性能的問(wèn)題,也正是說(shuō),資源的 ESM Import 和 Bundle Free 的趨勢(shì)是符合現(xiàn)代瀏覽器的發(fā)展規(guī)律的。

Bundleless

業(yè)界上,Bundleless的實(shí)現(xiàn)主要是vitesnowpack。尤雨溪已經(jīng)將vue3的工程化逐漸改變成vite,vite的出現(xiàn)有悖于vue-cli3。vue-cli3更多是webpack的深層封裝。webpack最大的問(wèn)題就是打包性能的問(wèn)題,如果MPA,你可以通過(guò)減少打包入口來(lái)提高打包效率。但是當(dāng)你的項(xiàng)目是一個(gè)異常龐大SPA的情況下,由于你不是多頁(yè)面框架(mpa),所以當(dāng)你的頁(yè)面有一個(gè)文件變動(dòng)的時(shí)候,需要重新構(gòu)建一個(gè)完整bundle,即使你用happypack 或者 thread-loader 壓榨電腦的每一寸性能,都達(dá)不到高效的開(kāi)發(fā)效率。

而vite則是Bundle Free的實(shí)現(xiàn),它的出現(xiàn)主要是有效減少開(kāi)發(fā)編譯時(shí)間,因?yàn)锽undle Free的原理就在于每一個(gè)文件都是獨(dú)立的import。Node 端和瀏覽器端的文件基本是對(duì)等的,每當(dāng)一個(gè)文件有變動(dòng)的時(shí)候,只需要替換其中一個(gè)文件即可。所以它在大型業(yè)務(wù)項(xiàng)目的開(kāi)發(fā)中,有著很不錯(cuò)的體驗(yàn)。

尤大大的Twitter

由于vue不同于渲染函數(shù),很多語(yǔ)法是模版約定的。熟悉vue原理的同學(xué)就知道,它有對(duì)應(yīng)的compiler,將模版轉(zhuǎn)換為createElement的函數(shù),這些函數(shù)才可以在瀏覽器直接運(yùn)行。每一個(gè)獨(dú)立的vue文件,都會(huì)轉(zhuǎn)換成相應(yīng)的js文件,它們?cè)跒g覽器上直接運(yùn)作。vite的基本原理就是在本地調(diào)試的時(shí)候?qū)⑿薷倪^(guò)的文件轉(zhuǎn)換成為js文件,然后再通過(guò)ESM Import引入本地項(xiàng)目當(dāng)中。vite2.0已經(jīng)支持了部分預(yù)打包機(jī)制并且支持react,它可以將部分common.js的npm依賴(lài)封裝成為esm模塊,保證了第三方倉(cāng)庫(kù)的可用性,大家可以放心大膽使用。

當(dāng)然,vite也有妥協(xié),生產(chǎn)模式上vite還是采用了rollup的打包方式,在考慮兼容性的情況下,讓代碼轉(zhuǎn)換為舊瀏覽兼容的模式。唯一的缺點(diǎn)就是增加了開(kāi)發(fā)和生產(chǎn)環(huán)境的差異性。所以資深的前端工程師在使用vite的時(shí)候要注意回歸一下兼容性的問(wèn)題。

同樣,snowpack也是采用了開(kāi)發(fā)模式Bundle Free,而生產(chǎn)模式還是提供bundle為可選擇模式。就此看出Bundle Free并非完全適用于生產(chǎn)環(huán)境。

優(yōu)點(diǎn)

  • SPA開(kāi)發(fā)效率高,每個(gè)頁(yè)面獨(dú)立
  • 與瀏覽器http2的并發(fā)請(qǐng)求契合
  • 與瀏覽器的ESM Import契合
  • 與deno的ESM Import契合
  • 依賴(lài)清晰

缺點(diǎn)

  • 兼容性不好,生產(chǎn)還是考慮打包模式
  • 如果生產(chǎn)使用bundle-free的加載效率問(wèn)題
  • 依賴(lài)需要都滿(mǎn)足esm(antd不滿(mǎn)足,vite2.0已經(jīng)支持了部分預(yù)打包機(jī)制)
  • 開(kāi)發(fā)與生產(chǎn)的不一致性

對(duì)未來(lái)的暢想

來(lái)自《The Third Age of JavaScript》

作為一個(gè)從Jquery走過(guò)來(lái)的前端工程師,你會(huì)明白以前的瀏覽器是弱化前端模塊化,沒(méi)有前端工程化的,所有的資源都是靜態(tài)引入,前端項(xiàng)目的維護(hù)性是很弱的。但是結(jié)構(gòu)是清晰的,學(xué)習(xí)成本非常低。進(jìn)入了第二個(gè)時(shí)代,也就是webpack引領(lǐng)的前端工程化時(shí)代,前端項(xiàng)目就變成了一個(gè)JS文件,各種工具層出不窮,學(xué)習(xí)成本非常高。2020后可能會(huì)進(jìn)入第三個(gè)時(shí)代,這個(gè)時(shí)代是屬于Bundle Free或者Bundleless,部分前端項(xiàng)目可能會(huì)回到直接引用的狀態(tài),部分項(xiàng)目則是混合依賴(lài)的情況,在另一個(gè)程度,給前端性能優(yōu)化帶來(lái)了另一個(gè)可能。

  • 第一階段:Jquery時(shí)代(直接依賴(lài))
  • 第二階段:前端工程化(打包依賴(lài))
  • 第三階段:Bundle Free?(混合依賴(lài))

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容