一、项目构建
安装脚手架
npm install -g create-react-app
// or
yarn add -g create-react-app
创建项目
创建一个新文件夹my-react-app,并在该文件夹下使用终端创建项目my-react-app
create-react-app my-react-app --template typescript
使用vscode打开刚创建的项目,可以看到项目结构:
react项目默认隐藏了webpack相关配置,执行
npm run eject
暴露webpack配置,此操作不可撤销,请按需要操作。
此时,项目已经创建完成,运行
npm run start
浏览器中可以看到
配置路径别名
这里我们使用craco来配置路径别名,安装craco:
npm i @craco/craco
// or
yarn add @craco/craco
修改package.json中的启动脚本:
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
在项目的根目录中创建craco.config.js,并配置路径别名:
const path = require('path')
module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src/'),
},
},
}
并在tsconfig.json中增加配置:
{
"compilerOptions": {
"path": {
"@/*": ["./src/*"],
},
}
}
二、引入 Ant Design 组件库
安装和初始化
Ant Design官网:https://ant-design.antgroup.com/index-cn
使用脚手架
$ npx create-react-app antd-demo --template typescript
// or
$ yarn create react-app antd-demo --template typescript
引入 antd
npm install antd --save
// or
yarn add antd
改造App.tsx
import React from 'react'
import { ConfigProvider, Button } from 'antd'
import dayjs from 'dayjs'
import zhCN from 'antd/locale/zh_CN'
import './App.css'
dayjs.locale('zh-cn')
const App: React.FC = () => {
return (
<ConfigProvider locale={zhCN}>
<div className='App'>
<Button type='primary'>欢迎</Button>
</div>
</ConfigProvider>
)
}
export default App
在页面上看到欢迎按钮,引入成功。
三、路由配置
React Router 官网:https://reactrouter.com/en/main
安装 React Router
npm install react-router-dom
// or
yarn add react-router-dom
使用路由
改造根目录 index.tsx,这里使用 BrowserRouter:
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'
import { BrowserRouter } from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</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()
在src下新增页面:
在src下增加router/index.tsx:
import { Navigate } from 'react-router-dom'
import Home from '@/pages/home'
import Login from '@/pages/login'
import Layout from '@/pages/layout'
const routes = [
{
path: '/login',
element: <Login />,
},
{
path: '/',
element: <Navigate to='/login' />,
},
{
path: '/layout',
element: <Layout />,
children: [
{
path: '/layout/home',
element: <Home />,
},
],
},
]
export default routes
改造App.tsx
import React from 'react'
import { ConfigProvider } from 'antd'
import dayjs from 'dayjs'
import zhCN from 'antd/locale/zh_CN'
import './App.css'
import routes from '@/router'
import { useRoutes } from 'react-router-dom'
dayjs.locale('zh-cn')
const App: React.FC = () => {
const elements = useRoutes(routes)
return (
<ConfigProvider locale={zhCN}>
<div className='App'>{elements}</div>
</ConfigProvider>
)
}
export default App
四、配置 axios
安装 axios
npm install axios -S
配置 axios
在src下新建文件src/api/http.ts
import axios from 'axios'
const service = axios.create({
timeout: 60 * 1000,
// 基础接口地址
baseURL: '',
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
service.interceptors.request.use(config => {
const token = localstorage.getItem('', 'user_token')
config.headers['x-auth-token'] = token
return config
})
service.interceptors.response.use(
response => {
return response.data
},
err => {
if (err?.response?.data?.code === '000401') {
window.location.href = '/login'
}
return Promise.reject(err)
}
)
export default service
五、配置 Redux 并持久化
Redux中文网:https://www.redux.org.cn/
安装 Redux Toolkit 和 React-Redux
npm install @reduxjs/toolkit react-redux
安装 redux-persist
npm install redux-persist --save
配置 Redux
在src文件夹下创建src/store/index.ts:
import { configureStore } from '@reduxjs/toolkit'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import rootReducer from './reducer'
const persistConfig = {
key: 'root',
storage,
whitelist: [], // 这里存放白名单
}
const store = configureStore({
reducer: persistReducer(persistConfig, rootReducer),
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: false,
}),
})
export const persistor = persistStore(store)
export default store
// 从 store 本身推断出 `RootState` 和 `AppDispatch` 类型
export type RootState = ReturnType<typeof store.getState>
// 推断出类型: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
src/store/reducer.ts:
import { combineReducers } from '@reduxjs/toolkit'
const rootReducer = combineReducers({})
export default rootReducer
改造根目录的index.tsx:
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
import reportWebVitals from './reportWebVitals'
import store from './store'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import { persistor } from './store'
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<BrowserRouter>
<App />
</BrowserRouter>
</PersistGate>
</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()
在src文件夹下创建src/hooks/redux.ts:
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from '@/store/index'
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
以用户登录成功后设置用户信息为例,在src文件夹下创建src/store/slices/user.ts:
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
interface UserState {
userInfo: any
token: string
}
const initialState: UserState = {
userInfo: {},
token: '',
}
export const userSlice = createSlice({
name: 'user',
initialState: initialState,
reducers: {
setUserInfo(state, action) {
state.userInfo = action.payload
},
setToken(state, action: PayloadAction<string>) {
state.token = action.payload
},
},
})
export const { setUserInfo, setToken } = userSlice.actions
export default userSlice.reducer
在src/store/reducer.ts中添加:
import { combineReducers } from '@reduxjs/toolkit'
import userReducer from './slices/user'
const rootReducer = combineReducers({
user: userReducer,
})
export default rootReducer
在src/pages/login/index.tsx中:
import React, { useState } from 'react'
import { useAppDispatch } from '@/hooks/redux'
import { setUserInfo, setToken } from '@/store/slices/user'
const Login: React.FC = () => {
const dispatch = useAppDispatch()
const onFinish = () => {
/*...一些代码...*/
dispatch(setUserInfo(user))
dispatch(setToken(token))
/*...一些代码...*/
}
return (
<div>{/*...一些代码...*/}</div>
)
}
export default Login