Lesson Weekend

Let's update our shape tracker application to use import and export statements. By the end of the lesson, we will be using webpack to bundle our code for us.

First, let's start by adding a dist directory. Next, we'll move index.html inside the dist directory. This is a temporary change - we will be moving index.html again in a few lessons when we use webpack to process it. By the way, don't forget that we've added dist/ to our .gitignore file. If we were to push our updated code, changes to index.html wouldn't get pushed. This is fine because this change is temporary - and there is no need to commit our code just yet.

Here's our current project structure:

shape-tracker/
├── dist
│   ├── index.html
├── src
│   ├── main.js
│   └── triangle.js
└── css
    └── styles.css
├── package.json
└── .gitignore

Note that we aren't including automatically generated files and directories like node_modules and package.lock.json in the structure above. We're only including the files and directories that we need to add manually.

Next, let's update our JavaScript files to use import and export statements. We'll use a default export for Triangle because it's only thing we'll export from the file.

src/triangle.js
export default function Triangle(side1, side2, side3) {
  ...
}

...

Now we need to make sure we import our Triangle constructor at the top of main.js:

src/main.js
import Triangle from './triangle.js';
...

Finally, we'll make some small updates to the <head> of index.html:

...
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script type="text/javascript" src="bundle.js"></script>
  <link rel="stylesheet" href="./../css/styles.css">
  <title>Shape Tracker</title>
</head>
...

Now we only have one JavaScript tag - for bundle.js. That file doesn't exist yet, but webpack will create it for us.

We've also updated the relative path to our stylesheet to ./../css/styles.css. That's because we've moved index.html to our dist folder. That path will be changing again in the next lesson.

Bundling Code

Now we can $ npm run build in the root directory of the project. webpack will access the entry point at src/main.js. Then webpack will recursively add any dependencies (anything that needs to be imported from elsewhere). Since main.js only imports Triangle from triangle.js and triangle.js has no imports of its own, our project currently only has one dependency.

If all goes well, your output will look something like this:

> [email protected] build /Users/staff/Desktop/shape-tracker
> webpack

Hash: 7d88ba320f665950d074
Version: webpack 4.39.3
Time: 70ms
Built at: 07/02/2020 9:33:20 AM
    Asset      Size  Chunks             Chunk Names
bundle.js  1.27 KiB       0  [emitted]  main
Entrypoint main = bundle.js
[0] ./src/main.js + 1 modules 650 bytes {0} [built]
    | ./src/main.js 443 bytes [built]
    | ./src/triangle.js 207 bytes [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

Note: If you get errors, you probably missed a step from previous lessons. Make sure you've actually added a webpack.config.js file with a configuration and updated the scripts section of package.json to include "build": "webpack".

Also, don't worry about the warning above for now. We haven't specified whether this should be a production or development environment, which won't affect our code.

If you check the dist folder, you'll see that webpack has created a new file called bundle.js. The file includes all of our concatenated source code plus some code that webpack has added. Because our environment currently defaults to production mode, it'll look something like this:

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";function r(e,t,n){this.side1=e,this.side2=t,this.side3=n}n.r(t),r.prototype.checkType=function(){return"I can't answer that yet!"},$(document).ready((function(){$("#triangle-checker-form").submit((function(e){e.preventDefault();const t=new r($("#length1").val(),$("#length2").val(),$("#length3").val()).checkType();$("#response").append("<p>"+t+"</p>")}))}))}]);

Not only has all the code been concatenated but it's also been minified. The complete file is only 5 K. It's not at all human readable, but it's much nicer for our browsers to read quickly and efficiently!

Now we can open our index.html file and the code will work. One other thing - if we go to the console and check the window object, we'll no longer see Triangle just hanging out in the global scope. Our JavaScript code is now modularized and code is scoped where it is needed. This is particularly important as our projects get larger.

So far, we haven't reduced our code much - all we've done is remove a single script tag from our code. However, it's a big deal when a project has hundreds of dependencies. More importantly, our JavaScript logic is separated in different files - which is great for human organization - but then bundled into one after we are finished with it - which is great for speed and efficiency.

We've barely scratched the surface of what webpack can do. In the next several lessons, we'll bundle our CSS, customize webpack for linting, and set up a live development server.

Lesson 12 of 48
Last updated more than 3 months ago.