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.
npm:
npm install vite-plugin-cdn-import-async --save-dev
yarn:
yarn add vite-plugin-cdn-import-async -D
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>
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>
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>
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>
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 basic ussages see vite-plugin-cdn-import.