React路由系统
1.组件路由
- yarn add react-router-dom
1.路由的简单使用
- HashRouter,BrowserRouter(react-router@6)
<!-- 在组件的顶层挂载了一个路由类型,其他地方无需挂载 -->
<HashRouter>
<!-- <BrowserRouter> -->
<Routes>
<Route path="/" element={<HomePage />} />
</Routes>
<!-- </BrowserRouter> -->
</HashRouter>
- HashRouter,BrowserRouter(react-router@5)
<!-- 在组件的顶层挂载了一个路由类型,其他地方无需挂载 -->
<HashRouter>
<!-- <BrowserRouter> -->
<Route path="/" component={HomePage} />
<!-- </BrowserRouter> -->
</HashRouter>
- react-router@5中的页面堆叠问题
<HashRouter>
<!-- exact精准匹配和Switch组件 -->
<Switch>
<Route exact path="/" component={HomePage} />
</Switch>
</HashRouter>
- 404页面(组件最后面)
<!-- react-router@6 -->
<Route path="*" element={<h1>404</h1>} />
<!-- react-router@5 -->
<Route path="*" render={()=><h1>404</h1>} />
2.路由传参(react-router@5)
- params参数
// 传递的参数
//声明接收params
<Route path="/home/message/detail/:id/:title" component={
Detail}/>
//获取参数
const {
title,id}=this.props.match.params
- query参数
// query参数,正常注册路由无需接收
<Route path="/home/message/detail" component={
Detail}/>
//获取参数
const {
search}=this.props.location
const {
id,title}=qs.parse(search.slice(1))
- state参数
// state参数
<Link to={
{
pathname:'/home/message/detail', state:{
id:item.id,title:item.title}}}>{
item.title}</Link>
// 读取参数
const {
id,title}=this.props.location.state
3.路由传参(react-router@6)
(1)类组件获取参数
- 定义高阶组件withRouter
import React from "react";
import {
NavigateFunction, useLocation, useNavigate, useParams } from "react-router";
export interface RoutedProps<Params = any, State = any> {
location?: State;
navigate?: NavigateFunction;
params?: Params;
}
export function withRouter<P extends RoutedProps>(Child: React.ComponentClass<P>) {
return (props: Omit<P, keyof RoutedProps>) => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return <Child {
...props as P } navigate = {
navigate } location = {
location } params = {
params } />;
}
}
- 类组件件中使用高阶组件修饰(接收参数)
import React from "react";
import {
RoutedProps, withRouter } from "../../hoc/withRouter";
interface Props extends RoutedProps {
}
class HomePage extends React.Component<Props> {
constructor(props: Props) {
super(props);
}
render(): React.ReactNode {
// 获取params
console.log(this.props.params);
// 获取query
console.log(this.props.location.search);
return (
<>
</>
);
}
}
export default withRouter(HomePage);
- 类组件接收state参数
import React from "react";
import {
RoutedProps, withRouter } from "../../hoc/withRouter";
import {
Link } from "react-router-dom";
interface Props extends RoutedProps {
}
class HomePage extends React.Component<Props> {
constructor(props: Props) {
super(props);
}
render(): React.ReactNode {
// 接收state参数
console.log(this.props.location.state);
return (
<Link to={
`/1`} state={
{
id:1}}>home</Link>
);
}
}
export default withRouter(HomePage);
(2)函数组件获取参数
import {
useParams, useLocation, useMatch } from "react-router-dom";
const HomePage: React.FC = () => {
// params参数
const params = useParams();
// query参数和state参数
const location = useLocation();
// params参数
const metch = useMatch("/:id");
console.log(params.id, location.search, metch?.params);
return <div>home</div>;
};
export default HomePage;
4.路由导航(react-router@5)
- 声明式导航
// push
<Link push={
true} to={
`/`}>{
item.title}</Link>
// replace
<Link replace={
true} to={
`/`}>{
item.title}</Link>
- 声明式导航
// 可以设置被选中的样式
// push
<NavLink push={
true} to={
`/`}>push</NavLink>
// replace
<NavLink replace={
true} to={
`/`} activeClassName="selected">replace</NavLink>
- 编程式导航
// 1.push跳转+携带params参数
props.history.push(`/b/child1/${
id}/${
title}`);
// 2.push跳转+携带search参数
props.history.replace(`/b/child1?id=${
id}&title=${
title}`);
// 3.push跳转+携带state参数
props.history.push(`/b/child1`, {
id, title });
// 7.前进
this.props.history.goForward();
// 8.回退
this.props.history.goForward();
// 9.前进或回退 ( go )
this.props.history.go(-2); //回退到前2条的路由
5.路由导航(react-router@6)
(1)类组件的编程式导航
- 定义高阶组件withRouter
import React from "react";
import {
NavigateFunction, useLocation, useNavigate, useParams } from "react-router";
export interface RoutedProps<Params = any, State = any> {
location?: State;
navigate?: NavigateFunction;
params?: Params;
}
export function withRouter<P extends RoutedProps>(Child: React.ComponentClass<P>) {
return (props: Omit<P, keyof RoutedProps>) => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return <Child {
...props as P } navigate = {
navigate } location = {
location } params = {
params } />;
}
}
- 类组件件中使用高阶组件修饰(跳转)
import React from "react";
import {
RoutedProps, withRouter } from "../../hoc/withRouter";
interface Props extends RoutedProps {
}
class HomePage extends React.Component<Props> {
constructor(props: Props) {
super(props);
}
render(): React.ReactNode {
return (
// 编程式跳转
<button onClick={
() => this.props.navigate("/2")}>跳转</button>
);
}
}
export default withRouter(HomePage);
(2)函数式组件编程跳转
import {
useNavigate } from "react-router-dom";
const HomePage: React.FC = () => {
const navigate = useNavigate();
// push跳转+携带params参数
navigate(`/b/child1/${
id}/${
title}`);
// push跳转+携带search参数
navigate(`/b/child2?id=${
id}&title=${
title}`);
// push跳转+携带state参数
navigate("/b/child2", {
state: {
id, title } });
return <button onClick={
() => navigate("/2")}>跳转</button>;
};
export default HomePage;
6.嵌套路由
- react-router@5的嵌套路由
<!-- react-router@5关闭精准匹配 -->
<HashRouter>
<Route path="/" component={HomePage} />
<!-- 二级路由 -->
<Route path="/home" />
</HashRouter>
- react-router@6的嵌套路由
<!-- 路由父组件 -->
<BrowserRouter>
<Routes>
<Route path="/*" element={<HomePage />}></Route>
<Route path="*" element={<h1>404</h1>} />
</Routes>
</BrowserRouter>
<!-- 路由子组件中写 -->
<Routes>
<Route path="/login" element={<h1>login</h1>} />
</Routes>
- 集中路由管理(嵌套路由)
<BrowserRouter>
<Routes>
<Route path="/" element={<Outlet />}>
<Route path="/login" element={<LoginPage />} />
</Route>
</Routes>
</BrowserRouter>
2. 配置路由
1.createBrowserRouter等
- 创建路由index文件
import {
createBrowserRouter, Outlet } from "react-router-dom";
// Outlet子路由的出口文件
const router = createBrowserRouter([
{
path: "/",
element: (
<h1>
<Outlet />
111
</h1>
),
children: [
{
path: "login",
element: <div>222</div>,
},
],
},
]);
export default router;
- 使用路由配置
import router from "./router";
function App() {
return (
<div className={
styles.app}>
<RouterProvider router={
router} />
</div>
);
}
export default App;
2.useRoutes创建路由
- 创建路由index文件
import {
Outlet, useRoutes } from "react-router-dom";
function BaseRouter() {
const element = useRoutes([
{
path: "/",
element: (
<div>
<Outlet />
111
</div>
),
// 向Vue一样创建children放置页面
children: [
{
path: "login",
element: <h1>222</h1>,
},
],
},
]);
return element;
}
export default BaseRouter;
- 使用路由配置
// 这里是组件首字符要大写
import BaseRouter from "./router";
function App() {
return (
<div className={
styles.app}>
<BrowserRouter>
<BaseRouter></BaseRouter>
</BrowserRouter>
</div>
);
}
export default App;
Redux全局状态管理
- yarn add redux
1. redux的基本使用
1. 读取redux中的数据
- 数据reducer目录
interface State {
language: 'en' | 'zh'
languageList: {
label:string,key:string }[]
}
const defaultState: State = {
language: 'zh',
languageList: [
{
label:'中文',key:'zh'},
{
label:'English',key:'en'}
]
}
export default (state=defaultState, action: any) => {
return state
}
- 引入数据目录导出state
import {
createStore } from 'redux'
import LanReducer from './Reducer/LanReducer'
const store = createStore(LanReducer);
export default store;
- 组件中读取store数据
import store from "../../../store";
class HeaderClass extends React.Component<Props, State> {
constructor(props: Props, state: State) {
super(props);
// 读取state中的数据
const storeState=store.getState()
this.state = {
language: storeState.language ,// 当前语言
languageList:storeState.languageList
};
}
....
}
2. 替换redux中的数据
- 数据reducer目录
interface State {
language: string
languageList: {
label: string; key: string }[]
}
const defaultState: State = {
language: 'zh',
languageList: [
{
label: '中文', key: 'zh' },
{
label: 'English', key: 'en' },
],
}
export default (state = defaultState, action: any) => {
switch (action.type) {
// 切换语言
case 'checkLanguage': {
const newState = {
...state, language: action.payload }
return newState
}
// 添加新语言
case 'addLanguage': {
const newlanguageList = [...state.languageList, action.payload]
const newState = {
...state, languageList: newlanguageList }
return newState
}
default:
return state
}
}
- 引入数据目录导出state
import {
createStore } from 'redux'
import LanReducer from './Reducer/LanReducer'
const store = createStore(LanReducer);
export default store;
- 派发action数据
// 点击切换语言
checkLanguage = (e:any) => {
const action = {
type: "checkLanguage",
payload:e.key
}
store.dispatch(action)
}
- 在挂载的生命周期中进行订阅
// 挂载
componentDidMount() {
store.subscribe(() => {
const storeState = store.getState()
this.setState({
language: storeState.language
})
})
}
3. redux的类型限制
- 创建action类型和创建action的工厂函数
export const CHECKLANGUAGE = 'checkLanguage'
export const ADDLANGUAGE = 'addLanguage'
interface changeLanguageAction {
type: typeof CHECKLANGUAGE
payload: string
}
interface addLanguageAction {
type: typeof ADDLANGUAGE
payload: {
label: string; key: string }
}
export type languageActionTypes = changeLanguageAction | addLanguageAction
// 函数
export const changeLanguageActionCreator = (languageCode: string): changeLanguageAction => {
return {
type: CHECKLANGUAGE,
payload: languageCode,
}
}
export const addLanguageActionCreator = (label: string, key: string): addLanguageAction => {
return {
type: ADDLANGUAGE,
payload: {
label, key },
}
}
- 修改reducer内容
import {
CHECKLANGUAGE,ADDLANGUAGE,languageActionTypes} from './languageActions'
interface language {
label: string
key: string
}
interface State {
language: string
languageList: language[]
}
const defaultState: State = {
language: 'zh',
languageList: [
{
label: '中文', key: 'zh' },
{
label: 'English', key: 'en' },
],
}
export default (state = defaultState, action: languageActionTypes) => {
switch (action.type) {
// 切换语言
case CHECKLANGUAGE: {
const newState = {
...state, language: action.payload }
return newState
}
// 添加新语言
case ADDLANGUAGE: {
const newlanguageList = [...state.languageList, action.payload]
const newState = {
...state, languageList: newlanguageList }
return newState
}
default:
return state
}
}
- 修改使用redux数据
import {
changeLanguageActionCreator, addLanguageActionCreator } from '../../../store/language/languageActions'
// 点击切换语言
checkLanguage = (e: any) => {
if (e.key === 'new') {
store.dispatch(addLanguageActionCreator('新语言', 'new_lang'))
} else {
i18n.changeLanguage(e.key)
store.dispatch(changeLanguageActionCreator(e.key))
}
}
2. react-redux实现派发和订阅
yarn add react-redux -S
- 入口文件中注入stroe注入
...
import {
Provider } from 'react-redux'
import store from './store'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
{
/* 注入属性 */}
<Provider store={
store}>
<App />
</Provider>
</React.StrictMode>,
)
1. 用react-redux完成对类组件的订阅以及初始化
- store入口文件中导出state的类型
import {
createStore } from 'redux'
import LanReducer from './language/LanReducer'
const store = createStore(LanReducer)
export type RootState = ReturnType<typeof store.getState>
export default store
- 使用高阶组件修饰完成订阅
...
import store, {
RootState } from '../../../store'
import {
connect as withConnect } from 'react-redux'
interface Props {
}
// Props类型
type PropsType = Props &
ReturnType<typeof mapStateToProps>
// 完成订阅(以及初始化,值会放入props中)
const mapStateToProps = (state: RootState) => {
return {
language: state.language,
languageList: state.languageList,
}
}
class HeaderClass extends React.Component<PropsType> {
constructor(props: PropsType) {
super(props)
}
...
render(): React.ReactNode {
const navigate = this.props.navigate as NavigateFunction
const {
t } = this.props
return (
...
// 值从props中取
<Menu
items={
[...this.props.languageList, {
label: '添加新语言', key: 'new' }]}
onClick={
(e) => this.checkLanguage(e)}></Menu>
...
)
}
}
export const Header = withConnect(mapStateToProps, mapDispatchToProps)(HeaderClass)
2. 用react-redux完成对类组件的派发
- 完成派发的数据定义
...
import store, {
RootState } from '../../../store'
import {
changeLanguageActionCreator, addLanguageActionCreator } from '../../../store/language/languageActions'
import {
connect as withConnect } from 'react-redux'
import {
Dispatch } from 'redux'
interface Props {
}
// Props类型
type PropsType = Props &
ReturnType<typeof mapDispatchToProps> &
...
// 完成数据修改
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
checkLanguage: (label: string) => {
dispatch(changeLanguageActionCreator(label))
},
addLanguage: (label: string, key: string) => {
dispatch(addLanguageActionCreator(label, key))
},
}
}
class HeaderClass extends React.Component<PropsType> {
constructor(props: PropsType) {
super(props)
}
...
render(): React.ReactNode {
... }
}
export const Header = withConnect(mapStateToProps, mapDispatchToProps)(HeaderClass)
- 修改点击修改语言
// 点击切换语言
checkLanguage = (e: any) => {
if (e.key === 'new') {
this.props.addLanguage('新语言', 'new_lang')
} else {
i18n.changeLanguage(e.key)
this.props.checkLanguage(e.key)
}
}
3. redux toolkit简化redux
- 介绍
Redux Toolkit 允许在 reducers 中编写 "mutating" 逻辑。
它实际上并没有改变 state,因为使用的是 Immer 库,检测到“草稿 state”的变化并产生一个全新的
基于这些更改的不可变的 state。
- 语言数据的模块
interface language {
label: string
key: string
}
interface State {
language: string
languageList: language[]
}
import {
createSlice, configureStore } from '@reduxjs/toolkit'
const LanguageSlice = createSlice({
name: 'language',
initialState: {
language: 'zh',
languageList: [
{
label: '中文', key: 'zh' },
{
label: 'English', key: 'en' },
],
},
reducers: {
checkLanguage(state: State, action: {
payload: any; type: language | string }) {
state.language=action.payload
},
addLanguage(state: State, action: {
payload: any; type: language | string }) {
state.languageList.push(action.payload)
},
},
})
export const {
checkLanguage, addLanguage } = LanguageSlice.actions
export const language = configureStore({
reducer: LanguageSlice.reducer,
})
- 引入数据的主模块
import {
language } from './Reducer/LanReducer'
const store = {
language,
}
export default store
1. 读取redux中的数据
- 读取数据
import store from '../../../store'
interface Props extends RoutedProps {
}
interface State {
language: string
languageList: {
label: string; key: string }[]
}
class HeaderClass extends React.Component<Props, State> {
constructor(props: Props, state: State) {
super(props)
// 获取当前的数据
const storeState = store.language.getState()
this.state = {
language: storeState.language, // 当前语言
languageList: storeState.languageList,
}
}
render(): React.ReactNode {
...
}
}
export const Header = withRouter(HeaderClass)
- 数据订阅
import store from '../../../store'
interface Props extends RoutedProps {
}
interface State {
language: string
languageList: {
label: string; key: string }[]
}
class HeaderClass extends React.Component<Props, State> {
constructor(props: Props, state: State) {
....
}
// 挂载订阅(构造器中不能再次初始化state)
componentDidMount() {
store.language.subscribe(() => {
const storeState = store.language.getState()
this.setState({
language: storeState.language,
languageList: storeState.languageList,
})
})
}
render(): React.ReactNode {
...
}
}
export const Header = withRouter(HeaderClass)
2. 修改redux中的数据
- 简单介绍
在store对应的模块中回应对于的方法
在派发中传递过去的值变成的action的形式
const action = {
type: 'checkLanguage',payload: e.key}
- 派发对应的函数
import store from '../../../store'
import {
checkLanguage, addLanguage } from '../../../store/lanReducer'
interface Props extends RoutedProps {
}
interface State {
language: string
languageList: {
label: string; key: string }[]
}
class HeaderClass extends React.Component<Props, State> {
constructor(props: Props, state: State) {
....
}
// 挂载订阅(构造器中不能再次初始化state)
componentDidMount() {
....
}
// 点击切换语言
checkLanguage = (e: any) => {
if (e.key === 'new') {
store.language.dispatch(addLanguage({
label: '新语言', key: 'new_lang' },))
} else {
store.language.dispatch(checkLanguage(e.key))
}
}
render(): React.ReactNode {
...
}
}
export const Header = withRouter(HeaderClass)