创建项目
#vite
pnpm create vite@latest xxx --template react-ts
#webPack
npx create-react-app xxx
vite配置路径别名
npm i @types/node -D 安装依赖
vite.config.ts 配置
tsimport { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], // 配置以下内容 resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, })
tsconfig.json 添加内容
json"compilerOptions":{ "baseUrl": ".", "paths": { "@/*": [ "src/*" ] }, }
路由
路由初始化
安装 pnpm i react-router-dom
初始化路由
router/index.tsx
jsximport { createBrowserRouter } from 'react-router-dom' import Home from '../pages/Home' import { Suspense, lazy } from 'react' #路由懒加载所需函数 const Detail = lazy(() => import('../pages/Detail')) #路由懒加载 const router = createBrowserRouter([ { path: '/', element: <Home /> }, { path: '/detail/:id', #注意此处配置params传参 element: ( // 懒加载必须包裹 <Suspense fallback={'加载中'}> <Detail></Detail> </Suspense> ) }, { path: '*', #在末尾配置404 element: <NotFound /> }, ]) // 导出路由 export default router
使用路由
main.tsx
tsximport ReactDOM from 'react-dom/client' #// 导入Provider 必须要用RouterProvider import { RouterProvider } from 'react-router-dom' #// 导入router实例 import router from './router/index.tsx' ReactDOM.createRoot(document.getElementById('root')!).render( <RouterProvider router={router} /> )
嵌套路由
#配置时
{
path: '/',
element: <Layout />,
children: [
// 设置为默认二级路由 一级路由访问的时候,它也能得到渲染
{
index: true, // 设置为默认二级路由
element: <Board />
},
{
path: 'about',
element: <About />
}
]
},
#Layout内使用 <Outlet />路由出口
const Layout = () => {
return (
<div>
<div>我是Layout</div>
<Outlet />
</div>
)
}
路由跳转&传参&参数获取方式
searchParams方式,获取用useSearchParams
params方式,获取用useParams
tsximport { useNavigate } from 'react-router-dom' import { useNavigate, useSearchParams, useParams } from 'react-router-dom' const navigate = useNavigate() const goDetail = (art_id: string) => () => { navigate(`/detail?id=${id}`) #searchParams方式 navigate(`/detail/${id}`) #params方式 } #获取 searchParams 方式 const [params] = useSearchParams() let id = params.get(id) #获取params方式 const params = useParams() let id = params.id
Redux基本使用
安装工具包 pnpm i @reduxjs/toolkit react-redux
store/modules/countStore.js 内对仓库进行定义
js
import { createSlice } from '@reduxjs/toolkit'
const countStore = createSlice({ name: 'count', initialState: { count: 0, userInfo: {} }, reducers: { increment(state, action) { state.count += action.payload }, setUserInfo(state, action) { state.userInfo = action.payload } } }) // 解构函数 const { increment, setUserInfo } = countStore.actions
// 异步函数 发送请求 const getUserInfo = () => { return async (dispatch) => { const res = await fetch('http://geek.itheima.net/v1_0/channels') const data = await res.json() dispatch(setUserInfo(data.data.channels)) } } export { increment, getUserInfo } // 导出函数 export default countStore.reducer // 导出reducerstore/modules/countStore.ts 内对仓库进行定义
2. #### store/index.js 内进行导入仓库
```js
import { configureStore } from "@reduxjs/toolkit";
import countStore from "./modules/countStore";
const store = configureStore({
reducer: {
counter:countStore // 前面counter定义的是什么,在项目中state.xxx就是什么
}
})
export default store;
main.tsx 进行挂载仓库
import ReactDOM from 'react-dom/client'
// 导入Provider
import { RouterProvider } from 'react-router-dom'
// 导入router实例
import router from './router/index.tsx'
// 导入provider 用于提供store
import { Provider } from 'react-redux'
// 导入store
import store from './store/index.ts'
ReactDOM.createRoot(document.getElementById('root')!).render(
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
)
在组件内使用仓库
import { useDispatch, useSelector } from 'react-redux'
import { getUserInfo, increment } from '@/store/modules/countStore'
import { useEffect } from 'react'
const Home = () => {
// 解构出仓库中定义的数据
const { count } = useSelector((state: any) => state.counter)
const dispatch = useDispatch() // 触发仓库中方法的函数
const handleCount = () => {
dispatch(increment(1))
}
// 上来就发送请求
useEffect(() => {
dispatch(getUserInfo())
}, [dispatch]) // 注意此处传参
return (
<Button onClick={handleCount}>count++{count}</Button>
)
}
export default Home
React组件灵活渲染
import { DetailRes, fetchDetailAPI } from '@/api/list'
import { NavBar } from 'antd-mobile'
import { useEffect, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
const Detail = () => {
const [detail, setDetail] = useState<DetailRes | null>(null)
const navigate = useNavigate()
const [params] = useSearchParams()
const id = params.get('id')
useEffect(() => {
const getDetail = async () => {
const res = await fetchDetailAPI(id!)
setDetail(res.data.data)
}
getDetail()
}, [id]) //id变化就会重新请求数据
const back = () => {
navigate(-1) // 返回
}
if (!detail) { //如果数据还没请求到
return <div>this is Loading</div>
} else {
return (
<div>
<NavBar onBack={back}>{detail?.title}</NavBar>
// 此处是渲染html格式的 v-html
<div dangerouslySetInnerHTML={{ __html: detail?.content }}></div>
</div>
)
}
}
export default Detail
React简单登录校验
定义个token校验的组件 components/AuthRoute/index.jsx
import { Navigate } from 'react-router-dom'
import { getToken } from '../../utils/token'
export function AuthRoute({ children }) { // 此处实际传递的是子组件
const token = getToken() //获取token的方法
if (token) { //如果有token
return <>{children}</> //渲染子组件
} else {
return <Navigate to='/login' replace />
}
}
在路由页面进行使用 对公共父组件Layout进行包裹
import { createBrowserRouter } from 'react-router-dom'
import Layout from '@/pages/Layout'
import Login from '@/pages/Login'
import { AuthRoute } from '../components/AuthRoute'
import { Suspense, lazy } from 'react'
const Home = lazy(() => import('@/pages/Home/index')) //懒加载
const Article = lazy(() => import('@/pages/Article'))
const Publish = lazy(() => import('@/pages/Publish'))
const router = createBrowserRouter([
{
path: '/',
element: (
// 进行包裹
<AuthRoute>
<Layout />
</AuthRoute>
),
children: [
{
index: 1, // 默认首页
element: (
// 懒加载也必须进行包裹
<Suspense fallback={'加载中'}>
<Home></Home>
</Suspense>
)
},
{
path: 'article',
element: (
<Suspense fallback={'加载中'}>
<Article></Article>
</Suspense>
)
}
]
},
{
path: '/login',
element: <Login />
}
])
export default router