Module Specifiers Versus Relative Import References
So far I have been demonstrating how to import ES6 modules into the client scripts by using relative URLS to reference the specific modules, and also adding paths to the
./src/client/tsconfig.json to indicate to VSCode and the TypeScript compiler, which type definition file (.d.ts) it should use for which import. I call this method using Relative Import References*.
eg, using Module Specifiers
Versus using Relative Import References
This course, by default uses Relative Import References, but I will show you how to use Module Specifiers so that you have a choice.
To import modules client side using the Module Specifiers method, there are a few different steps involved with setting up your project structure. Without these steps you may see an error in the browser console similar to
When writing your import statements in the VSCode IDE using Module Specifiers, you normally won't see an error underlining your import statements. This is true if your TypeScript compilerOptions are set to use
moduleResolution: "node", which is actually the default setting. The VSCode IDE and TypeScript compiler will scan the
node_modules folder using various rules and attempt to auto link a type definition file it finds to the Module Specifier that you wrote in the import statement.
You only become aware of the error when you finally try to run your code in the browser. For many developers, new and old, this doesn't make much sense, and can be a major source of confusion with many people giving up or trying to resolve there problems by asking questions on internet forums.
Module Specifiers rely on a Module Resolution traversing strategy. This doesn't work by default in browsers since the browser doesn't have direct access to the file system on the server in order to traverse all the folders and try all the methods of finding references that are involved during Module Resolution. If this was to run from the browser, it would trigger many 404 errors in the client as it tries out all the possible rules for finding references. You can read more about Module Resolution strategies at TypeScript Module Resolution
The tool that is used is commonly called a bundler. There are many bundlers that we can choose from to add to our projects, eg Webpack, Parcel, Rollup, Browserify and many more. The most common bundler at the time of writing this documentation is Webpack. So I will demonstrate setting up Webpack.
When bundling all the code and imports at compile time, a Module Resolution strategy will not be necessary for the browser to support in order to get Module Specifiers to work. All the code required by the client should already be sorted in memory ready for referencing for when the page has downloaded.
When using Relative Import References, it is also not necessary for the browser to understand any Module Resolution strategy, since you are already explicitly telling the browser where it can locate its resource by using a specific URL in the import statement. No guess work is required by the browser. URLs are one of the fundamental building blocks of the internet. If the path portion of the URL cannot be found by the Domain or IP of the server that you are requesting it from, it will return a 404 error indicating that it could not find anything at that resource location that you requested.
When using URLs in your import statements, you can also reference an import from an external web server by using the full domain name/ip and path, eg, this example below targets a specific Threejs release from a public CDN
In this course, I have been using paths relative to the local web server which is the NodeJS Express server the we've setup in earlier lessons. eg,
./dist/client/ folder and also serves the Threejs libraries via static routes.
When using Webpack, you also have the option of using it's own development server instead of the NodeJS and Express server that I have demonstrated so far. The development server also provides Hot Module Replacement (HMR) functionality. Many developers will use the Webpack development server with HMR during development so it is good to be aware of the option.
I have created a version of the course boilerplate that uses the Webpack bundler, development server and HMR.
If you would like to try using the Webpack option throughout the course, you can download this alternative boilerplate and compare the differences to the existing boilerplate that uses Relative Import References served by the NodeJS Express server. I will point out the major differences in the project structure and code in the video.
Download the Alternative Threejs TypeScript Webpack Boilerplate
Git clone the boilerplate into a new folder so that you don't overwrite what you have so far.
This will place the repository into a new folder called Threejs-TS-Webpack. You can use any folder name you prefer. Just modify the folder name in the above command to be something other than Threejs-TS-Webpack.
Now CD into the new folder
Checkout the statsguiwebpack branch to get the code specific to this branch.
And run it, visit http://127.0.0.1:8080 and it should appear in the browser identical to the existing boilerplate we have manually created so far. The browser should now show the green wire frame cube with the Stats panel and Dat GUI panel allowing you to rotate it.
The default webpack dev server uses port 8080 and not port 3000 which I have been using when starting the NodeJS Express server.
The major difference when developing using the Module Specifiers construct with Webpack, its development server and HMR, is that,
- when using the Webpack development server, the
bundle.jsfile is not saved to disk, but served directly from memory.
./src/server/sever.tssince the imported libraries would now be included into the compiled
bundle.jsbeing served via the Webpack development server contains a large amount of extra code relating to HMR and dev server functionality. It is also not compressed or minified and is a significantly larger file than just downloading the libraries manually using the Relative Import Reference method.
- You do not run the compiled
bundle.jsthrough the Webpack development server on a production server. Instead you run the
npm run buildcommand and it will produce an optimised
bundle.jsthat is minified, and contains only the code required to run statically in the browser. There should also be no HMR related code included in the output. This production version of
bundle.jsis usually smaller and faster to download then importing reverences individually such as when using the Relative Import Reference method.
- The Webpack development server is not a production quality server, so you will still need to resolve how to serve your project from a live public facing web server. Later in the course I demonstrate Deploying to Production by setting up the NodeJS Express server behind a Nginx proxy with a domain name and SSL certificate.
This course is written to use Relative Import References by default. I have added code to the next several lessons that you can comment out in the lesson code samples, that will let you choose whether you want to import using Module Specifiers or Relative Import References. I have not added these comments to every code example throughout the course. After seeing this a few times, the differences will become obvious for you to recognise and then you make the required changes yourself depending on which import construct you prefer.
For reference, I have added the code to this page, but I advise that it will be easier to download the Git repository instead.
In summary, this branch of the boilerplate contains 4 extra libraries involved in using Webpack with TypeScript and is less reliant on the NodeJS Express server during the development phase.
It has a modified client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
It has modified import statements in the
client.ts that uses Module Specifiers instead of Relative Import References.
1 2 3 4 5 6
Added a file called webpack.dev.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Added a file called webpack.prod.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
The package.json has modified scripts and dependencies sections specific for using webpack with the dev server option and HMR.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
index.html now references bundle.js instead of client.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18