Consider adding "sideEffects": false
to package.json
so that bundlers like webpack are able to tree shake three.js. See https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free for more info.
Hey! I just came to Github to propose something similar.
I'm just testing another javascript bundler (parcel.js) But I normally use webpack and rollup depending on the type of work I'm doing.
As an example I've created a simple end point which just has the following:
import { Vector3 } from 'three';
console.log(new Vector3(1, 2, 3));
I've compiled it, and I've created a 568.3kb
file out of that. Mainly because all of threejs library came along with it.
Would adding the "sideEffects": false
solve this issue entirely @bschlenk or do we need to refactor the exports across the threejs src in order to allow this to work?
It would greatly improve bundle sizes and website speeds if we could actually just use what we're importing from three.
Not sure if this can open up to a larger conversation on what's the core
of threejs (cause that can be a long conversation on itself) but would be nice to have this feature added in!
I've quickly created a repo that has the above example running on all 3 bundlers I've mention (Parceljs, Rollupjs and Webpackjs)
https://github.com/andrevenancio/test-treeshaking
None of them seem to have process any tree shaking on three.js. Which validates @bschlenk point above. I did try however to copy the latest three.module.js
to my src
folder and import that instead (to avoid having to add "sideEffects": true
I would expect that three shaking would happen in one of the bundlers, but they all fail to tree shake three.js.
import { Vector3 } from 'three';
console.log(new Vector3(1, 2, 3));
generates bundle sizes of:
parcel.js - 578kb
rollup.js - 553kb
webpack.js - 555kb
import { Vector3 } from 'three/src/math/vector3';
console.log(new Vector3(1, 2, 3));
generates bundle sizes of:
parcel.js - 16kb
rollup.js -14kb
webpack.js - 15kb
Now I'm confuse if this is a bundler problem and we should create an issue on their repos, or if this is a three.js specific issue.
I remember three.js has been heavily refactored by @Rich-Harris (author of Rollup) I would expect that tree shaking would at least work on his library. Surely I'm doing something wrong?
Maybe we should point to three.js source folder instead of the three.module.js file.
I pulled down your test package and did some experimenting. I was able to get tree shaking to work (I only tested webpack) after making two changes to three's package.json
:
Change module
to src/Three.js
, and add "sideEffects": false
.
I thought "sideEffects": false
was the only change that needed to be made, but I guess not. I think that tree shaking in webpack is done at the module level. When using src/Three.js
as the module, webpack can tree shake because this file has every single feature separated into a separate file, and Three.js
just re-exports all of them.
The original file, build/three.module.js
, has everything rolled into one file, so Webpack can't do tree shaking.
The sideEffects
property also accepts an array of files which have side effects, I believe if this change were to actually be done, then src/polyfills.js
would need to be listed there so that it doesn't get pruned out of the bundle.
Its possible that three.modules.js is for browser modules. But still your initial point is valid. package.json needs to be updated. I remember @TrySound changed gl-matrix
quite a bit to make it possible to import it in any way possible. But I can't remember what he done exactly.
Adding sideEffects: false
alone will have no effect. With aliasing three
to src/Three.js
you'll lost both glsl and gl-constants build optimization and JSM imports support that fixes this long standing issue.
I wrote a plugin that helps situation for some, https://github.com/vxna/optimize-three-webpack-plugin
But I am all hands up for this change as it costs nothing and will be already there if something changes.
@bschlenk make a PR?
No PR yet?
Anyway, I think this issue thread could serve as a place to discuss the possibility, and obstacles for, tree-shaking three.js. Maybe it should be renamed.
Can someone test if after #17276 this code produces small builds?
import { BoxBufferGeometry } from 'three';
console.log( new BoxBufferGeometry( 1, 2, 3 ) );
/ping @andrevenancio
Now sure I'm testing it the right way but I don't think it produces small builds.
I've pulled the PR from @Mugen87 on #17276 and did an npm run build
on it.
I then pulled the dist/three.module
to my repo and tried to build using parcel, rollup and webpack.
The only way I can get it to only import the BoxBufferGeometry instead of the whole library (resulting in a bundle of around 16kb instead of >500kb) is if I target the dependency directly like import { BoxBufferGeometry } from 'three/geometries/BoxGeometry'
.
Though I think moving into class based components is a good decision moving forward, I'm not sure if its the only thing we need to do in order to get tree shaking working properly.
Would love to get some feedback from @Rich-Harris on what's the best approach for tree shake (in in rollup) and maybe a few of us can then start going through classes and adjusting where possible so we can aim towards smaller builds. I'm happy to help we just need a nudge in the right direction.
Hi guys,
Just a few minor things need to happen if we want to treeshake threejs in webpack.
_The first thing on this list is the only required change in the threejs package._
"sideEffects": [
"src/Three.js"
]
mode: 'production'
resolve: {
alias: {
"three": "three/src/Three.js"
}
}
const TerserPlugin = require('terser-webpack-plugin');
optimization: {
minimizer: [
new TerserPlugin({
extractComments: true,
cache: false,
parallel: true,
sourceMap: false, // Must be set to true if using source-maps in production
terserOptions: {
extractComments: 'all',
compress: {
pure_funcs: ['console.info', 'console.debug', 'console.log'] // remove info debug and log, keep warn and error
},
output: {
comments: false,
},
}
})
]
}
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [
new BundleAnalyzerPlugin()
]
@Qubica
Adding
sideEffects: false
alone will have no effect. With aliasingthree
tosrc/Three.js
you'll lost both glsl and gl-constants build optimization and JSM imports support that fixes this long standing issue.
Also re-adding terser
does nothing because it's already enabled in production mode.
Would love to get some feedback from @Rich-Harris on what's the best approach for tree shake
Refactor the codebase to use classes. Everything else is displacement activity! (There may be additional refactoring needed after that to get everything to treeshake nicely, but that's the essential first step — it's hard to know what that work is until then)
Adding
sideEffects: false
alone will have no effect. With aliasingthree
tosrc/Three.js
you'll lost both glsl and gl-constants build optimization and JSM imports support that fixes this long standing issue.
Hmm, I had to read better. You're right, my bad.
Closing for now. As mentioned by @Rich-Harris, we focus now on the migration to ES6 classes (e.g. #17425). After that, we'll evaluate what's to do next in order to get treeshaking to work.
I've just developed a webpack/rollup plugin three-minifier to solve this problem.
Most helpful comment
Refactor the codebase to use classes. Everything else is displacement activity! (There may be additional refactoring needed after that to get everything to treeshake nicely, but that's the essential first step — it's hard to know what that work is until then)