We all know create-react-app
and how easily we can get a React project configuration done. However, when you need some more project-specific stuff, you would definitely like to know how to change or add things.
On the other hand, it's always a good idea to understand what's happening under the hood.
This is the second part of Let's create a Webpack configuration from scratch and I'll be using the files created on that post. So go and check it out before continue reading or just clone the repo if you have a good understanding of how Webpack works.
Here you have the repository of this post: react-webpack-configuration
Feel free to use it if you feel lost at some point.
Set Up
First of all, if we are going to code a React app, we'll need React.
$ npm i react react-dom
Also, let's create an entry point and a first component. Just to have something to test with.
// src/index.js
import './sass/index.scss' // remember to import your styles in this file
import React from 'react'
import { render } from 'react-dom'
import App from './components/App'
render(<App />, document.querySelector('#app'))
Let's create a hello world.
// src/components/App.js
import React from 'react'
const App = () => <p>Hello World!</p>
export default App
Now we are ready to go.
Babel for React
We already have some babel configuration in our .babelrc
file. But it's not enough for React.
We'll need one more package.
$ npm i -D @babel/preset-react
Then, just add it to the presets
array.
// .babelrc
{
"plugins": [
"@babel/plugin-transform-runtime"
],
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
Development
As you know for the previous post, we're using two different files to take apart development and production configurations.
We've done quite a good job last time. But we need to improve something for React.
Hot Module Replacement
Hot Module Replacement, allows us to refresh just one part of the browser every time we save a file. This makes the developer experience delightful.
Let's do it.
// webpack.dev.config.js
...
const webpack = require('webpack')
module.exports = {
...
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
open: true,
hot: true
},
...
plugins: [
new webpack.HotModuleReplacementPlugin(),
...
],
}
webpack.HotModuleReplacementPlugin
does quite all the job. Remember to import webpack
and set hot
to true
in devServer
object.
Finally, we'll need to make a little refactor in the entry point.
// src/index.js
import './styles/index.scss'
import React from 'react'
import { render } from 'react-dom'
import App from './components/App'
function renderApp() {
render(<App />, document.querySelector('#app'))
}
renderApp()
if (module.hot) {
module.hot.accept('./components/App', () => renderApp())
}
Encapsulate react render function into a custom function and then tell webpack
to re-run that function every time we save the file.
Production
This is probably the worthiest part of making a custom configuration.
You can really improve performance and user experience with the following technique. However, there are a lot of things you could add here.
Dynamic Link Library
Dynamic link library is a set of libraries that hardly ever change. So we're going to pre-bundle them in order to avoid bundle them every time we run our build command. Also, the fact of having these libraries in a different file, will prevent the browser to download them over and over again.
First, we need to create a new webpack file. Yes, another one. This one is so simple and it's usually called webpack.dll.config.js
.
// webpack.dll.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
modules: ['react', 'react-dom'],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].dll.js',
library: '[name]',
},
mode: 'production',
plugins: [
new webpack.DllPlugin({
name: '[name]',
path: path.join(__dirname, '[name]-manifest.json'),
}),
],
}
You might be able to understand almost everything. But there are a couple of weird things...
Why is the entry point receiving and array of strings? Well, those strings are the names of the libraries. And those libraries are the ones that we are compiling.
In this case we're only using react
and react-dom
because it's a very simple example. But go ahead and install react-router
, redux
or any other library that you want and add them to the modules array.
Output
and mode
are quite simple. We've been using them for a while and they don't have nothing new in this case.
Finally, we are using a new plugin. It is part of the Webpack core and it help us creating a manifest. This manifest, is going to tell Webpack not to re-compile this libraries that we've already compiled.
That next step, is the build
command and in order to make that happen, we need to add a plugin.
// webpack.config.js
...
const webpack = require('webpack')
module.exports = {
...
plugins: [
...
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['**/main.*']
}),
new webpack.DllReferencePlugin({
manifest: require('./modules-manifest.json')
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, "dist/js/*.dll.js"),
outputPath: "js",
publicPath: "http://localhost:3001/js/"
})
]
}
This one is again part of the Webpack core and it reads the output of DllPlugin
and prevents Webpack from compiling our libraries again.
On the other hand, I've changed the pattern of CleanWebpackPlugin
to avoid deleting our modules.dll.js
file.
Finally, we're using a new plugin which links our modules.dll.js
to our html file automatically.
$ npm i -D add-asset-html-webpack-plugin
React code and Webpack Docs
We've finished. But if you want to continue improving your user and development experience, I leave you a couple of things to look at.
You can make a lot of difference in user experience if you take care of your React code. These are a couple of React techniques related with performance:
PWAs could be complicated at the beginning but they're really useful and allow you to have awesome features like offline support, notifications, offline analytics, shortcut icon from smartphones and computers, etc.
Also, don't forget to check out Webpack's documentation. It's plenty of interesting options to improve either your experience or the users one.
Top comments (0)