If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeWhen it comes to using require()
and module.exports
, well, we rock. So now... let's completely change things! Woohoo!
Let me tell you a story of bravery, cunning and JavaScript... simplified to fit into 20 seconds. require()
and module.exports
basically come from Node.js. They're not part of ECMAScript - so not officially part of the JavaScript language. But, if you're in Node, you know that these are available to use.
Node did this because... well... there was no module system in ECMAScript. So they invented require()
and module.exports
to fit their needs... and rescue the princess. But, guess what!? In ES6... there now is a module system! It uses two new keywords: import
and banana
, I mean export
.
Webpack supports the original require()
and module.exports
and the ES6 syntax. But, import
and export
have a few subtle advantages... other than being newer and shinier.
But mostly, they work the same! Instead of const _ = require()
, say import _ from 'lodash'
:
... lines 1 - 7 | |
import _ from 'lodash'; | |
... lines 9 - 215 |
When we do, Webpack is still happy... and our app still works!
Takeaway number 1 is: import
and require()
basically do the same thing. But import
has one special power: the ability to import just one part of a module. We know that lodash
actually exports an object with many keys on it... random
is one of them... which is why we can say _.random()
.
Instead of importing everything, you can instead say import { random } from 'lodash'
:
... lines 1 - 7 | |
import { random } from 'lodash'; | |
... lines 9 - 215 |
This uses destructuring - a little ES6 feature we talked about in the previous tutorial. Since lodash
returns an object with a random
key on it, this syntax sets a new variable called random
to that value.
Of course, we're not importing an _
variable anymore. So, search for _.random()
and replace this with just random()
:
... lines 1 - 7 | |
import { random } from 'lodash'; | |
... lines 9 - 11 | |
class RepLogApp { | |
... lines 13 - 171 | |
_clearForm() { | |
... lines 173 - 177 | |
// pre-fill with a random rep number | |
$form.find('[name="reps"]').val(random(1, 10)); | |
} | |
... lines 181 - 195 | |
} | |
... lines 197 - 215 |
Great! And yea... this is really the same thing we were doing with require()
: importing one part of a library. But... stay tuned!
Our app still works... but we have a problem! Our file size! It was 1.3Mb before, but with import, it's much bigger! This makes sense: import
loads everything from lodash
... even if we only use the random
key on it.
But, dream with me for a moment. What if Webpack were so smart that it knew we were only using this one key... and it removed the rest of the code from the built file. That would be awesomesauce! Oh, and I have the perfect name for this imaginary feature: tree shaking! Yea, because it would be like Webpack was "shaking" our JS and allowing all unused modules to "fall off"!
Wait what? It does exist? It is called tree shaking? Ok, apparently Webpack can do this tree shaking thing already! And this is where import
starts to get really interesting.
When you require()
something, that entire module is included. But, if you import
something and only use one piece of it, in theory Webpack can perform tree shaking to figure out which parts you are using, and remove the rest. But... you'll notice that... well, this does not appear to be happening! The file size is too big!
At the time of this recording, the latest Webpack - version 3.5 - seems to not do tree shaking perfectly. It works in some cases, but not others - and there's a lot of conflicting info about this. But, I expect it to work better in the future. And by using import
instead of require()
, we're opting into tree shaking... if and when it works.
But for now, I do want a smaller file size. And since tree shaking isn't helping, update the line to import random from 'lodash/random
:
... lines 1 - 7 | |
import random from 'lodash/random'; | |
... lines 9 - 215 |
That's effectively the same thing we had before with require()
.
Let's change the rest of our code to use import
! Since this is a chore, I'll speed through part of this. For the CSS, we don't need the value it imports. So, just say import
then the filename:
... lines 1 - 2 | |
import Helper from './RepLogAppHelper'; | |
import $ from 'jquery'; | |
import swal from 'sweetalert2'; | |
import 'sweetalert2/dist/sweetalert2.css'; | |
import Routing from './Routing'; | |
import random from 'lodash/random'; | |
... lines 9 - 215 |
Keep going: inside rep_log.js
, change require()
to import
:
import $ from 'jquery'; | |
import 'bootstrap-sass'; | |
import RepLogApp from './Components/RepLogApp'; | |
... lines 4 - 13 |
And then in login.js
:
... lines 1 - 2 | |
import $ from 'jquery'; | |
import '../css/login.css'; | |
... lines 5 - 24 |
And finally layout.js
:
... lines 1 - 2 | |
import $ from 'jquery'; | |
import 'bootstrap-sass'; | |
// make sure the polyfill library is loaded in this main entry | |
import 'babel-polyfill'; | |
import '../css/main.scss'; | |
... lines 8 - 12 |
After all that... Webpack is still happy! The page still works... and our code is trendier than ever!
What about exporting values? At the bottom of RepLogApp
, change module.exports
to export default RepLogApp
:
... lines 1 - 213 | |
export default RepLogApp; |
Most of the time, you will only export one value from a module, like a single class or function. In these cases, use export default
. Repeat this in RepLogAppHelper
: export default Helper
:
... lines 1 - 36 | |
export default Helper; |
And in Routing.js
: export default window.Routing
:
... lines 1 - 4 | |
export default window.Routing; |
In some cases, you might have a module that exports multiple things, like two functions. In that case, you could say export function foo() {}
and also export function bar() {}
:
// ...
export function foo() {};
export function bar() {};
Instead of exporting a default
, you're using named exports: you're exporting foo
and bar
.
When you do this, the import
changes a bit to import { foo, bar } from
:
import { foo, bar } from './cool_functions';
We use destructuring to grab whatever named exports we want.
It can look a little confusing at first. Fortunately, most of the time, you'll probably export a single, default
value and import it like we are. But keep this in mind.
Next! Let's fix a big problem we've been ignoring... the fact that jQuery is packaged inside every individual built file. Oof, wasteful!
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": "^7.2.0",
"symfony/symfony": "3.3.*", // v3.3.16
"twig/twig": "2.10.*", // v2.10.0
"doctrine/orm": "^2.5", // v2.7.0
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
"symfony/swiftmailer-bundle": "^2.3", // v2.6.3
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.4.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"friendsofsymfony/user-bundle": "^2.0", // v2.1.2
"doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.6
"symfony/phpunit-bridge": "^3.0" // v3.3.5
}
}
// package.json
{
"dependencies": [],
"devDependencies": {
"babel-core": "^6.25.0", // 6.25.0
"babel-loader": "^7.1.1", // 7.1.1
"babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
"babel-preset-env": "^1.6.0", // 1.6.0
"bootstrap-sass": "^3.3.7", // 3.3.7
"clean-webpack-plugin": "^0.1.16", // 0.1.16
"copy-webpack-plugin": "^4.0.1", // 4.0.1
"core-js": "^2.4.1", // 2.4.1
"css-loader": "^0.28.4", // 0.28.4
"extract-text-webpack-plugin": "^3.0.0", // 3.0.0
"file-loader": "^0.11.2", // 0.11.2
"font-awesome": "^4.7.0", // 4.7.0
"jquery": "^3.2.1", // 3.2.1
"lodash": "^4.17.4", // 4.17.4
"node-sass": "^4.5.3", // 4.5.3
"resolve-url-loader": "^2.1.0", // 2.1.0
"sass-loader": "^6.0.6", // 6.0.6
"style-loader": "^0.18.2", // 0.18.2
"sweetalert2": "^6.6.6", // 6.6.6
"webpack": "^3.4.1", // 3.4.1
"webpack-chunk-hash": "^0.4.0", // 0.4.0
"webpack-dev-server": "^2.6.1", // 2.6.1
"webpack-manifest-plugin": "^1.2.1" // 1.2.1
}
}