Build React scaffolding step by step using Webpack5 from scratch
Webpack5-React
Initialize the
Create a folder and go to:
mkdir webpack5-react && cd webpack5-react
Copy the code
Initialization package. Json
yarn init
Copy the code
Webpack installation is related
yarn add webpack webpack-cli -D
Copy the code
Creating a project structure
webpack5-react
|- package.json
|- /dist
|- index.html
|- /scripts
|- webpack.config.js
|- index.html
|- /src
|- index.js
Copy the code
src/index.js
document.getElementById('root').innerText = 'Hello React';
Copy the code
index.html
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, Initial-scale =1.0"> <title>webpack5-react</title> </head> <body> <div id="root"></div> <script src="./src/index.js"></script> </body> </html>Copy the code
dist/index.html
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, ">< title>webpack5-react</title> </head> <body> <div id="root"></div> <script SRC ="main.js"></script> </body> </html>Copy the code
scripts/webpack.config.js
const path = require('path'); module.exports = { mode: "development", entry: "./src/index.js", output: { filename: '[name].js', path: path.resolve(__dirname, '.. /dist'), }, };Copy the code
package.json
{/ /... "scripts": { "build": "webpack --config scripts/webpack.config.js" }, // ... }Copy the code
Then the root terminal type: YARN Build Open up the index.html in the dist directory in your browser. If all is well, you should see the following text: ‘Hello React’
Install Babel related
yarn add @babel/cli @babel/core babel-loader @babel/preset-env @babel/preset-react -D
Copy the code
scripts/webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.(js|jsx|tsx)$/,
loader: "babel-loader",
exclude: /node_modules/,
},
],
},
};
Copy the code
.babelrc
Add the.babelrc file to the root directory:
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
Copy the code
style
yarn add style-loader css-loader less less-loader postcss-loader autoprefixer -D
Copy the code
scripts/webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.(css|less)$/,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
options: {
importLoaders: 1,
},
},
{
loader: "less-loader",
lessOptions: {
javascriptEnabled: true,
},
},
{
loader: "postcss-loader",
options: {
plugins: [require('autoprefixer')]
}
}
]
}
],
},
};
Copy the code
Picture font
yarn add file-loader -D
Copy the code
scripts/webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|jpeg)$/,
loader: 'file-loader'
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader'
}
],
},
};
Copy the code
HTML
yarn add html-webpack-plugin -D
Copy the code
scripts/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
title: 'webpack5-react',
template: './index.html'
})
],
};
Copy the code
index.html
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, Initial scale=1.0"> <title>webpack5-react</title> </head> <body> <div id="root"></div> </body> </ HTML >Copy the code
Development services
yarn add webpack-dev-server -D
Copy the code
scripts/webpack.config.js
const path = require("path"); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // ... devServer: { contentBase: path.resolve(__dirname, ".. /dist"), hot: true, historyApiFallback: true, compress: true, }, };Copy the code
package.json
{/ /... "scripts": { "start": "webpack serve --mode=development --config scripts/webpack.config.js" }, // ... }Copy the code
Clean up the dist
yarn add clean-webpack-plugin -D
Copy the code
Scripts/webpack. Config. Js or output
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// ...
plugins: {
new CleanWebpackPlugin()
}
};
Copy the code
output
module.exports = { // ... output: { filename: '[name].js', path: path.resolve(__dirname, ".. /dist"), clean: true } }Copy the code
The environment variable
yarn add cross-env -D
Copy the code
package.json
{/ /... "scripts": { "start": "cross-env NODE_ENV=development webpack serve --mode=development --config scripts/webpack.config.js", "build": "cross-env NODE_ENV=production webpack --mode=production --config scripts/webpack.config.js" }, // ... }Copy the code
Use the dotenv plugin to read different. Env files
const Dotenv = require('dotenv-webpack');
const env = process.env.NODE_ENV;
plugins: [
// ...
new Dotenv({
path: env === 'production' ? './.env.prod' : './.env'
})
]
Copy the code
Install the React
yarn add react react-dom @types/react @types/react-dom
Copy the code
.babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
Copy the code
src/App.jsx
In the SRC directory, add app. JSX:
import React from 'react'
const App = () => {
return (
<h1>Hello, World! </h1>
)
}
export default App;
Copy the code
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App />, document.getElementById('root'))
Copy the code
React Router
Install dependencies
yarn add react-router @types/react-router history
Copy the code
src/About.jsx
Add the about.jsx file to the SRC directory:
import React from 'react'
const About = () => {
return (
<h1>About</h1>
)
}
export default About;
Copy the code
src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { Router, Route, Link } from "react-router";
import { createBrowserHistory } from "history";
import App from "./App.jsx";
import About from "./About.jsx";
ReactDOM.render(
<Router history={createBrowserHistory()}>
<Route path="/" component={App} />
<Route path="/about" component={About} />
</Router>,
document.getElementById("root")
);
Copy the code
MobX
Install dependencies
yarn add mobx mobx-react babel-preset-mobx -D
Copy the code
.babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react", "mobx"]
}
Copy the code
src/store.js
Create store.js in the SRC directory
import { observable, action, makeObservable } from "mobx";
class Store {
constructor() {
makeObservable(this);
}
@observable
count = 0;
@action("add")
add = () => {
this.count = this.count + 1;
};
@action("reduce")
reduce = () => {
this.count = this.count - 1;
};
}
export default new Store();
Copy the code
index.js
import { Provider } from "mobx-react";
import Store from "./store";
// ...
ReactDOM.render(
<Provider store={Store}>
<Router history={createBrowserHistory()}>
<Route path="/" component={App} />
<Route path="/about" component={About} />
</Router>
</Provider>,
document.getElementById("root")
);
Copy the code
src/App.jsx
import React, { Component } from "react";
import { observer, inject } from "mobx-react";
@inject("store")
@observer
class App extends Component {
render() {
return (
<div>
<div>{this.props.store.count}</div>
<button onClick={this.props.store.add}>add</button>
<button onClick={this.props.store.reduce}>reduce</button>
</div>
);
}
}
export default App;
Copy the code
TypeScript
Install dependencies
yarn add typescript @babel/preset-typescript -D
Copy the code
.babelrc
{
"presets": [
// ...
"@babel/preset-typescript"
]
}
Copy the code
tsconfig.json
Add the tsconfig.json file in the root directory:
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "ES5",
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"outDir": "./dist/",
"esModuleInterop": true,
"noImplicitAny": false,
"sourceMap": true,
"module": "esnext",
"moduleResolution": "node",
"isolatedModules": true,
"importHelpers": true,
"lib": ["esnext", "dom", "dom.iterable"],
"skipLibCheck": true,
"jsx": "react",
"typeRoots": ["node", "node_modules/@types"],
"rootDirs": ["./src"],
"baseUrl": "./src"
},
"include": ["./src/**/*"],
"exclude": ["node_modules"]
}
Copy the code
src/App.jsx
App.jsx -> app.tsx about.jsx -> about.tsx
Code specification
Code verification, code formatting, Git pre-submission verification, Vscode configuration, compile verification
ESLint
Install dependencies
yarn add @typescript-eslint/parser eslint eslint-plugin-standard @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-promise -D
Copy the code
.eslintrc.js
In the root directory, add the.eslintrc.js file:
module.exports = {
extends: ["eslint:recommended", "plugin:react/recommended"],
env: {
browser: true,
commonjs: true,
es6: true,
},
globals: {
$: true,
process: true,
__dirname: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaFeatures: {
jsx: true,
modules: true,
},
sourceType: "module",
ecmaVersion: 6,
},
plugins: ["react", "standard", "promise", "@typescript-eslint"],
settings: {
"import/ignore": ["node_modules"],
react: {
version: "latest",
},
},
rules: {
quotes: [2, "single"],
"no-console": 0,
"no-debugger": 1,
"no-var": 1,
semi: ["error", "always"],
"no-irregular-whitespace": 0,
"no-trailing-spaces": 1,
"eol-last": 0,
"no-unused-vars": [
1,
{
vars: "all",
args: "after-used",
},
],
"no-case-declarations": 0,
"no-underscore-dangle": 0,
"no-alert": 2,
"no-lone-blocks": 0,
"no-class-assign": 2,
"no-cond-assign": 2,
"no-const-assign": 2,
"no-delete-var": 2,
"no-dupe-keys": 2,
"use-isnan": 2,
"no-duplicate-case": 2,
"no-dupe-args": 2,
"no-empty": 2,
"no-func-assign": 2,
"no-invalid-this": 0,
"no-redeclare": 2,
"no-spaced-func": 2,
"no-this-before-super": 0,
"no-undef": 2,
"no-return-assign": 0,
"no-script-url": 2,
"no-use-before-define": 2,
"no-extra-boolean-cast": 0,
"no-unreachable": 1,
"comma-dangle": 2,
"no-mixed-spaces-and-tabs": 2,
"prefer-arrow-callback": 0,
"arrow-parens": 0,
"arrow-spacing": 0,
camelcase: 0,
"jsx-quotes": [1, "prefer-double"],
"react/display-name": 0,
"react/forbid-prop-types": [
2,
{
forbid: ["any"],
},
],
"react/jsx-boolean-value": 0,
"react/jsx-closing-bracket-location": 1,
"react/jsx-curly-spacing": [
2,
{
when: "never",
children: true,
},
],
"react/jsx-indent": ["error", 4],
"react/jsx-key": 2,
"react/jsx-no-bind": 0,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-literals": 0,
"react/jsx-no-undef": 1,
"react/jsx-pascal-case": 0,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 2,
"react/no-danger": 0,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 0,
"react/no-direct-mutation-state": 2,
"react/no-multi-comp": 0,
"react/no-set-state": 0,
"react/no-unknown-property": 2,
"react/prefer-es6-class": 2,
"react/prop-types": 0,
"react/react-in-jsx-scope": 2,
"react/self-closing-comp": 0,
"react/sort-comp": 0,
"react/no-array-index-key": 0,
"react/no-deprecated": 1,
"react/jsx-equals-spacing": 2,
},
};
Copy the code
Perttier
Install dependencies
yarn add prettier -D
Copy the code
prettier.config.js
Add the file prettier.config.js in the root directory:
Module.exports = {// Max 100 characters printWidth: 100, // tabWidth: 2, // useTabs instead of indentation: False, // A semicolon should be used at the end of the line, // singleQuote: true, // The key of the object should be quoted only when necessary: 'as-needed', // JSX uses double quotes instead of single quotes, // trailingComma: 'None ', // braces need spacing at the beginning and end of bracketSpacing: true, // JSX tag Angle brackets need newline jsxBracketSameLine: False, // arrowParens: 'avoid', // Each file is formatted to the full contents of the file rangeStart: 0, rangeEnd: // @prettier requirePragma: false does not automatically insert @prettier insertPragma: False, / / use the default fold line standard proseWrap: 'preserve, / / according to display style decided to don't fold line htmlWhitespaceSensitivity HTML: 'CSS ', // use lf endOfLine: 'lf'};Copy the code
stylelint
Install dependencies
yarn add stylelint stylelint-config-standard stylelint-config-prettier -D
Copy the code
stylelint.config.js
In the root directory, add the stylelint.config.js file:
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
ignoreFiles: [
'**/*.ts',
'**/*.tsx',
'**/*.png',
'**/*.jpg',
'**/*.jpeg',
'**/*.gif',
'**/*.mp3',
'**/*.json'
],
rules: {
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['extends', 'ignores']
}
],
indentation: 4,
'number-leading-zero': null,
'unit-allowed-list': ['em', 'rem', 's', 'px', 'deg', 'all', 'vh', '%'],
'no-eol-whitespace': [
true,
{
ignore: 'empty-lines'
}
],
'declaration-block-trailing-semicolon': 'always',
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global']
}
],
'block-closing-brace-newline-after': 'always',
'declaration-block-semicolon-newline-after': 'always',
'no-descending-specificity': null,
'selector-list-comma-newline-after': 'always',
'selector-pseudo-element-colon-notation': 'single'
}
};
Copy the code
Lint – staged, pre – commit
Install dependencies
yarn add lint-staged prettier eslint pre-commit -D
Copy the code
package.json
{
// ...
"scripts": {
"lint": "eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx src",
"lint:css": "stylelint --aei .less .css src",
"precommit": "lint-staged",
"precommit-msg": "echo 'Pre-commit checks...' && exit 0"
},
"pre-commit": [
"precommit",
"precommit-msg"
],
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write",
"git add"
],
"*.{css,less}": [
"stylelint --fix",
"prettier --write",
"git add"
]
}
}
Copy the code
eslint-webpack-plugin
Install dependencies
yarn add eslint-webpack-plugin -D
Copy the code
scripts/webpack.config.js
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
// ...
plugins: [new ESLintPlugin()],
};
Copy the code
Webpack optimization
1. Alias optimizes the file path
- Extension: You can specify extension without adding file extensions to require or import
- Alias: Configuring aliases speeds up WebPack’s search for modules
resolve: {
modules: ['node_modules'],
extensions: ['.js', '.jsx', '.tsx', '.json'],
alias: {
'~': path.resolve(__dirname, 'src')
}
},
Copy the code