vite-plugin-cdn-import-async v1.1.0
Vite plugin which can import modules asynchronously from CDN.
This plugin is forked from vite-plugin-cdn-import and allows you to specify modules that should be loaded in defer/async mode in addition.
Installation
npm:
npm install vite-plugin-cdn-import-async --save-devyarn:
yarn add vite-plugin-cdn-import-async -DUsage
Plugin config
Specify async or defer to mode param whthin configs of the module you want to import asynchronously from CDN:
// vite.config.js
import cdnImport from 'vite-plugin-cdn-import-async'
export default {
plugins: [
cdnImport({
modules: [
{
name: 'react',
var: 'React',
mode: 'async', // 'async' atrribute will be added to its <script> tag.
path: `https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js`,
},
{
name: 'lottie-web',
var: 'lottie',
mode: 'defer', // 'defer' atrribute will be added to its <script> tag.
path: `https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js`,
},
{
name: 'axios', // Module without 'mode' param will be loaded synchronously.
var: 'axios',
path: 'https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js',
}
],
}),
],
}This demo will generate codes below into the output file:
<script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
<script async onload="__cdnImportAsyncHandler('React')" onerror="__cdnImportAsyncHandler('React', true)" src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
<script defer onload="__cdnImportAsyncHandler('lottie')" onerror="__cdnImportAsyncHandler('lottie', true)" src="https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js"></script>Input file
By default, every module that defined in plugin config will generate its <script> tag into the output file, no matter this module was used in the project or not.
But now you can set attribute data-cdn-import in <meta> tag of the input file, to determind which module should generate <script> into the output file.
For example (example/react/index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" data-cdn-import="React,lottie" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>According to data-cdn-import="React,lottie", plugin will only handle React and lottie modules and generate their <script> into the output file (example/react/dist/index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Example</title>
<script>window.__cdnImportAsync_varToNameMap={"React":"react","lottie":"lottie-web"};</script>
<script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
<script async onload="__cdnImportAsyncHandler('React')" onerror="__cdnImportAsyncHandler('React', true)" src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
<script defer onload="__cdnImportAsyncHandler('lottie')" onerror="__cdnImportAsyncHandler('lottie', true)" src="https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js"></script>
<script type="module" crossorigin src="/assets/index.c9473e27.js"></script>
<link rel="stylesheet" href="/assets/index.cd9c0392.css">
</head>
<body>
<div id="root"></div>
</body>
</html>Mode suffix
Using suffix @async or @defer within data-cdn-import can also generate asynchronous <script> tag of marked module.
Example
Notice that here's no mode: 'async' in the configs of React module:
// vite.config.js
import cdnImport from 'vite-plugin-cdn-import-async'
export default {
plugins: [
cdnImport({
modules: [
{
name: 'react',
var: 'React',
path: `https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js`,
},
{
name: 'lottie-web',
var: 'lottie',
mode: 'defer',
path: `https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js`,
},
{
name: 'axios',
var: 'axios',
path: 'https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js',
}
],
}),
],
}And the input file (example/react/index.html) likes:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" data-cdn-import="React@async,lottie" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>According to React@async within data-cdn-import, the React module will generate <script> tag with async attribute into the output file (example/react/dist/index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Example</title>
<script>window.__cdnImportAsync_nameToVar={"react":"React","lottie-web":"lottie"};</script>
<script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
<script async onload="__cdnImportAsyncHandler('React')" onerror="__cdnImportAsyncHandler('React', true)" src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
<script defer onload="__cdnImportAsyncHandler('lottie')" onerror="__cdnImportAsyncHandler('lottie', true)" src="https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js"></script>
<script type="module" crossorigin src="/assets/index.c9473e27.js"></script>
<link rel="stylesheet" href="/assets/index.cd9c0392.css">
</head>
<body>
<div id="root"></div>
</body>
</html>Lazy loading
In addition to async or defer as the value of mode, here's other avalable values for lazy-loading:
Value of mode | Description |
|---|---|
| DOMContentLoaded | Module will start being loaded within DOMContentLoaded event of window. |
| load | Module will start being loaded within load event of window. |
| milliseconds | Module will start being loaded in specified milliseconds after load event emits. |
Example
In vite.config.js:
export default defineConfig({
plugins: [
importToCDN({
modules: [
{
name: 'react',
var: 'React',
mode: 'DOMContentLoaded',
path: `https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js`,
},
{
name: 'lottie-web',
var: 'lottie',
mode: '3000',
path: `https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js`,
},
{
name: 'axios',
var: 'axios',
mode: 'load',
path: 'https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js',
}
],
}),
reactRefresh(),
],
})Or the entry file:
<meta data-cdn-import="React@DOMContentLoaded,lottie@3000,axios@load" />The output file would be like:
<script>window.__cdnImportAsync_nameToVar={"react":"React","lottie-web":"lottie"};</script>
<script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
<script>function __cdnImportAsync_deferredLoader(n,r){var c=document.createElement("script");c.onload=function(){__cdnImportAsyncHandler(n)},c.onerror=function(){__cdnImportAsyncHandler(n,!0)},c.src=r,document.body.appendChild(c)}</script>
<script>!function(){window.addEventListener("DOMContentLoaded",function e(){__cdnImportAsync_deferredLoader("React","https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"),window.removeEventListener("DOMContentLoaded",e)},!1)}();</script>
<script>!function(){window.addEventListener("load",function e(){setTimeout(function(){__cdnImportAsync_deferredLoader("lottie","https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js")},3000),window.removeEventListener("load",e)},!1)}();</script>
<script>!function(){window.addEventListener("load",function e(){__cdnImportAsync_deferredLoader("axios","https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js"),window.removeEventListener("load",e)},!1)}();</script>
Import async module
Once a module is loaded asynchronously by mode config, you should create an function to handle it while using it in pages:
export function cdnAsyncImport(moduleName: string, isDev: boolean): Promise<any> {
if (isDev) {
return Promise.reject()
}
return new Promise((resolve, rejects) => {
const errorModules = window.cdnImportAsync_loadingErrorModules || []
const moduleVar = window.__cdnImportAsync_nameToVar[moduleName]
if (errorModules.includes(moduleVar)) {
rejects()
} else if (window[moduleVar]) {
resolve(window[moduleVar])
} else {
window.addEventListener(
'asyncmoduleloaded',
(e: any) => {
const { detail } = e || {}
if (detail && !detail.isError && window[moduleVar]) {
resolve(window[moduleVar])
} else {
rejects()
}
},
false
)
}
})
}
cdnAsyncImport('lottie-web', import.meta.env.DEV).then(lottie => {
// Async module has been successfully loaded.
console.log(lottie)
}).catch(() => {
// Handling DEV or Error case
import('lottie-web').then(...)
})Other ussages
Other basic ussages see vite-plugin-cdn-import.