Is there any way of building my svelte or react application in a way, that the three.js module (which I usually import using npm) will be declared as a script tag which will call the module from a CDN? I would like to keep the advantages of a framework but also be able to reduce my final bundle size, since most of my bundle contains three code.
Thank you for your wisdom
Yes, you can do the following:
In your "index.html" file, you can import the js file from a CDN as follow:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
Then, in the file where you want to use it, which could be a React component for instance, you can do the following:
const THREE = window.THREE;
Which would replace your import statement, which would have been import * as THREE from "three"; or import THREE from "three";
There are two ways to go about your goal of reducing bundle size:
To keep semantics of ESModules, you may simply replace your current three.js imports with a URL from an npm CDN, like unpkg:
| Pros | Cons |
|---|---|
| No extra configuration needed | Slower to load, as browser needs to spin up new connections to access third-party CDN |
Asynchronously
<script>
// App.svelte
import('https://unpkg.com/three@0.133.1/build/three.min.js').then(({ default: THREE }) => {
// your code here
});
</script>
Synchronously
Note: Importing like this blocks the rest of your script from loading while
three.jsis downloading, which defeats the purpose of the whole shebang. It's just here for completeness
<script>
// App.svelte
import { default as THREE } from 'https://unpkg.com/three@0.133.1/build/three.min.js';
// your code here
</script>
This method takes advantage of the fact that you're already using a bundler (probably rollup, vite, or webpack). This answer will focus on rollup as it's the default used in svelte's examples.
| Pros | Cons |
|---|---|
| Faster to load, as browser can use existing connections to access first-party resources | More complicated to get set up |
Asynchronously
In your rollup.config.js file, ensure output.format is set to 'esm' & output.dir is set instead of output.file
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/index.js',
output: {
sourcemap: !production,
format: 'esm',
name: 'app',
dir: 'public',
},
plugins: {
// your plugins
svelte({
compilerOptions: {
dev: !production,
},
}),
postcss({
extract: 'bundle.css',
}),
resolve({
browser: true,
dedupe: ['svelte'],
}),
commonjs(),
}
}
<script>
// App.svelte
import('three').then(({ default: THREE }) => {
// your code here
});
</script>
Note: There is no synchronous way due to how code-splitting is evaluated at compile time. Plus it doesn't make much sense to do it like that anyways.