This scaffold is suitable for the following projects:

  1. Use React as a library to build the user interface.
  2. Use TypeScript for type checking.
  3. Use the React Router for route management.
  4. Use Redux for state management.
  5. Write styles using Sass.
  6. Use Eslint to keep code styles consistent.
  7. Unit testing using Jest, etc.

The tool for packaging code uses Webpack and Bable to compile JavaScript to a browser-compatible version.

The following articles:

Use Webpack to build scaffolding for React projects (1-React, TypeScript)

Use Webpack to build a React scaffolding (2-React Router, Redux, Sass)

Scaffolding React projects with Webpack, etc. (3-ESLint, Jest)

Build a Scaffolding for React projects using Webpack, etc. (4 – Optimization)

Use Webpack to build a React development environment.

Build a scaffolding for React projects using Webpack and others (5-Scaffolding). The scaffolding’s function is to create a project using instructions that retrieve the code written in the previous articles.

Initialize the

Create a folder and go to the file directory:

mkdir simple-scaffold && cd simple-scaffold
Copy the code

Initialization project:

npm init -y
Copy the code

-y means that all the options during the initialization of the project are selected by default. After executing the command, there is a package.json file in the file directory, which currently looks like this.

{
  "name": "simple-scaffold".// Project name, the default is the name of the folder where the project is located
  "version": "1.0.0".// Version number, used when releasing packages
  "description": "".// description, used in NPM search
  "main": "index.js".// The main entry of the program, if the user publishes the package and requires the package, then the content returned by require is the content exported in index.js
  "scripts": { // The contents of scripts are configured script commands
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [].// Program keyword, used in NPM search
  "author": ""."license": "ISC"
}
Copy the code

Create a SRC folder and create index.js and index.html files in the SRC folder:

mkdir src && touch src/index.js src/index.html
Copy the code

The contents of the index.js file are as follows:

window.onload = function () {
  var root = document.getElementById('root');
  var content = document.createElement('h1');
  content.textContent = 'Build a scaffolding for React project using Webpack etc.';
  root.appendChild(content);
}
Copy the code

The contents of the index.html file are as follows:

<! DOCTYPEhtml>
<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>simple-scaffold</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
Copy the code

Webpack configuration

Install webpack and webpack-CLI by executing the following statement in the project root directory:

npm i --save-dev webpack webpack-cli
Copy the code

Create a folder and go to the file directory:

mkdir config && cd config
Copy the code

Create three files under the config folder to place the generic WebPack configuration, the development environment webPack configuration, and the production environment Webpack configuration:

touch webpack.common.js webpack.dev.js webpack.prod.js
Copy the code

Package HTML and JS files

Install html-loader, html-webpack-plugin, clean-webpack-plugin:

npm i --save-dev html-webpack-plugin html-loader clean-webpack-plugin
Copy the code

Webpack.com mon. Js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'none'.entry: {
    app: path.resolve(__dirname, '.. /src/index.js'),},output: {
    filename: '[name].[hash].js'.path: path.resolve(__dirname, '.. /dist'),
    publicPath: '/',},module: {
    rules: [{test: /\.html$/,
        exclude: /[\\/]node_modules[\\/]/,
        loader: 'html-loader',}},plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'simple-scaffold'.template: path.resolve(__dirname, '.. /src/index.html'),
      filename: 'index.html',})],}Copy the code

Mode indicates the packaging mode of webpack. Webpack is optimized according to different configuration modes. Mode: ‘None’ in the code means that no optimizations are used.

Entry specifies the entry to the package, and WebPack generates a dependency graph from the file that entry specifies. When Entry is defined as an object, the key value is the name of the output file.

Filename in output defines the filename of the output, and hash in [name].[hash].js is the hash value of the module identifier. PublicPath public path generated by the specified file, such as the above code generated HTML file, the path of the introduction of js for SRC = “. / app. 17934 a47c82529729b11. Js “. If the publicPath: ‘/’ publicPath instead: ‘/ test/’, so the introduction of the generated js file path for SRC = “/ test/app. 17934 a47c82529729b11. Js”.

Webpack can only package JavaScript files without loader, and it can handle all kinds of files (modules) with Loader. The html-loader in the above code is specifically used to parse HTML files into strings, and the html-webpack-plugin is used to generate an HTML file. The clean-webpack-plugin is used to clean up the last packed file.

Add to package.json:

  "scripts": {
    "build": "webpack --config ./config/webpack.common.js"
  },
