Hands-On Full-Stack Web Development with GraphQL and React
上QQ阅读APP看书,第一时间看更新

Preparing and configuring webpack

Our browser requests an index.html file when accessing our application. It specifies all of the files that are required to run our application. We need to create the index.html, which we serve as the entry point of our application:

  1. Create a separate directory for our index.html file:
mkdir public
touch index.html
  1. Then, save this inside index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Graphbook</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

As you can see, no JavaScript is loaded here. There is only div with the root id. This div tag is the DOMNode in which our application will be rendered by ReactDOM.

So, how do we get React up and running with this index.html file?

To accomplish this, we need to use a web application bundler. It prepares and bundles all our application assets. All of the required JavaScript files and node_modules are bundled and minified; SASS and SCSS preprocessors are transpiled to CSS as well as being merged and minified.

To name a few application bundler packages, there are webpack, Parcel, and Gulp. For our use case, we will use webpack. It is the most common module bundler, which has a large community surrounding it. To bundle our JavaScript code, we need to install webpack and all of its dependencies as follows:

npm install --save-dev @babel/core babel-eslint babel-loader @babel/preset-env @babel/preset-react clean-webpack-plugin css-loader eslint file-loader html-webpack-plugin style-loader url-loader webpack webpack-cli webpack-dev-server @babel/plugin-proposal-decorators @babel/plugin-proposal-function-sent @babel/plugin-proposal-export-namespace-from @babel/plugin-proposal-numeric-separator @babel/plugin-proposal-throw-expressions @babel/plugin-proposal-class-properties

This command adds all of the development tools to devDependencies in the package.json file that are needed to allow the bundling of our application. They are only installed in a development environment and are skipped in production.

As you can see in the preceding code, we also installed eslint, which goes through our code on the fly and checks it for errors. We need an eslint configuration file, which, again, we install from https://npmjs.com. The following handy shortcut installs the eslint configuration created by the people at Airbnb, including all peer dependencies. Execute it straight away:

npx install-peerdeps --dev eslint-config-airbnb

Create a .eslintrc file in the root of your project folder to use the airbnb configuration:

{
"extends": ["airbnb"],
"env": {
"browser": true,
"node": true
},
"rules": {
"react/jsx-filename-extension": "off"
}
}

In short, this .eslinrc file loads the airbnb config; we define the environments where our code is going to run, and we turn off one default rule.

The react/jsx-filename-extension rule throws a warning when using JSX syntax inside a file not ending in .jsx. Our files will end with .js, so we enable this rule.

If you aren't already aware, setting up webpack can be a bit of a hassle, There are many options that can interfere with each other and lead to problems when bundling your application. Let's create a webpack.client.config.js file in the root folder of your project.

Enter the following:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const buildDirectory = 'dist';
const outputDirectory = buildDirectory + '/client';
module.exports = {
mode: 'development',
entry: './src/client/index.js',
output: {
path: path.join(__dirname, outputDirectory),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
devServer: {
port: 3000,
open: true
},
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [path.join(__dirname,
buildDirectory)]
}),
new HtmlWebpackPlugin({
template: './public/index.html'
})
]
};

The webpack configuration file is just a regular JavaScript file in which you can require node_modules and custom JavaScript files. This is the same as everywhere else inside Node.js. Let's quickly go through all of the main properties of this configuration. Understanding these will make future custom webpack configs much easier. All of the important points are explained below:

  • HtmlWebpackPlug: This automatically generates an HTML file that includes all of the webpack bundles. We pass our previously created index.html as a template.
  • CleanWebpackPlugin: This empties all of the provided directories to clean old build files. The cleanOnceBeforeBuildPatterns property specifies an array of folders which are cleaned before the build process is started.
  • The entry field tells webpack where the starting point of our application is. This file needs to be created by us.
  • The output object specifies how our bundle is called and where it should be saved. For us, this is dist/client/bundle.js.
  • Inside module.rules, we match our file extensions with the correct loaders. All JavaScript files (except those located in node_modules) are transpiled by Babel, specified by babel-loader , so that we can use ES6 features inside our code. Our CSS gets processed by style-loader and css-loader. There are many more loaders for JavaScript, CSS, and other file extensions available.
  • The devServer feature of webpack enables us to run the React code directly. It includes hot reloading code in the browser without rerunning a build or refreshing the browser tab.
If you need a more detailed overview of the webpack configuration, have a look at the official documentation here: https://github.com/webpack/docs/wiki/configuration.

With this in mind, let's move on. We are missing the src/client/index.js file from our webpack configuration, so let's create it as follows:

mkdir src/client
cd src/client
touch index.js

You can leave this file empty for the moment. It can be bundled by webpack without content inside. We are going to change it later in this chapter.

To spin up our development webpack server, we add a command to package.json , which we can run using npm.

Add this line to the scripts object inside package.json:

"client": "webpack-dev-server --devtool inline-source-map --hot --config webpack.client.config.js"

Now execute npm run client in your console, and watch how a new browser window opens. We are running webpack-dev-server with the newly created configuration file.

Sure, the browser is still empty, but if you inspect the HTML with Chrome DevTools, you can see that we have already got a bundle.js file and our index.html file was taken as a template.

We have accomplished including our empty index.js file with the bundle and can serve it to the browser. Next, we'll render our first React component inside our template index.html file.