We've created our first component (MenuItem
). Next we'll create another new
component (Menu
which contains a series of MenuItem
's). But before we do,
let's think about what our code is beginning to look like.
We've been jamming all the JS into a single file so far, and that's going to get messy pretty quick. So let's split some things out.
Move MenuItem
into its own file which is also included into the index.html
page. The new file will be called lib/menu-item.js
.
Great, now we have 2 Javascript files: lib/app.js
, and lib/menu-item.js
.
This is a huge boon for code reusability and maintenance (not to mention the
git
diffs).
If we continued to follow this pattern, we would end up with loads of <script>
tags on the page, and a whole bunch of global variables (function MenuItem
creates a public, mutable global variable). There is also a large performance
penalty in terms of network requests for every JS file we include in the page.*
To solve the global variables problem, node.js defines a module pattern
using require
and module.exports
Modules are a way of exporting arbitrary code (functions, JSON,
variables, etc) for consumption without polluting the global namespace. Every
module file comes with 2 language constructs: require
and module.exports
.
To export anything from a module, you must assign it to module.exports
:
my-module.js
module.exports = 'foo'
require
is a function which you pass a module name to import:
output.js
let myModule = require('./my-module')
You can read the specifics of how the require
resolution algorithm works
here.
Now myModule
will have the value 'foo'
, so we could output it:
output.js
let myModule = require('./my-module')
console.log(myModule) // foo
Modules are not yet built into any of our JavaScript environments (this may change in ES2017, but we're still in 2016 today).
So we need a build tool which can mimic this behaviour for us. Enter Browserify.
Browserify lets you
import
in the browser by bundling up all of your dependencies.
Browserify will take a single entry file (eg; src/js/index.js
), and look for
all calls to require()
. For every call, it automatically bundles the
require
'd file together with the entry file then saves it to a single output
file. Along the way you can add extra build tools for certain tasks that need
performing (minification for example).
Browserify is fairly straight forward to use (and has amazing documentation).
First, we'll have to install it into our project:
npm install --save-dev browserify
There is also a piece of configuration we now need to add:
Now that the tool is setup, we can create a task which will utilize them.
We'll add that task along side the "start"
task in our package.json
:
package.json
"build": "browserify src/js/index.js -o lib/app.js"
The Browserify command can be broken up in browserify <input file> -o <output file>
Since we want lib/app.js
to be our output file, but it's currently got our
code in it, let's move it to src/js/index.js
to make it the input file:
mkdir -p src/js
mv lib/app.js src/js/index.js
We also want to move our new lib/menu-item.js
file too:
mv lib/menu-item.js src/js/menu-item.js
Now our folder structure should look like this:
.
├── lib
│ └── index.html
├── node_modules
│ └── ...
├── src
│ └── js
│ ├── index.js
│ └── menu-item.js
└── package.json
Before we can use browserify, we need to require()
the menu-item.js
file
inside of src/js/index.js
so we can use what it exports.
Inside of src/js/menu-item.js
, we have a single function MenuItem
, which we
want to export so our other code can use it in the browser without having the
extra <script>
tags.
To do so, we can add the following line to the end of src/js/menu-item.js
:
function MenuItem() {
return React.createElement('li', null, 'Menu Item')
}
module.exports = MenuItem
Now to use that MenuItem
, which is no longer a global variable, we can
require()
the src/js/menu-item.js
file into our src/js/index.js
file.
Add the following line to the top of your src/js/index.js
file:
src/js/index.js
let MenuItem = require('./menu-item')
Note that we used a relative path here (./
) as the two files are in the same
directory.
Note also we don't need to add the .js
file extension - browserify is smart
enough to figure that out for us.
To run the build command, in your terminal execute:
npm run build
This will give us an output file saved to lib/app.js
.
Restart your server (npm start
), and refresh the webpage. You should now see
the same output we had before.
The last piece is to remove the unnecessary extra <script src="menu-item.js">
tag from the HTML since we no longer need it (Browserify has bundled all that up together into lib/app.js
for us!).
If you're interested, you can see what the output file looks like in
lib/app.js
, but keep in mind it's not meant to be human-readable.
* Unless when you're talking about HTTP/2, but that's a whole other workshop!