Copy the code

So when performing NPM run the build, is equivalent to perform webpack — config. / config/webpack.com mon. Js, — config webpack specified configuration file.

Perform NPM run after build pack file, check the packaging good app. 17934 a47c82529729b11. Js file, found it already contains the entry file SRC/index, js code.

/ * * * / (function(module.exports) {

window.onload = function () {
  var root = document.getElementById('root');
  var content = document.createElement('h1');
  content.textContent = 'Build a scaffolding for React project using Webpack etc.';
  root.appendChild(content);
}

/ * * * / })
Copy the code

The packaged HTML file introduces the packaged JS file:

.<script type="text/javascript" src="./app.17934a47c82529729b11.js"></script></body>
</html>
Copy the code

When you open the generated HTML file in your browser, you can see that the page says “Build a scaffolding for the React project using Webpack etc.”

Distinguish between development/production environments

The packed JS files and HTML files are not compressed, but the production environment needs to keep the code size as small as possible, so the code needs to be compressed in the production environment. Development environments typically use devServer to configure webpack-dev-server. Webpack-dev-server provides a server that can access packaged files on the local server (webpack-dev-server puts file contents in memory, The content is not written to a file, and the production environment does not require Webpack-dev-server. After packaging file if an error in the execution, only after packaging file (app. 17934 a47c82529729b11. Js) positioning to the wrong place, cannot locate to source (SRC/index. Js), So the development environment must use Devtool or other means (such as SourceMapDevToolPlugin) to map the packaged code to the pre-packaged code, the source map is optional in the production environment. In summary, the development and production environments are so different that they need to be configured separately.

Cross – env installation:

npm i --save-dev cross-env
Copy the code

Modify the scripts section of the package.json file:

"scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --config ./config/webpack.dev.js"."build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"."build:dev": "cross-env NODE_ENV=development webpack --config ./config/webpack.dev.js"
},
Copy the code

Build :dev is set to make it easy to see the content of the code packaged in an uncompressed way.

Cross-env is used to set and use environment variables across platforms. Different operating systems do not set environment variables in the same way. For example, export NODE_ENV=development is used on macs. On Windows, set NODE_ENV=development. Cross-env NODE_ENV=development The environment variable NODE_ENV can be set to development successfully.

Install webpack-merge, webpack-dev-server, webpack-merge to merge webpack configuration, webpack-dev-server provides a server.

npm i --save-dev webpack-merge webpack-dev-server
Copy the code

Modify the contents of webpack.common.js, webpack.dev.js, and webpack.prod.js slightly:

Webpack.com mon. Js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
constdevMode = process.env.NODE_ENV ! = ='production';

module.exports = {
  entry: {
    app: path.resolve(__dirname, '.. /src/index.js'),},output: {
    filename: devMode ? '[name].js' : '[name].[hash].js'.path: path.resolve(__dirname, '.. /dist'),
    publicPath: '/',},module: {
    rules: [{test: /\.html$/,
        exclude: /[\\/]node_modules[\\/]/,
        loader: 'html-loader'.options: {
          minimize: !devMode,
        },
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'simple-scaffold'.template: path.resolve(__dirname, '.. /src/index.html'),
      filename: 'index.html',})],}Copy the code

Deleted mode, mode: ‘none’, and changed the value of publicPath: ‘/’, (‘ publicPath: ‘./’).

When the process. The env. NODE_ENV! == ‘production’ indicates a development environment that does not minimize HTML compression:! DevMode,. In the following webpack.prod.js file, mode is set to production. When mode is set to production, process.env.node_env is automatically set to production. Process. Env.NODE_ENV =production; cross-env =production; Otherwise, process.env.NODE_ENV is undefined in webpack.common.js.

In the production environment, if the packaged file is app.js, when using the CDN, the user requests the path app.js, which may fetch the cached data on the server instead of the latest data. Therefore, the packaged file requires a hash value, which will change when the file content changes. Make sure the user gets the correct file. However, the development environment does not have this requirement, so the development environment filename does not need to use hash (filename: devMode? ‘[name].js’ : ‘[name].[hash].js’,).

Webpack. Dev. Js:

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development'.devtool: 'inline-source-map'.devServer: {
    open: true.hot: true,}});Copy the code

Setting mode to development does something for the development environment, such as setting process.env.node_env to development.

With devtool configured as inline-source-map, the source map is added to the packaged file as a DataUrl. After executing NPM run build:dev to pack the file, you can see that the packed JS file has the following code:

