react 项目开发文档记录
1 初始化
使用模板安装 npx create-react-app my-app --template typescript
eslint+prettier 配置
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
prettier eslint-config-prettier eslint-plugin-prettier
.eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'prettier'],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'prettier'],
rules: {
'prettier/prettier': 'error',
},
};
.prettierrc
{
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "avoid"
}
配置 VS Code
{
"editor.formatOnSave": true,
"[javascript]": {
"editor.formatOnSave": false
},
"[typescript]": {
"editor.formatOnSave": false
},
"eslint.alwaysShowStatus": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
2 路由配置
安装依赖 yarn add react-router-dom @types/react-router-dom
在 index.ts
文件中引入 BrowserRouter
并将其包裹在 App
组件周围,以便在整个应用程序中使用路由。
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
路由嵌套 多层级路
app.ts
这里面定义了两个页面,登录和首页 ,首页里面有 Menu 和 Head 以及 Content。以及子路由 User实现常用的管理后台布局
import { Routes, Route } from 'react-router-dom';
import Login from './views/login';
import Layout from '@/views/layout';
import User from './views/users';
import UserInfo from '@/views/users/iuser-info';
import 'antd/dist/reset.css';
import './assets/style/index.scss';
function App() {
return (
<Routes>
<Route path="" element={<Login />}></Route>
<Route path="/login" element={<Login />}></Route>
<Route path="/*" element={<Layout />}>
<Route path="users" element={<User />}></Route>
</Route>
</Routes>
);
}
export default App;
layout中
import LayoutHead from './components/layout-head';
import LayoutMenu from './components/layout-menu';
import { Outlet } from 'react-router-dom';
import './index.scss';
function Layout() {
return (
<div className="layout">
<LayoutHead></LayoutHead>
<LayoutMenu></LayoutMenu>
<div className="layout-content">
<Outlet></Outlet>
{/* Outlet 作为路由出口 等同vue中的 <reoute-view></reoute-view>
渲染我们定义的User ,如果想在 User中配置子级路由,同样的嵌套操作*/}
</div>
</div>
);
}
export default Layout;
动态路由
3 sass
npm install node-sass sass-loader --save-dev
注意 node 版本兼容,直接新建 .scss
文件,引入即可使用
4 antd
yarn add antd
//app.tsx 中
import 'antd/dist/reset.css';
import { Button } from 'antd';
<div>
<Button type="primary">Button</Button>
</div>
5 redux reduxjs/toolkit
yarn add react-redux
yarn add @reduxjs/toolkit
使用 Redux Toolkit 的切片(slice)是一种更简单、更直观的方式来管理 Redux 状态
新建 src/store/reducer/user.ts
创建切片,将切片暴露出去
import { createSlice } from '@reduxjs/toolkit';
export const userSlice = createSlice({
name: 'user',
initialState: {
name: '张三',
age: 22,
},
reducers: {
setUser(state, action) {
//state 当前的值 是一个代理对象。不能直接做赋值处理
Object.assign(state, action.payload);
},
},
});
//切片对象会自动生成action
export const { setUser } = userSlice.actions; //更新方法暴露外部直接调用无需设置action.type
src/store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import { userSlice } from './reducer/user';
const store = configureStore({
reducer: userSlice.reducer,
});
export { store };
src/index.ts
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from './store';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>
);
tsx 页面
import { useDispatch, useSelector } from 'react-redux';
import { setUser } from '../../../store/reducer/user';
function LayoutConent() {
const dispatch = useDispatch();
const store: any = useSelector(state => state);
const updateUser = () => {
dispatch(setUser({ name: '李四' }));
};
return (
<div className="layout-content">
<p>{store.user.name}</p>
{/* 在store reducer 中配置了key(user) */}
<button onClick={updateUser}>更新用户</button>
</div>
);
}
export default LayoutConent;
redux 全局状态持久化配置 yarn add redux-persist
在 store/index.ts
中添加配置
import { configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { userSlice } from './reducer/user';
const store = configureStore({
reducer: {
user: persistReducer({ key: 'user', storage }, userSlice.reducer),
},
//A non-serializable value was detected in an action, in the path: `register` 需要关闭序列化
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: false,
}),
});
const persistor = persistStore(store);
export { store, persistor };
src/index.ts
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store, persistor } from './store';
import { PersistGate } from 'redux-persist/integration/react';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<BrowserRouter>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</BrowserRouter>
);
页面中使用方式不变
6 配置 @ 路径
安装依赖 yarn add @craco/craco
根路径下创建 craco.config.js
/* eslint-disable */
//eslint 文件禁止以 commonjs 方式引入,这里加下文件忽略
const path = require("path")
module.exports = {
webpack:{
alias:{
"@":path.resolve(__dirname,"src")
}
}
}
package.json
文件
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
tsconfig.ts
件添加配置
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
7 动态路由配置
我们定义一组数据模拟接口返回的权限 src/router/index.ts
interface Irouter {
name: string;
path: string;
children?: Array<Irouter> | null;
component: string;
}
export const routerMap: Array<Irouter> = [
{
name: '用户中心',
path: 'users',
component: 'users/index',
children: [
{
name: '新增用户',
path: 'createUser',
component: 'users/user-info',
children: [],
},
],
},
{
name: '公司管理',
path: 'company',
component: 'company/index',
children: [],
},
{
name: '账户管理',
path: 'home',
component: 'company/index',
children: [],
},
];
在 app.ts
中进行路由懒加载 Suspense
组件的作用是当路由未加载完成时可以做前置处理,(最开始使用时将这个组件放在app 组件的最外层,导致路由跳转出现闪屏问题,原来是这个组件配置出了问题)
import React, { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
import Login from './views/login';
import Layout from '@/views/layout'; //包括左侧 Menu, 头部Head 以及主要路由页面 content
import { routerMap } from '@/router';
import 'antd/dist/reset.css';
import './assets/style/index.scss';
const lazyLoad = (moduleName: string) => {
const Module = lazy(() => import(`@/views/${moduleName}`));
return (
<Suspense fallback={<div>Loading...</div>}>
<Module />
</Suspense>
);
};
function App() {
return (
<Routes>
<Route path="" element={<Login />}></Route>
<Route path="/login" element={<Login />}></Route>
<Route path="/*" element={<Layout />}>
{routerMap.map(item => {
return <Route key={item.path} path={item.path} element={lazyLoad(item.component)}></Route>;
})}
</Route>
</Routes>
);
}
export default App;
8 axios 配置
在之前 vue3
项目搭建已经有相应的配置,可以直接拿来使用
juejin.cn/post/721699…
结语
下篇会进行项目的打包配置,以及常用组件的封装等,目前公司所有业务线都已经停掉了,得抽出时间来把之前欠的东西补出来了!