ReactJS only implements part of a Web application stack. We’ll take time now to refactor the Facebook tutorial application to support some additional development tools and application features.
Webpack is a well-known module-bundling tool for ReactJS applications.
We start by distinguishing between the application source code and the bundled application.
Make a copy of your solution to the solution in the previous lab and
store it under lab09
. Note that some of the configuration
in this lab is version-dependent so we suggest that you integrate the
package.json
file provided in the course repo as follows.
Copy your lab 8 solution into your lab 9 directory and confirm that it still runs as it did last week.
Copy in the new package.json
file from the course
lab 9 repo, delete and rebuild your node_modules
directory, and confirm that your application still runs. This
configuration files sets module versions that are known to work and
provides some useful NPM run scripts.
Now, bundle the application as follows.
Upgrade the environment by doing the following.
package.json
file discussed
above. If still necessary, install some new tools
using the Node Package Manager (npm).
Note that we save a record of each of these tools in the npm environment. Seenpm install webpack \ babel-loader babel-core babel-preset-react babel-preset-es2015 \ style-loader css-loader postcss-loader \ --save
package.json
.
Rename public/
as app/
and
create a new directory dist/
. You’re
building an application now, not serving up public/static
files, and this will separate the distributed application
source from the bundled application being distributed. This
change requires the following updates:
server.js
to serve up the static
files from dist/
(rather than public/
).
dist/
to your main
.gitignore
file.
Rename app/scripts/example.js
to index.js
.
This is a more common application name.
Configure Webpack by creating webpack.config.js
with
the following contents.
This specifies the application’s source code modules (i.e., entry), the destination for the bundle (i.e., output) and the code pre-processors (i.e., loaders). The pre-processors grab all code files with JS, JSX and CSS extensions. For details on this configuration, see Configuring Webpack.module.exports = { entry: [ __dirname + '/app/scripts/index.js' ], output: { path: __dirname + '/dist', filename: '/bundle.js' }, module: { loaders: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.css$/, loader: 'style!css?modules!postcss' } ] } };
Configure these new loaders as follows.
.babelrc
,
with the following contents.
This specifies that when Webpack runs the Babel compilation (see the loaders specified above), it will support React JSX and ES2015. For details on this configuration, see Loaders: Babel.{ "presets": [ "react", "es2015" ] }
Create a PostCSS configuration file, postcss.config.js
,
with the following contents.
This should be optional but it makes the PostCSS loader happy for some configurations. For details on this configuration, see PostCSS Loader.module.exports = {};
Run Webpack.
node_modules/.bin/webpack
Check out the size and contents of the new bundle file, dist/bundle.js
.
For details on this, see Running Your First
Build.
Modify index.html
to load the new bundle file we’ll
create (bundle.js
rather than example.js
).
<script type="text/javascript" src="bundle.js"></script>
Note that we’ve changed the script type to JavaScript;
Webpack is now doing the required Babel compilations (see above)
so the browser doesn’t need to do them anymore. Also, you
can safely remove the loading of the Babel library in index.html
.
As a temporary measure, copy app/index.html
and
app/css/*
to dist/.
We’ll bundle
these HTML and CSS files as well later in the lab.
The server and application should now run as they did before. When you’ve confirmed that they do, consider the following:
file:///path…/index.html
using the browser? Why or why not?
webpack.config.js
, what does the loader test entry
test: /\.jsx?$/
do?
Save your answers to these questions in a lab09.txt
file in
the root of your lab repo.
The bundling is now configured properly, but the application is still coded in one (overly) large source code file (as specified by the Facebook tutorial).
Modularization is a fundamental computing practice that brings many well-known benefits and JavaScript modules are stored in separate files.
Modularize the application code as follows.
You can skip this step if you’ve integrated the
suggested lab 9 package.json
file discussed
above. If still necessary, install a few more useful packages.
npm install react react-dom jquery jquery-ui remarkable html-webpack-plugin --save
Set up a template HTML file to be inflated by Webpack.
Replace app/index.html
with the following
template, stored as app/index.tmpl.html
.
Note that this template does not load the CDN libraries or the CSS file; Webpack is already configured to handle this. Neither does it load the bundle; we’ll configure Webpack to take care of this next.<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Lab 9 - Webpack</title> </head> <body> <div id="content"></div> </body> </html>
Add this require statement and this plugin specification
to webpack.config.js
.
This plugin instructs Webpack to inflate the template with an import of the bundle it creates and to load the result in the output directory,var HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: [ … ], output: { … }, module: { … }, plugins: [ new HtmlWebpackPlugin({template: __dirname + "/app/index.tmpl.html"}) ] };
dist/
.
The webpage template defined above no longer loads the
client-side libraries (e.g., React, react-dom, ...). Do this
by modifying scripts/index.js
as follows.
import React from 'react'; import ReactDOM from 'react-dom'; import Remarkable from 'remarkable'; import $ from 'jquery'; import '../css/base.css'; var CommentBox = React.createClass({ … }); var CommentList = React.createClass({ … }); var Comment = React.createClass({ … }); var CommentForm = React.createClass({ … }); ReactDOM.render( … );
Note that this is actually a more appropriate place to load the libraries because this is where they’re actually being used.
With this configuration, it’s safe to delete the
contents of dist/.
Do that now. Webpack will
re-build and re-deploy all the required files.
Break index.js
into separate JavaScript module
files, one for each React component, leaving the main
ReactDOM.render( … )
code in index.js
. The new
modules should each have the following structure:
external imports required by this module… local imports required by this module… module.exports = React.createClass({ class definition… });
The external imports for each module should be selected, as
needed, from the list of imports currently included in the
index.js
file. Include only those libraries that
are needed for each individual module.
Each class definition is exported using module.exports =
class definition
and must be included by other modules what use using the
following form.
import NewModuleName from './NewModuleFileName';
Again, import only those local modules that are required. For
example, the CommentBox
module must import CommentList
and CommentForm
.
Re-run Webpack and the server.
You can skip this step if you’ve integrated the
suggested lab 9 package.json
file discussed
above. If still necessary, streamline this process by adding
the following to package.json
.
{ "name": "server", old settings... "scripts": { "build": "webpack --progress", "start": "node server.js" }, old settings... }
Note that NPM knows where to look for Node binaries, so
you don’t need to include the full
path for the Webpack command. Note also that
non-standard npm scripts, e.g., build or dev,
must be run with the run option, e.g., npm
run
script
; standard scripts,
e.g., start, don’t need the run directive.
npm run build
and
run, as before, using npm start
.
The server and application should now run as they did before. When you’ve confirmed that they do, consider the following:
dist/index.html
and dist/bundle.js
.
How are they different from the previous versions?
Save your answers to these questions in your lab09.txt
file.
The application is now bundled.
The Webpack Dev Server supports a number of nice development features, including hot module replacement. Because this dev server can only serve up static files, we’ll configure the dev server to serve the application itself over port 3001 and use the original Express server to serve the data API (i.e., /api/comments) over port 3000.
Deploy the Webpack Dev Server as follows.
package.json
file discussed
above. If still necessary, install the dev server, this time as a
development-only tool.
npm install webpack-dev-server --save-dev
webpack.config.js
.
This new plugin instructs Webpack to rebundle the application when source files are edited and refresh the browser automatically. The devserver specification runs the server on port 3001 and diverts all data API request through to server.js, which is assumed to be running on port 3000. See Webpack Development Server.var webpack = require('webpack'); other requires... module.exports = { entry: [ … ], output: { … }, module: { … }, plugins: [ other plugins..., new webpack.HotModuleReplacementPlugin() ], devServer: { port: 3001, proxy: { '/api/*': 'http://localhost:3000' }, colors: true, historyApiFallback: true, inline: true, hot: true } };
You can skip this step if you’ve integrated the
suggested lab 9 package.json
file discussed
above. If still necessary, add a development script to package.json
.
You can run the servers using{ "name": "server", old settings... "scripts": { "build": "webpack --progress", "dev": "node server.js & webpack-dev-server --progress", "start": "node server.js" }, more old settings... }
npm run dev
.
The server and application should now run as they did before. When you’ve confirmed that they do, consider the following:
localhost:3001
— Try editing one of the
React module files. Explain what happens, both to the
displayed SPA webpage and to the bundle/index files.
localhost:3000
— Try loading /api/comments
.
Explain what you get. Does the SPA page running on this port
change (due to the edits made above) as well?
Is it worth all this trouble to build a development configuration?
Save your answers to these questions in your lab09.txt
file.
We will grade your work according to the following criteria: