搭建 react 脚手架

基本构建(React, webpack, and Babel 7)

项目初始化

1
2
3
4
5
mkdir react-project && cd $_

mkdir -p src

npm init -y

配置 webpack

1
2
3
4
npm i webpack --save-dev

// 可能还需要 webpack-cli
npm i webpack-cli --save-dev

增加 package.json 脚本

1
2
3
"scripts": {
"build": "webpack --mode production"
}

配置 Babel

babel-loader is the Webpack loader responsible for taking in the ES6 code and making it understandable by the browser of choice.

1.babel preset env for compiling Javascript ES6 code down to ES5 (please note that babel-preset-es2015 is now deprecated).
2.babel preset react for compiling JSX and other stuff down to Javascript.

安装依赖:

1
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev

创建 .babelrc 文件:

1
2
3
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

到此我们可以开始配置一个最简单的 webpack.config.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};

创建两个示例组件

安装:

1
npm i react react-dom

创建目录结构:

1
mkdir -p src/js/components/{container,presentational}

创建一个简单的展示组件:

1
touch src/js/components/container/FormContainer.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { Component } from "react";
import ReactDOM from "react-dom";

class FormContainer extends Component {
constructor() {
super();
this.state = {
title: ""
};
}

render() {
return (
<form id="article-form">
</form>
);
}
}

export default FormContainer;

创建另一个组件:

1
touch src/js/components/presentational/Input.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React from "react";
import PropTypes from "prop-types";

const Input = ({ label, text, type, id, value, handleChange }) => (
<div className="form-group">
<label htmlFor={label}>{text}</label>
<input
type={type}
className="form-control"
id={id}
value={value}
onChange={handleChange}
required
/>
</div>
);

Input.propTypes = {
label: PropTypes.string.isRequired,
text: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired
};

export default Input;

最好使用 prop-types 确保类型准确:

1
npm i prop-types --save-dev

现在完善下前面的组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Input from "../presentational/Input.jsx";
class FormContainer extends Component {
constructor() {
super();
this.state = {
seo_title: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
render() {
const { seo_title } = this.state;
return (
<form id="article-form">
<Input
text="SEO title"
label="seo_title"
type="text"
id="seo_title"
value={seo_title}
handleChange={this.handleChange}
/>
</form>
);
}
}
export default FormContainer;

组合

webpack 默认入口为 ./src/index.js,创建该文件并引入组件:

1
import FormContainer from "./js/components/container/FormContainer.jsx";

输出的文件在:

1
./dist/main.js

增加 HTML webpack plugin

1
npm i html-webpack-plugin html-loader --save-dev

更新 webpack 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
})
]
};

创建一个HTML文件 ./src/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" >
<title>How to set up React, Webpack, and Babel</title>
</head>
<body>
<div class="container">
<div class="row mt-5">
<div class="col-md-4 offset-md-1">
<p>Create a new article</p>
<div id="create-article-form">
<!-- form -->
</div>
</div>
</div>
</div>
</body>
</html>

将 react 组件渲染到 html

修改 ./src/js/components/container/FormContainer.jsx,在底部添加:

1
2
const wrapper = document.getElementById("create-article-form");
wrapper ? ReactDOM.render(<FormContainer />, wrapper) : false;

配置开发环境

安装 webpack-dev-server

1
npm i webpack-dev-server --save-dev

package.json 增加 dev 指令:

1
2
3
4
"scripts": {
"start": "webpack-dev-server --open --mode development",
"build": "webpack --mode production"
}

添加 react-router

添加 redux

添加 redux-thunk

相关链接

A Complete React Boilerplate Tutorial — From Zero to Hero

How to build your own React boilerplate

react-boilerplate

基于webpack4.X从零搭建React脚手架

从零搭建React全家桶框架教程

Tutorial: How to set up React, webpack, and Babel 7 from scratch (2019)
Webpack 4 Tutorial: from 0 Conf to Production Mode