//# sourceMappingURL=data:application/json; charset=utf-8; base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2...
Copy the code

When configuring webpack-dev-server in devServer, open means that the default browser is opened. When setting hot to true, hot-module-replacement is used to change the file content, and the corresponding module (not the entire application) is reloaded. NPM start starts the service and opens the default browser, where you see the words “use Webpack, etc., to scaffolding the React project.”

webpack.prod.js:

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'production'.devtool: 'source-map'.output: {
    publicPath: '/',}});Copy the code

When mode is set to production, Webpack sets process.env.node_env to production. The Terser-webpack-plugin is automatically configured, which uses Terser to filter out unwanted content in JS, including removing comments and whitespace from JS.

Setting devtool to source-map generates a separate Source map file with a line comment in the packaged JS indicating where to find it. Execute the NPM run build package code:

Asset Size Chunks Chunk Names app. Da19f0220d534a1d34d1. Js 1.15 KiB 0 [emitted] [immutable] app App. Da19f0220d534a1d34d1. Js. Map 5.05 KiB 0 [emitted] [dev] app index. The HTML 333 bytes (emitted)Copy the code

Can see there is a. The map file, and the app. Da19f0220d534a1d34d1. Have the following comments in js:

//# sourceMappingURL=app.da19f0220d534a1d34d1.js.map
Copy the code

Use the React

1. Create a simple page using React.

Install react and react-dom:

npm i --save react react-dom
Copy the code

React contains only the necessary functions to define the React component. It is usually used in conjunction with the React renderer. Web applications use the React dom renderer, and native applications use the React Native renderer.

SRC contains files:

. └ ─ ─ the SRC ├ ─ ─ components │ └ ─ ─ the Header │ └ ─ ─ the Header. The js ├ ─ ─ index. The HTML ├ ─ ─ index. The js └ ─ ─ pages └ ─ ─ Home └ ─ ─ Home. JsCopy the code

Index.js file contents:

import ReactDOM from 'react-dom';
import Home from './pages/Home/Home';

ReactDOM.render(
  <Home />.document.getElementById('root'));Copy the code

The contents of the home.js file:

import Header from '@/components/Header/Header';

export default function Home() {
  return (
    <div>
      <Header userName="As a foam" />
      <h1>Use Webpack, etc., to build a scaffolding for the React project</h1>
    </div>
  );
}
Copy the code

The contents of the header. js file:

export default function Header (props) {
  return (
    <div>
      <p>{props.userName}</p>
    </div>
  );
}
Copy the code

2. Use @babel/preset- React to convert JSX syntax.

Install babel-loader, @babel/core, @babel/preset-react:

npm i --save-dev babel-loader @babel/core @babel/preset-react
Copy the code

@babel/core is the core module of Babel.

Create a.babelrc.js file in the root directory for the Babel configuration.

touch .babelrc.js
Copy the code

.babelrc.js file contents:

module.exports = {
  presets: ['@babel/preset-react']};Copy the code

Presets are a collection of plugins, so you don’t have to set them up one by one. For example, @babel/preset-react normally includes @babel/plugin-syntax-jsx, @babel/plugin-transform-react-jsx, and @babel/plugin-transform-react-di Splay-name Babel plugin.

3.Webpack configuration (webpack.mon.js) :

