<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          了解 JavaScript 模塊系統(tǒng)基礎(chǔ)知識(shí),搭建自己的庫(kù)

          共 8706字,需瀏覽 18分鐘

           ·

          2020-10-23 17:36

          我想很多“前端工程師”都聽(tīng)過(guò)說(shuō)過(guò) “JavaScript 模塊”,那你們都知道如何處理它,以及它在日常工作中如何發(fā)揮作用嗎?

          JS 模塊系統(tǒng)到底是什么呢

          隨著 JavaScript 開(kāi)發(fā)越來(lái)越廣泛,命名空間和依賴項(xiàng)變得越來(lái)越難以處理,極客們?cè)缫呀?jīng)開(kāi)發(fā)出不同的模塊系統(tǒng)解決方案來(lái)解決該問(wèn)題。

          為什么理解 JS 模塊系統(tǒng)很重要

          我的日常工作是設(shè)計(jì)和項(xiàng)目架構(gòu),并且我很快意識(shí)到跨項(xiàng)目需要許多通用功能。我總是一次又一次地將這些功能復(fù)制粘貼到新項(xiàng)目中。

          問(wèn)題是,每當(dāng)更改一部分代碼時(shí),我都需要在所有項(xiàng)目中手動(dòng)同步這些更改。為了避免所有這些繁瑣的手動(dòng)任務(wù),我決定提取通用功能并從中組成一個(gè) NPM 軟件包。這樣,團(tuán)隊(duì)中的其他人將能夠?qū)⑺鼈冎匦掠米饕蕾図?xiàng),并在每次推出新版本時(shí)都可以對(duì)其進(jìn)行更新。

          這種方法具有一些優(yōu)點(diǎn):

          • 如果核心庫(kù)中有一些更改,則只需在一個(gè)地方進(jìn)行更改,而無(wú)需為同一件事重構(gòu)所有應(yīng)用程序的代碼。
          • 所有應(yīng)用程序保持同步。無(wú)論何時(shí)進(jìn)行更改,所有應(yīng)用程序僅需要運(yùn)行 npm update  命令。

          庫(kù)的源碼

          因此,下一步是發(fā)布庫(kù)。

          這是最困難的部分,因?yàn)槲夷X海中突然跳出一堆東西,例如:

          1. 如何使用搖樹(shù)優(yōu)化
          2. 應(yīng)該針對(duì)哪些 JS 模塊系統(tǒng)(CommonJS、AMD、ES modules)
          3. 需要轉(zhuǎn)譯源碼嗎
          4. 需要打包源碼嗎
          5. 應(yīng)該發(fā)布哪些文件

          在發(fā)布第三方庫(kù)(組件庫(kù),工具庫(kù))時(shí),我們每個(gè)人的腦海中都應(yīng)該冒出這些問(wèn)題。

          來(lái), 我們一步步解決以上的問(wèn)題。

          不同類(lèi)型的 JS 模塊系統(tǒng)

          1. CommonJS

          • 由 Node.js 實(shí)現(xiàn)
          • 多用在服務(wù)器端安裝模塊時(shí)
          • 沒(méi)有 runtime/async 模塊
          • 通過(guò) require  導(dǎo)入模塊
          • 通過(guò) module.exports  導(dǎo)出模塊
          • 無(wú)法使用搖樹(shù)優(yōu)化,因?yàn)楫?dāng)你導(dǎo)入時(shí)會(huì)得到一個(gè)模塊時(shí),得到的是一個(gè)對(duì)象,所以屬性查找在運(yùn)行時(shí)進(jìn)行,無(wú)法靜態(tài)分析
          • 會(huì)得到一個(gè)對(duì)象的副本,因此模塊本身不會(huì)實(shí)時(shí)更改
          • 循環(huán)依賴的不能優(yōu)雅處理
          • 語(yǔ)法簡(jiǎn)單

          2. AMD 異步模塊定義

          • 由  RequireJs  實(shí)現(xiàn)
          • 當(dāng)你在客戶端(瀏覽器)環(huán)境中,異步加載模塊時(shí)使用
          • 通過(guò) require  實(shí)現(xiàn)導(dǎo)入
          • 語(yǔ)法復(fù)雜

          3. UMD 通用模塊定義

          • CommonJs + AMD  的組合(即 CommonJs 的語(yǔ)法 + AMD 的異步加載)
          • 可以用于 AMD/CommonJs 環(huán)境。
          • UMD 還支持全局變量定義,因此,UMD 模塊能夠在客戶端和服務(wù)器上工作。

          4. ES modules

          • 用于服務(wù)器/客戶端
          • 支持模塊的 Runtime/static loading
          • 當(dāng)你導(dǎo)入時(shí),獲得是實(shí)際對(duì)象
          • 通過(guò) import  導(dǎo)入,通過(guò) export  導(dǎo)出
          • 靜態(tài)分析——你可以決定編譯時(shí)的導(dǎo)入和導(dǎo)出(靜態(tài)),你只需要看源碼,不需要執(zhí)行它
          • 由于 ES6 支持靜態(tài)分析,因此搖樹(shù)優(yōu)化是可行的
          • 始終獲取實(shí)際值,以便實(shí)時(shí)更改模塊本身
          • 比 CommonJS 有更好的循環(huán)依賴管理

          現(xiàn)在,我們了解了不同類(lèi)型的 JS 模塊系統(tǒng)以及它們?nèi)绾窝葑儭?/p>

          盡管所有工具和現(xiàn)代瀏覽器都支持 ES modules,但我們?cè)诎l(fā)布庫(kù)時(shí)不知道用戶如何利用我們的庫(kù)。因此,我們必須確保我們的庫(kù)在所有環(huán)境中都能正常工作。

          讓我們深入研究并設(shè)計(jì)一個(gè)示例庫(kù),更好地回答與發(fā)布庫(kù)有關(guān)的所有問(wèn)題。

          我已經(jīng)建立了一個(gè)小型的 UI 庫(kù)(你可以在 GitHub 上找到源代碼),并且我將分享我在編譯,打包和發(fā)布中的所有經(jīng)驗(yàn)和探索。

          目錄結(jié)構(gòu)

          在這里,我們有一個(gè)小的 UI 庫(kù),其中包含 3 個(gè)組件:Button,Card 和 NavBar。讓我們一步步進(jìn)行編譯并發(fā)布。

          發(fā)布前的最佳實(shí)踐

          1. 搖樹(shù)優(yōu)化(Tree Shaking)

          webpack 官方文檔有說(shuō)明

          • 搖樹(shù)優(yōu)化是一個(gè)術(shù)語(yǔ),通常用于描述移除 JavaScript 上下文中的未引用代碼(dead-code)。它依賴于 ES2015 模塊系統(tǒng)中的靜態(tài)結(jié)構(gòu)特性,例如 [import][4]  和 [export][5]。這個(gè)術(shù)語(yǔ)和概念實(shí)際上是興起于 ES2015 模塊打包工具 rollup。新的 webpack 4 正式版本,擴(kuò)展了這個(gè)檢測(cè)能力,通過(guò) package.json  的 "sideEffects"  屬性作為標(biāo)記,向 compiler 提供提示,表明項(xiàng)目中的哪些文件是純的 ES2015 模塊,由此可以安全地刪除文件中未使用的部分。
          • webpack 和 Rollup 都支持搖樹(shù)優(yōu)化,這意味著我們需要牢記某些事情,以便我們的代碼可被 Tree Shaking。

          2. 發(fā)布所有模塊形態(tài)

          • 我們應(yīng)該發(fā)布所有模塊形態(tài),例如 UMD 和 ES Module,因?yàn)槲覀冇肋h(yuǎn)不知道用戶在哪個(gè)版本的瀏覽器或 webpack 中使用此庫(kù)/包。
          • 即使所有打包程序(如  webpack  和  Rollup)都能解析 ES Module ,但如果我們的使用者使用的是 webpack 1.x,則它無(wú)法解析 ES 模塊。
          // package.json
          {
            "name""js-module-system",
            "version""0.0.1",


          • package.json 文件的 main 字段通常用于指向 UMD 版本的庫(kù)/包。
          • package.jso 文件的 module 字段用于指向 ES 版本的庫(kù)/包。

          鮮為人知的事實(shí):webpack 使用 resolve.mainfields 確定檢查 package.json  中的哪些字段。

          性能提示:由于所有現(xiàn)代瀏覽器現(xiàn)在都支持 ES 模塊,因此也請(qǐng)務(wù)必發(fā)布  ES 版本的庫(kù)/包。這樣一來(lái),可以減少編譯次數(shù),最終可以減少向用戶交付的代碼。這將提高應(yīng)用程序的性能。

          那么,下一步是什么?編譯還是打包?我們應(yīng)該使用什么工具?啊,這是最棘手的部分!讓我們深入研究研究。

          webpack vs Rollup vs Babel

          這些我們?cè)谌粘9ぷ髦惺褂玫墓ぞ?,用于承載我們的應(yīng)用程序/庫(kù)/軟件包。沒(méi)有它們,我無(wú)法想象現(xiàn)代的 Web 開(kāi)發(fā)有多么糟糕。因此,我們無(wú)法將它們進(jìn)行比較 ?

          每種工具都有其自身的優(yōu)勢(shì),并根據(jù)使用者的需求達(dá)到不同的目的。

          現(xiàn)在讓我們看一下這些工具:

          webpack

          webpack 是一個(gè)很棒的模塊打包工具, 它被廣泛接受并且主要用于構(gòu)建 SPA。它提供了開(kāi)箱即用的所有功能,例如代碼拆分按需加載、搖樹(shù)優(yōu)化等,并且它本身使用的是 CommonJS 模塊系統(tǒng)。

          RollupJS

          RollupJS 還是類(lèi)似于 webpack 的模塊打包器。但是,RollupJS 的主要優(yōu)點(diǎn)是它遵循 ES6 修訂版中包含的代碼模塊的新標(biāo)準(zhǔn)化格式,因此你可以使用它來(lái)打包  ES module variant 的 library/package,但它不支持按需加載。

          Babel

          Babel 是 JavaScript 的編譯器,以將 ES6 代碼轉(zhuǎn)換為可在你的瀏覽器(或服務(wù)器)中運(yùn)行的代碼而聞名。請(qǐng)記住,它只是編譯而不會(huì)打包你的代碼。

          我的建議:對(duì)庫(kù)使用 Rollup.js,對(duì)應(yīng)用程序使用 webpack。

          編譯(Babel-ify)源代碼還是直接打包源代碼

          在構(gòu)建我的 NPM 庫(kù)時(shí),我花費(fèi)了大量時(shí)間來(lái)試圖找出該問(wèn)題(如何編譯、如何打包)的答案。我開(kāi)始挖掘自己的 node_modules,查找所有優(yōu)秀的庫(kù)并檢查它們的構(gòu)建系統(tǒng)。

          對(duì)比 libraries/packages 構(gòu)建的輸出

          在查看了不同 libraries/packages 的構(gòu)建輸出之后,我清楚地了解了這些庫(kù)的作者在發(fā)布之前可能會(huì)想到的不同策略。以下是我的觀察。

          如你在上圖中所看到的,我已根據(jù)它們的特性將這些庫(kù)/軟件包分為兩組:

          • UI Libraries-UI 庫(kù)(styled-components, material-ui
          • Core Packages-核心包(react,react-dom

          你可能已經(jīng)弄清楚了這兩組之間的區(qū)別。

          UI Libraries

          • 有一個(gè) dist 文件夾,該文件夾是針對(duì) ES 和 UMD/CJS 模塊系統(tǒng) 的打包和壓縮版本。
          • 有一個(gè) lib 文件夾,用來(lái)存放被編譯后的代碼。

          Core Packages

          • 只有一個(gè)文件夾,其中包含針對(duì) CJS 或 UMD 模塊系統(tǒng)的打包和壓縮版本。

          但是,為什么 UI Libraries 和 Core Packages 的構(gòu)建輸出有所不同?

          UI Libraries

          想象一下,如果我們只是發(fā)布庫(kù)的 bundled version 將其托管在 CDN 上,我們的用戶將直接在  標(biāo)記中使用它?,F(xiàn)在,如果使用者只想使用  組件,則他們必須加載整個(gè)庫(kù)。另外,在瀏覽器中,沒(méi)有可以解決 tree shaking 的打包工具,最終我們會(huì)將整個(gè)庫(kù)代碼發(fā)送給我們的使用者。因此,我們不能像如下代碼引入整個(gè)庫(kù)文件。

          <script type="module">
            import { Button } from "https://unpkg.com/uilibrary/index.js";
          script>

          現(xiàn)在,如果我們只是簡(jiǎn)單地將 src 轉(zhuǎn)換為 lib 并將該 lib 托管在 CDN 上,那么我們的使用者實(shí)際上可以得到他們想要的任何東西而沒(méi)有任何開(kāi)銷(xiāo),“代碼更少,加載更快” ?

          <script type="module">
            import { Button } from "https://unpkg.com/uilibrary/lib/button.js";
          script>

          Core Packages

          Core Packages(核心包)永遠(yuǎn)不會(huì)通過(guò)  標(biāo)記使用,因?yàn)樗鼈儽仨毷侵鲬?yīng)用程序的一部分。因此,我們可以安全地發(fā)布這些軟件包的構(gòu)建版本( UMDES),并將構(gòu)建后的系統(tǒng)交給用戶使用。

          例如,他們可以使用 UMD 而不使用搖樹(shù)優(yōu)化,或者如果打包器能夠識(shí)別并獲得搖樹(shù)優(yōu)化的好處,則可以使用 ES。

          // CJS require
          const Button = require("uilibrary/button");
          // ES import
          import {Button} from "uilibrary";

          對(duì)于 UI 庫(kù)

          • 當(dāng)我們針對(duì)  es  模塊系統(tǒng)構(gòu)建時(shí),需要  Babel  編譯源代碼,并將編譯后的代碼放置在  lib  文件夾中。我們甚至可以將 lib 托管在 CDN 上。
          • 當(dāng)我們針對(duì)  cjs/umd  模塊系統(tǒng)和  es  模塊系統(tǒng) 等多個(gè)模塊系統(tǒng)構(gòu)建時(shí),需要 rollup  ? 打包和壓縮代碼。

          下面我們修改 package.json  以指向?qū)?yīng)的模塊系統(tǒng)。

          // package.json
          {
            "name""js-module-system",
            "version""0.0.1",
            // for umd/cjs builds
            "main""dist/index.js",
            // for es build
            "module""dist/index.es.js"
          }

          對(duì)于 core packages,我們不需要 lib 版本。我們只需要針對(duì) cjs/umd 模塊系統(tǒng)和 es 模塊系統(tǒng),使用 rollup 進(jìn)行 ? 打包和壓縮源代碼即可。

          提示:對(duì)于愿意通過(guò)  標(biāo)記下載整個(gè)庫(kù)/軟件包的用戶,我們也可以在 CDN 上托管 dist 文件夾

          我們?cè)趺催M(jìn)行打包

          我們應(yīng)在在 package.json  中為了不同的目的編寫(xiě)不同的腳本。你能在 GitHub 上面找到 Rollup 的一些配置—— rollup config。

          // package.json
          {
            "scripts": {
              "clean""rimraf dist",
              "build""run-s clean && run-p build:es build:cjs build:lib:es",
              "build:es""NODE_ENV=es rollup -c",
              "build:cjs""NODE_ENV=cjs rollup -c",
              "build:lib:es""BABEL_ENV=es babel src -d lib"
            }
          }

          我們應(yīng)該發(fā)布哪些東西

          • License
          • README
          • Changelog
          • Metadata( "main"  , "module"  , "bin"  )— package.json
          • Control through package.json  "files"  property

          package.json  中,  "files"  字段是一個(gè)數(shù)組類(lèi)型 ,用來(lái)表示軟件包被當(dāng)做第三方依賴安裝時(shí),都有哪些文件或文件夾需要下載到業(yè)務(wù)項(xiàng)目中。如果你在數(shù)組中加入了一個(gè)文件夾,那么在你 npm install  時(shí),文件夾及下面的文件都會(huì)被下載。

          在我的示例項(xiàng)目中,我在 "files"  中加入了 libdist 文件夾。

          // package.json
          {
            "files": ["dist""lib"]
          }

          最后,終于可以準(zhǔn)備發(fā)布了。只需在終端中鍵入 npm run build  命令,你就看到以下輸出。仔細(xì)查看 distlib 文件夾都有哪些東西。

          可以發(fā)布了?

          總結(jié)

          至此,我們已經(jīng)了解了 JavaScript 模塊系統(tǒng)以及如何創(chuàng)建自己的庫(kù)并發(fā)布它。下面是一些注意事項(xiàng):

          1. 是否可以啟用搖樹(shù)優(yōu)化

          2. 至少需要構(gòu)建 ES modules  and CommonJS  兩種模塊系統(tǒng)

          3. 使用 Babel 和 Bundlers 搭建 libraries

          4. 使用 Bundlers 搭建 Core packages

          5. 在 package.json  中使用 "module"  字段 來(lái)構(gòu)建 es 模塊的版本(PS:這有助于使用 tree shaking)

          6. 發(fā)布已編譯的文件夾以及模塊的編譯版



          原文鏈接:https://www.freecodecamp.org/news/anatomy-of-js-module-systems-and-building-libraries-fadcd8dbd0e/

          作者:Kamlesh Chandnani

          譯者:古月

          校對(duì)者:水歌


          非營(yíng)利組織 freeCodeCamp.org 自 2014 年成立以來(lái),以“幫助人們免費(fèi)學(xué)習(xí)編程”為使命,創(chuàng)建了大量免費(fèi)的編程教程,包括交互式課程、視頻課程、文章等。我們正在幫助全球數(shù)百萬(wàn)人學(xué)習(xí)編程,希望讓世界上每個(gè)人都有機(jī)會(huì)獲得免費(fèi)的優(yōu)質(zhì)的編程教育資源,成為開(kāi)發(fā)者或者運(yùn)用編程去解決問(wèn)題。

          你也想成為

          freeCodeCamp 社區(qū)的貢獻(xiàn)者嗎

          歡迎點(diǎn)擊以下文章了解

          ??
          招募丨freeCodeCamp 翻譯計(jì)劃
          成為 freeCodeCamp 專欄作者,與世界各地的開(kāi)發(fā)者分享技術(shù)知識(shí)
          瀏覽 54
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费A在线看 | 韩国无码毛片 | 黄片视频链接 | 天堂网黄| 国产色情在线观看 |