您好,登錄后才能下訂單哦!
本篇內容介紹了“如何動態引入DynamicImport”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
本文介紹的動態引入實現方式基于 rollup 插件 @rollup/plugin-dynamic-import-vars
通常情況下,我們都是通過確定的字面量路徑來引用文件模塊的,例如:
import './a.js'; require('./a.js'); import('./a.js');
對于確定的文件路徑來說,構建工具可以輕易的抓取文件并進行相關的轉換。
但當import或者require的目標不是一個靜態字符串,而是一個動態表達式時,構建工具其實也不確定用戶到底引用了什么,所以通常這種情況只能依靠 JavaScript 的運行時來解析。
若動態表達式實際代表的路徑無法被解析,則運行時會引起控制臺的錯誤。通常是因為生成的文件路徑并沒有被納入打包體系,所以找不到文件。
下面列出了一些常見的動態引入表達式:
// TemplateLiteral 模板字符串 import(`./icons/arrow-${type}.svg`); require(`./icons/arrow-${type}.svg`); // BinaryExpression 二元表達式 import('./icon/arrow-' + type + '.svg'); // 直接引用一個變量 import(path); require(path)
但經過前人們的實踐發現,當動態表達式滿足一定的結構時,構建工具便可以通過一些特殊手段抓取并打包路徑匹配的相關文件,并自動注入一些 polyfill,從而實現動態引入(DynamicImport)的效果,也就是本文的主題。
本節內容翻譯加工自 @rollup/plugin-dynamic-import-vars README.md 部分章節
當動態導入的路徑中包含變量時,經過 AST 分析可以生成對應的通配符。在構建的時候,這些通配符將被用于抓取匹配的文件。隨后這些文件會被添加進構建體系中,在運行時,根據導入的實際路徑返回對應的文件內容。
下面是一些通配符的轉換示例:
`./locales/${locale}.js` -> './locales/*.js' `./${folder}/${name}.js` -> './*/*.js' `./module-${name}.js` -> './module-*.js' `./modules-${name}/index.js` -> './modules-*/index.js' './locales/' + locale + '.js' -> './locales/*.js' './locales/' + locale + foo + bar '.js' -> './locales/*.js' './locales/' + `${locale}.js` -> './locales/*.js' './locales/' + `${foo + bar}.js` -> './locales/*.js' './locales/'.concat(locale, '.js') -> './locales/*.js' './'.concat(folder, '/').concat(name, '.js') -> './*/*.js'
待轉換的代碼可能是這樣的:
function importLocale(locale) { return import(`./locales/${locale}.js`); }
經過轉換后它會變成下面這樣:
function __variableDynamicImportRuntime__(path) { switch (path) { case './locales/en-GB.js': return import('./locales/en-GB.js'); case './locales/en-US.js': return import('./locales/en-US.js'); case './locales/nl-NL.js': return import('./locales/nl-NL.js'); default: return new Promise(function (resolve, reject) { queueMicrotask(reject.bind(null, new Error('Unknown variable dynamic import: ' + path))); }); } } function importLocale(locale) { return __variableDynamicImportRuntime__(`./locales/${locale}.js`); }
可以看到,實際的 import 被替換成了注入的 __variableDynamicImportRuntime__
函數,該函數會根據運行時拼接的具體字符串返回對應的打包文件。
本節內容翻譯加工自 @rollup/plugin-dynamic-import-vars README.md 部分章節
為了知道要在代碼中注入什么,我們必須能夠對代碼進行一些靜態分析,并對可能的導入做出一些假設。例如,如果只使用一個變量,理論上可以從整個文件系統中導入任何內容。
function importModule(path) { return import(path); // 這根本無法推斷引入了什么 }
為了能夠實現靜態分析,并避免可能出現的問題,動態引入的實現上限定了一些規則:
所有導入都必須相對于導入文件進行。導入不應該是純變量、絕對路徑或裸導入:
// Not allowed import(bar); // 純變量 import(`/foo/${bar}.js`); // 絕對路徑 import(`${bar}.js`); // 裸導入 import(`some-library/${bar}.js`); // 裸導入
文件夾中可能包含你不打算導入的文件。因此,我們要求導入的靜態部分以文件擴展名結束。
import(`./foo/${bar}`); // Not allowed import(`./foo/${bar}.js`); // Allowed
如果你從當前目錄導入文件,很可能會導入一些原本不打算導入的文件,包括書寫代碼的這個文件本身。因此這種情況下需要給出一個更具體的文件名匹配格式:
import(`./${foo}.js`); // not allowed import(`./module-${foo}.js`); // allowed
在生成通配符時,字符串中的每個變量都會被轉換為通配符中的*,每個層級的目錄最多一個星號。這避免了無意中從更多的目錄中添加文件到導入中。
下面的例子中,最終將會生成 ./foo/*/.js
而非 ./foo/**/.js
。
import(`./foo/${x}${y}/${z}.js`);
插件核心轉換代碼僅有 100 行,且非常易懂 —— plugins/index.js at master · rollup/plugins
整體流程分為以下幾步:
通過 AST 分析,拿到對應的導入路徑,也就是 import 表達式括號中的源碼部分。
對這部分的源碼進行處理,調用 dynamicImportToGlob 函數
執行上述限制條件的判斷,嘗試獲取一個合法的通配符。
如果通配符不合法,將會引發錯誤,終止進程。
執行通配符,抓取相關文件。
替換 import 表達式,并注入 __variableDynamicImportRuntime__
函數。
附上插件核心轉換代碼的截圖。
“如何動態引入DynamicImport”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。