<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [JS] ES Modules的運作原理

      ESM 通過 import 語句引入其它依賴,通過 export 語句導出模塊成員。

      在瀏覽器環境中,<script> 可以通過聲明 type="module" 將一個 JS 文件標記為模塊,帶有 type="module" 聲明的<script> 類似于啟用了 defer,腳本文件的下載不會阻塞HTML渲染,代碼內容會被延后執行。

      這篇文章僅討論瀏覽器環境下的 ESM。

      概括

      ES模塊的加載主要分為三個步驟:

      1. 構建 Construction
        • 找到入口文件;
        • 根據import語句遞歸構建依賴圖;
        • 下載模塊腳本文件,并文件轉換為 Module Record。
      2. 實例化 Instantiation
        • 為模塊導出的成員申請內存空間;
        • 建立importexport之間的鏈接;
      3. 求值 Evaluation
        • 運行模塊代碼;
        • 向內存中的成員填充實際的值。

      模塊加載過程

      步驟1 構建

      構建過程的作用在于:構建依賴圖,以及了解各個模塊之間import/export的成員(靜態)。

      路徑解析與文件下載

      在代碼中我們使用的模塊通常是相對路徑,path resolver負責將相對路徑轉換為文件的絕對路徑,從而可以讓瀏覽器去下載模塊文件。

      image-20240915173728406

      轉換為模塊記錄

      當模塊文件下載到瀏覽器本地之后,瀏覽器會對模塊文件進行靜態解析,從模塊代碼文件總結出一個模塊記錄(Module Record),可以理解為是模塊的元數據。

      一個模塊記錄大致包含了如下信息:

      • 模塊文件的源代碼,以及根據源代碼構建的 AST;

      • 該模塊依賴的其它模塊;

      • 從其它模塊分別導入了哪些成員。

      緩存機制

      在瀏覽器中,一個標簽頁會維護一個模塊緩存映射表,它的 key 是模塊解析后的實際路徑,它的 value 是模塊記錄(Module Record)。

      image-20240915181338503

      當模塊文件的路徑被解析完成之后,它就會被添加到緩存中,而在“完成路徑解析”和“轉換為模塊記錄”這段時間內,它的 value 會被標記為 fetching

      遞歸

      image-20240915181952763

      場景描述:

      1. 用戶訪問 https://www.example.com/index.html,返回的 HTML 文件包含模塊入口腳本文件
      <script src="main.js" type="module"/>
      
      1. 相對路徑main.js被解析為絕對路徑 https://www.example.com/main.js,然后瀏覽器開始下載文件(此時這個模塊路徑已經被記錄到緩存了,標記為 fetching);
      2. 文件下載到瀏覽器本地之后,靜態解析代碼,捕獲import語句(import語句會被默認提升到代碼頂部),解析結果得到模塊記錄(Module Record),模塊記錄會被更新到緩存里;
      3. 模塊記錄包含依賴的其它模塊,此時瀏覽器會遞歸地解析它們的路徑,并下載它們的腳本文件(由上圖紅色箭頭標明)。

      在這個過程中,網絡請求下載腳本文件占據了大部分的時間開銷。

      復雜的依賴關系可能導致初始化構建過程過久,影響首屏時間。

      常用的優化手段是使用動態import,在運行時按需引入指定的模塊。

      動態加載

      語法

      import('./dynamic-module.js').then(module => {
          console.log(module.default);
          console.log(module.xxx);
      });
      
      import(`./module-${moduleName}.js`).then(module => {
         // ... 
      });
      

      import函數的參數是模塊的文件路徑,返回一個 Promise 對象,通過 then 方法可以獲取到模塊對象。

      模塊對象包含模塊導出的成員,默認導出使用default屬性獲取。

      應用場景

      • 模塊懶加載,優化首屏時間;
      • 根據不同邏輯加載不同的模塊,所需的模塊是在運行時才確定的。

      步驟2 實例化

      實例化的主要作用是為模塊的state分配內存空間,此時僅作內存的分配,state的值在這一刻還不確定。

      瀏覽器會以 深度優先后序遍歷 的方式遍歷依賴圖,為每一個模塊 export 的成員分配內存空間。

      當模塊的所有 export 完成內存分配之后,會開始將 import 鏈接到相應的內存地址。

      這意味著 export 導出的成員和 import 引入的成員指向同一處內存空間。基礎數據類型也是如此。

      特點

      • 模塊內部更新 state,外部的state 也隨之變化(因為它們指向同一塊內存);
      • 模塊導出的 state只讀的

      image-20240915225358286

      這種現象和 CommonJS 存在很大區別,CommonJs 在導入模塊成員的時候,是對模塊的導出進行了拷貝

      image-20240915225757132

      這意味著在使用模塊導出的 state 時,要注意其數據是否是最新的,因為模塊內部和外部的 state 是相互獨立的,內部更新 state 并不會影響到外部的 state

      不過這種情況一般比較少發生,我們很少直接導出一個基本數據類型,而是導出一個對象,對象內部再記錄這些基本數據類型。由于導出的是對象,只要模塊內部不要直接覆蓋整個對象,而是對對象的屬性進行更新,就不會有太大問題。

      步驟3 求值

      步驟1和2完成之后,模塊的成員已經完成了內存的分配,以及 import/export 之間的鏈接。

      最后需要完成的,就是運行模塊代碼,并將成員的值填入先前分配的內存中。

      模塊代碼中可能存在一些帶有副作用的代碼,為了避免每一次執行都會導致模塊的 exports 發生變化,模塊代碼只會被執行一次

      循環依賴

      循環依賴是所有模塊化方案都要討論的問題。

      案例

      image-20240916001635606

      實際項目中,依賴圖是很復雜的,導致循環依賴的環可能包含了許多模塊。這里僅討論最簡單的情況,即兩個模塊相互依賴對方。

      CommonJS

      假設main.js是入口文件。

      main.js

      const num = require('./a.js');
      console.log(num);
      exports.message = 'main';
      

      a.js

      const { message } = require('./main.js');
      module.exports = 123;
      setTimeout(()=>console.log(message), 0);
      

      我們期待在main.js中輸出的num為123,而在a.js中輸出的message為 main;而實際運行結果是:

      123
      undefined
      
      image-20240916003859389

      CommonJS 的 require 函數是同步地加載模塊,并且一次性完成,不像ESM分為三個步驟。

      如上圖,當代碼執行到 ① 時,執行require函數,解析路徑、記錄到緩存中、讀取模塊文件、執行模塊代碼(步驟②)。

      由于 CommonJS 的同步特性,它不能直接運行于瀏覽器環境,這里討論的 Node.js 環境下的模塊加載。

      在執行步驟②的過程中,main.js導出的成員還沒有賦值,此時的module.exports是一個空對象。

      但是由于 CommonJS 是在模塊的路徑解析階段就記錄了緩存,因此步驟②的require函數可以得到模塊main.jsmodule.exports,只不過此時的module.exports還是空對象。

      由于它此時還是空對象,因此解構賦值出來的messageundefined

      我們期待等步驟③這些同步代碼執行完成之后,message應該就會更新為main了,于是我們在a.js中,使用setTimeout來將任務推入宏任務隊列中,延后執行。

      但結果是,盡管main.js中的message被賦值了,a.js中的message也不會被更新。這是因為在導入的時候進行了拷貝,所以兩個message是相互獨立的。

      image-20240916005943833

      ESM

      main.js

      import num from './a.mjs';
      
      console.log(num);
      
      export const message = 'main';
      

      a.js

      import { message } from "./main.mjs";
      
      export default 123;
      
      setTimeout(()=>console.log(message), 0);
      

      由于 ESM 的 import/export 是被鏈接到同一塊內存區域的,因此當 main.js 賦值message之后,a.js中的message 也會更新為 main

      輸出結果

      123
      main
      

      在瀏覽器環境下,為了使用 ESM 語法,入口腳本文件需要標明 type="module"

      在 Node.js 環境下,為了表明文件是使用 ES 模塊化語法,需要將文件后綴改為 .mjs,或者在 package.json 中配置 typemodule

      總結

      ES Modules (ESM) 是一種現代模塊化方案,具備以下特點和優勢:

      • 模塊化聲明

        • 使用 importexport 語句實現模塊的引入與導出。
        • 在瀏覽器中通過 <script type="module"> 標簽加載,不阻塞 HTML 渲染。
      • 加載過程

        1. 構建:遞歸構建依賴圖并下載模塊。
        2. 實例化:為導出的成員分配內存空間,建立 importexport 的鏈接。
        3. 求值:運行模塊代碼,填充內存中的成員值。
      • 與 CommonJS 對比

        特性 ESM CommonJS
        加載方式 異步加載,不阻塞渲染 同步加載
        導入成員機制 共享同一內存空間,實時更新 拷貝機制,數據獨立
        瀏覽器支持 原生支持 <script type="module"> 僅支持 Node.js 環境
      • 優勢

        • 原生支持 動態加載
        • 解決 循環依賴 問題,確保模塊成員實時更新。

      引用

      [1] ES modules: A cartoon deep-dive - Mozilla Hacks - the Web developer blog

      posted @ 2024-09-16 01:24  feixianxing  閱讀(734)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 曝光无码有码视频专区| 一区二区三区激情都市| 中文字幕人妻不卡精品| 四虎影视一区二区精品| 精品午夜福利无人区乱码| 蜜臀视频一区二区在线播放| 亚洲国产精品综合久久2007| 正在播放国产真实哭都没用| 日本丰满人妻xxxxxhd| 国内精品自线在拍| 男女啪祼交视频| 国产精品色三级在线观看| 欧美人成精品网站播放| 亚洲欧美日韩综合一区二区| 亚洲国产精品久久无人区| 欧美成人精品三级在线观看| 国产成人一区二区三区免费| 极品尤物被啪到呻吟喷水| 粉嫩国产av一区二区三区| 精品国产福利久久久| 国产成人高清精品亚洲| 丁香婷婷综合激情五月色| 青青青青国产免费线在线观看| 国产高清在线男人的天堂| 夜夜躁狠狠躁日日躁视频| 日本道高清一区二区三区| 国产精品中文字幕免费| 连州市| 精品视频国产狼友视频| 国产成人无码A区在线观| 欧美精品国产综合久久| 中文字幕理伦午夜福利片| 蜜臀精品视频一区二区三区| 又黄又刺激又黄又舒服| 日韩区二区三区中文字幕| 印江| av天堂久久精品影音先锋| 国产区精品福利在线观看精品| 国产精品二区中文字幕| 高颜值午夜福利在线观看| 台山市|