前置条件
node >= 14,安装了yarn
一. 初始化项目及基础配置
1. create-react-app初始化项目
npx create-react-app my-app
注意: 第一行的 npx 不是拼写错误 —— 它是 [npm 5.2+ 附带的 package 运行工具]
你也可以基于以下模板初始化项目:
结合redux初始化项目 npx create-react-app my-app --template redux
结合typescript初始化项目 npx create-react-app my-app --template typescript
2. 目录结构调整
我们将目录结构调整为以下结构,方便项目管理:
└── /src
├── /pages // 页面存放处,相当于vue2的views
├── /src index.js
└── App.js
├── /components // 公共组件
├── /api // 后台接口地址
├── /mock // 模拟数据
├── /store // 状态管理,同vue2功能一致
├── /features 状态配置
└── index.js
├── /router // 路由配置
├── /utils // 常量、工具函数等
└── /assets // 静态资源,如图片
调整后的文件目录结构如下显示:
更新./index.js的App和store的引入路径
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
// 更改store的引入路径
import { store } from './store';
// 更改App的引入路径
import App from './pages/App';
import reportWebVitals from './reportWebVitals';
import './index.css';
更新完成后,启动一下服务,验证配置是否成功。
3. 安装Ant Design
安装Ant Design,简单写几个页面,方便后面添加路由测试页面
yarn add antd
无任何报错信息提示,表示安装成功:
4. 配置路由
接下来我们配置项目路由,相当于vue技术栈的vue-router,不同的是vue的生态vue-cli已将这些内置好,普通用户只需要使用即可,而react需要自行配置~~
官网文档react-router已更新至6.3.0,本文将参照官方文档配置。新项目路由配置参考: github.com/remix-run/r…
安装react-router 6至项目中
npm install react-router-dom@6
出现以下提示,表示安装成功:
修改./index.js文件如下所示:
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import { store } from './store/store';
import App from './pages/App';
import reportWebVitals from './reportWebVitals';
import './index.css';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
完成以上配置后,运行yarn start验证项目正常运行,代表路由加载成功。
配置简单的路由
接下来我们简单配两个路由,熟悉一下react-router6的新用法,在src/pages/login/Index.js中写入如下代码:
import { Link } from "react-router-dom";
import { Button } from 'antd';
function Login() {
return (
<div className="login">
<h1>Welcome to React Router!</h1>
<Button type="primary">登录</Button>
<br/>
<Link to="/">首页</Link>
</div>
);
}
export default Login;
./pages/App.js改写如下:
import React from 'react';
import { Routes, Route, Link } from "react-router-dom";
import Login from './login/Index';
import './App.css';
function Home() {
return (
<>
<main>
<h2>Welcome to the homepage!</h2>
<p>You can do this, I believe in you.</p>
</main>
<nav>
<Link to="/login">登录</Link>
</nav>
</>
);
}
function App() {
return (
<div className="App">
<header className="App-header">
<Routes>
<Route path="/" element={<Home />} />
<Route path="login" element={<Login />} />
</Routes>
</header>
</div>
);
}
export default App;
修改后,启动服务,验证是否配置成功:
5. 设置路径别名
我们需要对 create-react-app 的默认配置进行自定义,create-react-app官方脚手架默认将webpack配置隐藏起来了,你也可以使用 create-react-app 提供的 yarn run eject 命令将所有内建的配置暴露出来,只是这个操作不可逆。
在这里我们使用 craco。 对create-react-app 的默认配置进行自定义,安装craco
yarn add @craco/craco --save
在项目根目录创建craco.config.js
/* craco.config.js */
const path = require('path');
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, './src') // 路径别名
}
}
};
修改 package.json 里的 scripts 属性将package.json的启动改为craco启动:
"start": "craco start",
"build": "craco build",
"test": "craco test",
配置完成后,启动一下服务,验证配置是否正确。
接下来我们继续尝试别名是否配置成功,增加了craco.config.js别名配置后,启动服务明显慢一点了
const path = require('path');
module.exports = {
webpack: { // webpack配置
// 配置别名
alias: { // 设置别名是为了让后续引用的地方减少路径的复杂度
"@": path.resolve("src"),
"@utils": path.resolve("src/utils"),
}
}
}
再次启动服务,验证别名配置是否成功。
6. 配置后端接口请求axios
接下来我们写个简单的页面,配置后端请求服务, 编写登录页面如下:
import React from 'react';
import { Form, Input, Button } from 'antd';
export default function LoginForm(){
const onFinish = (values) => {
console.log({values})
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
return (
<Form
name="basic"
labelCol={{
span: 4,
}}
wrapperCol={{
span: 20,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
requiredMark={false}
colon={false}
>
<Form.Item
label="用户名"
name="username"
rules={[
{
required: true,
message: '请输入用户名!',
},
]}
>
<Input className='login-input' size='large'/>
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[
{
required: true,
message: '请输入密码!',
},
]}
>
<Input.Password className='login-input' size='large'/>
</Form.Item>
<Form.Item
label=" "
>
<Button type="primary" htmlType="submit" className='btn-login' block>
登录
</Button>
</Form.Item>
</Form>
);
}
index.css文件引入如下语句:
@import '~antd/dist/antd.css';
启动服务,刷新显示页面如下:
接下来我们需要向后端发送请求,安装axios:
yarn add axios --save-dev
安装成功后,在utils下新建request.js
import axios from 'axios';
const service = axios.create({
// baseURL: '',
timeout: 1000,
headers: {'Content-Type': 'application/json;charset=utf-8'}
});
// 添加请求拦截器
service.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default service;
设置api/user.js文件:
/**
* 用户
*/
import request from "@/utils/request";
// 登录
export const fetchLogin = (params) => {
return request({
url: "/sys/manager/login",
method: "post",
data: params,
});
};
7. 配置反向代理,发送请求
设置craco.config.js如下:
const path = require('path');
module.exports = {
webpack: { // webpack配置
alias: { // 设置别名方便文件引用
"@": path.resolve("src"),
"@utils": path.resolve("src/utils"),
}
},
devServer: {
// 配置代理解决跨域问题
proxy: {
'/sys': {
target: 'http://1xx.xx.xx.xx:8080', // 要请求的服务端目标IP地址
changeOrigin: true, // 默认值:false 是否需要跨域
// logLevel: 'debug', // 查看代理请求的真实地址
}
}
}
}
修改login/index.js文件
import React from 'react';
import { Form, Input, Button } from 'antd';
import { fetchLogin } from '@/api/user'
export default function LoginForm(){
const onFinish = (values) => {
console.log({values})
fetchLogin(values).then(response => {
console.log({response})
})
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
return (
<Form
name="basic"
labelCol={{
span: 4,
}}
wrapperCol={{
span: 20,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
requiredMark={false}
colon={false}
>
<Form.Item
label="用户名"
name="username"
rules={[
{
required: true,
message: '请输入用户名!',
},
]}
>
<Input className='login-input' size='large'/>
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[
{
required: true,
message: '请输入密码!',
},
]}
>
<Input.Password className='login-input' size='large'/>
</Form.Item>
<Form.Item
label=" "
>
<Button type="primary" htmlType="submit" className='btn-login' block>
登录
</Button>
</Form.Item>
</Form>
);
}
点击登录,接口正常访问,登录接口调用成功。
5. 配置less编译
安装如下语句:
yarn add -D less less-loader
yarn add craco-less
index.css改为index.less,并更改如下:
@import '~antd/dist/antd.less';
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
./index.js的index.css文件引入更改如下:
import './index.less';
craco.config.js更改如下:
const path = require('path');
const CracoLessPlugin = require('craco-less');
module.exports = {
webpack: { // webpack配置
alias: { // 设置别名方便文件引用
"@": path.resolve("src"),
"@utils": path.resolve("src/utils"),
},
module: {
rules: [
{
test: /\.less$/i,
use: [
// compiles Less to CSS
"style-loader",
"css-loader",
"less-loader",
],
},
],
},
},
devServer: {
// 配置代理解决跨域问题
proxy: {
'/sys': {
target: 'http://xxx:9098', // 要请求的服务端目标IP地址
changeOrigin: true, // 默认值:false 是否需要跨域
}
}
},
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
javascriptEnabled: true,
},
},
},
},
],
};
完成了基本的页面编写了,下一步我们配置状态管理,将需要统一获取的数据缓存到仓库中。
8. 安装redux
Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。它包含了 Redux 核心,并包含我们认为对于构建 Redux 应用必不可少的软件包和功能。Redux Toolkit简化了大多数 Redux 任务,防止了常见错误,使编写 Redux 应用程序更加容易。
yarn add @reduxjs/toolkit react-redux
接下来我们基于redux配置一个全局字典,./store/index.js代码如下:
import { configureStore } from '@reduxjs/toolkit';
import dictReducer from './features/platform/dictSlice';
export const store = configureStore({
reducer: {
dictionaryInfo: dictReducer,
},
})
./store/features/platform/dictSlice.js代码如下:
import { createSlice } from '@reduxjs/toolkit'
import { fetchAreas } from '@/api/common/dictionary'
const initialState = {
areasDict: [], // 字典
}
export const dictSlice = createSlice({
name: 'user',
initialState,
reducers: {
initAreasDict: (state, action) => {
state.areasDict = action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { initAreasDict } = dictSlice.actions
export const AreasDictAsync = (params) => (dispatch) => {
fetchAreas(params).then(response => {
dispatch(initAreasDict(response.list))
})
}
export default dictSlice.reducer
以上一个字典的缓存store就写好了,初始化时,在页面上调用
import { useDispatch } from 'react-redux';
const dispatch = useDispatch()
dispatch(AreasDictAsync())
在需要获取相应的值的页面:
import { useSelector } from 'react-redux';
const { areasDict } = useSelector(state => state.dictionaryInfo)
此时就能成功获取areasDict的值了。
9. 环境变量
create-react-app和vue-cli一样,内置了环境变量,当我们需要配置生产环境的访问地址时,这个时候就需要环境变量,在根目录上创建以下三个文件
.env: 公共环境变量.env.development:本地环境.env.production: 生产环境
.env.development文件配置如下:
REACT_APP_NODE_ENV = 'development'
REACT_APP_ENV = 'development'
REACT_APP_BASE_URL = ''
.env.production文件配置如下:
REACT_APP_NODE_ENV = 'production'
REACT_APP_ENV = 'production'
REACT_APP_BASE_URL = '后端服务地址'
然后我们配置下axios的baseURL,这样就可以根据环境调用不同的后端服务地址了,
./src/utils/request.js文件的baseURL设置如下:
import axios from 'axios';
const service = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
timeout: 30000,
headers: { 'Content-Type': 'application/json;charset=utf-8' }
});
这样,当我们运行yarn build时,取的就是.env.production的配置了。
重点:关于反向代理与环境变量的配置说明
发现有朋友使用这个文档配置反向代理和环境变量的时候,两者设置有重叠的地方,导致本地服务启动的时候代理设置不成功。其中.env.development文件中设置REACT_APP_BASE_URL = '',记住了此处为空字符串。
当REACT_APP_BASE_URL = ''时,而又配置了代理,此时所有的服务端请求前端是走的代理服务器的地址,即craco.config.js文件中此处target指向的地址:
其中/sys是代理代理/sys前缀的IP地址,如果需要代理所有的地址,设置为/。
检查代理是否设置成功,运行后查看向后端服务请求的IP地址:
当前缀是我们本地服务的地址时,即代理配置成功。
至此,我们已经完成了一个后台管理项目需要基本的配置,后面的就是在配置过程中的一些爬坑了。
二. 爬坑小集锦
在安装过程中遇到的报错及处理
安装antd报错
运行yarn add antd报错如下
yarn add v1.22.10
info No lockfile found.
[1/4] Resolving packages...
warning @testing-library/jest-dom > css > source-map-resolve@0.6.0: See https://github.com/lydell/source-map-resolve#deprecated
warning react-scripts > @svgr/webpack > @svgr/plugin-svgo > svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x.
[2/4] Fetching packages...
error eslint@8.11.0: The engine "node" is incompatible with this module. Expected version "^12.22.0 || ^14.17.0 || >=16.0.0". Got "14.16.0"
error Found incompatible module.
info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.
这是在说我的node版本太低的,检查一下我的node版本
$ node -v
v14.16.0
我们升级一下node。
更新node版本
上官网下载最新的node包,我的是windows系统的,根据提示完成node安装后,再次查看node版本,已更新到v16.14.0
如无任何报错,表示安装成功。
安装craco报错cb() never called!
安装npm install @craco/craco --save报错如下:
$ npm install @craco/craco --save
npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I'll try to do my best with it!
npm ERR! cb() never called!
npm ERR! This is an error with npm itself. Please report this error at:
npm ERR! <https://npm.community>
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\jean\AppData\Roaming\npm-cache\_logs\2022-03-21T02_11_15_372Z-debug.log
删除package-lock.json和yarn-err文件,再次安装,可以了。
安装craco报错ERESOLVE unable to resolve dependency tree
安装craco报错
$ npm install @craco/craco --save
$ npm install @craco/craco --save
npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: test-attestation@0.1.0
npm ERR! Found: react-scripts@5.0.1
npm ERR! node_modules/react-scripts
npm ERR! react-scripts@"5.0.1" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react-scripts@"^4.0.0" from @craco/craco@6.4.3
npm ERR! node_modules/@craco/craco
npm ERR! @craco/craco@"*" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See C:\Users\Administrator\AppData\Local\npm-cache\eresolve-report.txt for a full report.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Administrator\AppData\Local\npm-cache\_logs\2022-06-21T02_36_30_040Z-debug-0.log
换成yarn安装即可:
yarn add @craco/craco --save
安装了craco,启动服务,报Can't resolve '@ant-design/icons'
感觉还没开始改什么东西,再启动服务就是又报错了,只好尝试把npm install --save @ant-design/icons再安装一遍又可以了
$ yarn start
yarn run v1.22.10
$ react-scripts start
Starting the development server...
(node:14564) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:14564) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
Failed to compile.
Module not found: Error: Can't resolve '@ant-design/icons' in 'D:\homework\sports-web\src\pages\home'
assets by path static/js/*.js 4.7 MiB
asset static/js/bundle.js 4.69 MiB [emitted] (name: main) 1 related asset
asset static/js/node_modules_web-vitals_dist_web-vitals_js.chunk.js 6.93 KiB [emitted] 1 related asset
asset index.html 1.68 KiB [emitted]
asset asset-manifest.json 458 bytes [emitted]
cached modules 5.96 MiB (javascript) 31.5 KiB (runtime) [cached] 1074 modules
重新安装了图标包,启动服务,服务启动成功,而且能看到是caro启动的
启动服务时停留在Starting the development server...
运行yarn start后,无报错提示,一直停留在Starting the development server... 出现这种提示,请检查你的node版本是不是原服务安装的版本,前面为了兼容多项目不同的node版本依赖,引入了nvm管理node版本(具体使用请查看 Nodejs多版本管理工具nvm)。出现以下提示后,node版本切换为16.14.0即可正常启动服务。
$ yarn start
yarn run v1.22.10
$ react-scripts start
[HPM] Proxy created: / -> http://120.xx.8.xx:8080
(node:1508) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:1508) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
Starting the development server...
端口号被占用
$ yarn start
yarn run v1.22.17
$ craco start
Something is already running on port 3000.
Done in 21.22s.
启动[任务管理器],在进程中关闭git bash 和 node 进程,然后重新打开git bash,再次进入目录后 npm start发现成功。