In recent days I’ve been working on a small single page JS app using React and bootstrap. After getting over my initial issues yesterday saw a lot of progress and I’m finding it far simpler to work with React than almost any other JS “thing” I’ve tried. However, (you knew it was coming didn’t you?) at present my single page app is just that – a monster of a single HTML file with a bunch of required files on my hard drive. While it’s allowed me to make good progress, going forward it’s not what I want.
Today was time to dive back into the murky world of npm, webpack and try and start splitting the monster into lots of smaller pieces. After finding a lot of tutorials online that helped, I’m scribbling this to try and capture what I learned today and hopefully help myself in the future from making the same mistakes! If it helps anyone else then that’s a nice bonus.
There are probably many better ways to do this, but I’ve yet to find them, so if you have suggestions or comments then pass them on as I’m keen to learn and improve my understanding.
Starting Point
For this I’m going to start with a really simple HTML page in a newly created directory containing just the files I need.
The HTML file looks like this.
Yet Another Hello World...
This works and produces the expected results, so now to make something simple very much more complicated 🙂
NPM
I’ll be using npm, so the next step is to get everything installed in preparation. I’ll skip the pain of figuring out what I missed first time around (and second, third etc) and just list everything that will be needed. I think the logic for the –save and –save-dev is right (–save if it’s needed for the final output, –save-dev if it’s only for the background development), but if it’s not I’m sure someone will yell.
npm init -y npm install --save-dev webpack npm install --save-dev babel npm install --save-dev babel-core npm install --save-dev babel-loader npm install --save-dev babel-preset-react npm install --save-dev babel-preset-es2015 npm install --save react npm install --save react-dom
As I’m not going to be putting this into git and it’s only for my own use, I added
"private": true
which stopped the constant warning about no repository being listed.
Structure
This is the area I am still experimenting with. There are loads of boilerplate projects in existance, but while all generally agree o principals the way they arrange things is often different. My small brain can’t handle too much complexity and things can always be changed later, so I’m going to start simple as this is a simple project.
/package.json /webpack.config.js /components /App.js /Main.js /html /index.html
The plan is that all the react components go into (guess where?) components and the HTML file into HTML. I’ll use webpack to generate a single js file also into html so that I could distribute the entire directory. As I want to keep components as components, I’m also going to create an App.js file in components that will include the top level React element.
*NB I haven’t decided if I’m going to use .jsx for JSX formatted files. It seems like a good idea but it’s another change 🙂 For now I’ll try and make sure things will work whichever extension I use, but here it’ll be plain ‘ol .js
*
/componets/Main.js
var React = require('react'); module.exports = React.createClass({ render: function() { return (
Hello World!
) } });/componets/App.js
var React = require('react'); var ReactDOM = require('react-dom'); var Main = require('./Main.js'); ReactDOM.render(, document.getElementById('example'));
/html/index.html
Yet Another Hello World...
Having split all the files, I now need to configure webpack to build the bundle.js file.
Webpack
Webpack expects to find a file called webpack.config.js in the directory you call it from, so we’ll create one. We need to tell it which file is the “entry” where it will start building the required file from and where we want it to create the output. If that’s all you supply there will be an error,
Module parse failed: /components/App.js Line 5: Unexpected token < You may need an appropriate loader to handle this file type.
The module configurations allow you to tell webpack how to handle the various files, so need to add a line that tells webpack how to handle our files. To do this we add a module loader entry that tests for the appropriate files and uses babel-loader. To handle the JSX formatting you also need to tell it to use the react preset.
var path = require('path'); module.exports = { entry: path.resolve(__dirname, "components/App.js"), output: { path: path.resolve(__dirname, "html"), filename: 'bundle.js' }, module: { loaders: [ { test: /components/.+.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['react'] } } ] } };
With this file in place, you should now be able to generate the required bundle.js file by running webpack.
Hash: 1a44318bada57501b499 Version: webpack 1.12.13 Time: 1484ms Asset Size Chunks Chunk Names bundle.js 677 kB 0 [emitted] main + 160 hidden modules
After webpack runs OK, the file html/index.html can be loaded in a browser and looks exactly as the original file 🙂 Success.
Improvements…
While using ‘./Main.js’ for the require is OK, it seems a little messy, so it would be easier to be able to use ‘Main’. To allow this we need to tell webpack a bit more about where to find files, which we do using the resolve entry.
resolve: { modulesDirectories: ['node_modules', 'components'], extensions: ['', '.js', '.jsx'] },
Following this addition, we can change App.js to use
var Main = require('Main');
Using the expected config file for webpack is fine, but if it’s not in the root directory you need to use the –config flag for webpack. I had this in one of my iterations and did forget it a few times – more likely with code you don’t look at for a while. To allow this to work and not forget, it’s possible to add a build command to package.json telling it to use webpack. This can also be used to provide commonly used flags.
"scripts": { "test": "echo "Error: no test specified" && exit 1" },
becomes
"scripts": { "build": "webpack --progress --colors", "test": "echo "Error: no test specified" && exit 1" },
To build using the command you need to use npm run build.
Next Steps
Now that I have a structure and a working webpack I can start to split up my monster file. I also need to figure out how to handle CSS and then look at add some extras to webpack to get minification of the output. Figuring out how to strip the CSS to the base essentials would also be nice, but that’s an optimisation for the future 🙂