const webpack = require('webpack'); .module.exports = { 
  ...
  resolve: {
    extensions: ['.js'.'.jsx'].alias: {
      The '@': path.resolve(__dirname, '.. /src'),}},module: {
    rules: [{...test: /\.js(x?) $/,
        exclude: /[\\/]node_modules[\\/]/,
        loader: 'babel-loader? cacheDirectory=true',}]},plugins: [...new webpack.ProvidePlugin({
      React: 'react',})],optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors'.chunks: 'all'},},},},}Copy the code

After defining file extensions in extensions, you can import files without adding file extensions. Webpack will process files in the order defined by the extensions: [‘.js’, ‘.jsx’] and import the module import Home from ‘./pages/Home/Home’; Webpack will try to add the.js suffix to the file, and if it can’t find the file, it will try to add the.jsx suffix to the file and continue searching.

The SRC directory alias is defined as @ in alias, so that when importing other files in the file, you can use @ directly, rather than looking for the relative path of the file.

Use webpack.ProvidePlugin to define the React: ‘React’ flag that automatically finds the React module in the current directory or node_modules when the React variable is needed. This eliminates the need to use import React from ‘React’ once in every component file.

Use cacheDirectory to cache the execution results of the Loader. loader: ‘babel-loader? CacheDirectory =true’, which uses the default cache directory node_modules/. Cache /babel-loader.

SplitChunks package common modules into a single file. If splitChunks are not configured, the code will be packaged into app.hash.js, resulting in a large app.hash.js file. The longer the page takes to render to the user. The configuration in the code above packages only modules used in node_modules into a file (not modules outside node_modules).

Run NPM start to view the page.

Use the TypeScript

Install the required dependencies first:

npm i --save-dev typescript @babel/preset-typescript
npm i --save-dev @types/react @types/react-dom
Copy the code

@types/react contains the react type definition, and @types/react-dom contains the react type definition.

@babel/preset-typescript is a Babel preset used to work with typescript.

1. Modify the * *. Babelrc. Js * * :

module.exports = {
  presets: [
    '@babel/preset-react'.'@babel/preset-typescript',]};Copy the code

Preset is run from back to front. Based on the Babel configuration of the above code, @babel/preset-typescript is executed first, and then @babel/preset- React.

2. Create a tsconfig.json file in the root directory for TypeScript configuration:

{
  "compilerOptions": {
    "baseUrl": ". /"."jsx": "react"."paths": {
      "@ / *": ["src/*"]},"esModuleInterop": true,},"include": [
    "src/*"."typings/*"]}Copy the code

BaseUrl sets the base path. JSX is used to set how JSX syntax is converted to JavaScript. The esModuleInterop is true to allow import React from ‘React’, otherwise import * as React from ‘React’ must be used for modules that do not have a default export, such as React. Include sets the range of files that typescript processes.

In the previous code, we defined the react flag via webpack.ProvidePlugin to represent the React package. In TypeScript, we need to define types for react.

mkdir typings && cd typings && touch react.d.ts
Copy the code

The react.d.ts file contains the following contents:

import React from 'react';

declare global {
  const React: typeof React;
}
Copy the code

The path alias @ is defined in Webpack above, and the corresponding configuration is also required in tsconfig.json:

"baseUrl": ". /"."paths": {
  "@ / *": ["src/*"]},Copy the code

Define path aliases in Paths. The baseUrl option must be set to use the Paths option.

More Typescript configurations can be found in the tsConfig documentation.

3. The Webpack configuration

With TypeScript, the content in webpack.common.js may change as follows:

.module.exports = {
  entry: {
    app: path.resolve(__dirname, '.. /src/index.tsx'),},resolve: {
    extensions: ['.js'.'.jsx'.'.ts'.'.tsx'],... },module: {
    rules: [{...test: /(\.js(x?) )|(\.ts(x?) ) $/,
        exclude: /[\\/]node_modules[\\/]/,
        loader: 'babel-loader? cacheDirectory=true',},],}...Copy the code

4. Use the TypeScript

Change the suffix of index.js, home.js, and header. js to.tsx.

Modify the Header. The TSX

interface HeaderProps {
  userName: string;
}
export default function Header (props: HeaderProps) :JSX.Element {
  return (
    <div>
      <p>{props.userName}</p>
    </div>
  );
}
Copy the code

TSX

Property 'userName' is missing in type '{}' but required in type 'HeaderProps'.ts(2741)
Copy the code

Run NPM start to view the page.

The use of Babel

Babel transforms code into code that works in a browser. Babel is already used to parse JSX and TypeScript. However, JavaScript generated after parsing JSX and TypeScript may not run properly in some browsers, so use @babel/preset-env, @babel/preset-env to find out the desired plugin based on the preset target environment. You pass the list of plug-ins to Babel so that the target environment is configured and the rest of Babel handles it.

Install dependency: NPM I — save-dev@babel /preset-env

Modify the * *. Babelrc. Js * * :

module.exports = {
  presets: [['@babel/preset-env',
      {
        targets: '> 2% in CN and not ie <= 8 and not dead'],},'@babel/preset-react'.'@babel/preset-typescript',]};Copy the code

targets: ‘> 2% in CN and not IE <= 8 and not dead’. Targets indicates that the target environment is: browsers with more than 2% statistics in China, excluding Internet Explorer whose version number is less than 8, and excluding those browsers that are not officially maintained.

Run NPM start to see the expected page in Google Chrome.

Next: Scaffolding the React project with Webpack (2-React Router, Redux, Sass).