1. Redux 介绍
// 创建一个简单的Redux store
const { createStore } = Redux;// reducer函数
function counterReducer(state = { count: 0 }, action) {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 };case 'DECREMENT':return { count: state.count - 1 };default:return state;}
}// 创建store实例
const store = createStore(counterReducer);// 订阅数据变化
store.subscribe(() => {console.log(store.getState());
});// 触发数据变化
store.dispatch({ type: 'INCREMENT' });
-
概念
- 是 React 常用的集中状态管理工具,可独立于框架运行,类似于 Vue 中的 Pinia(Vuex)。
- 通过集中管理方式管理应用状态。
-
使用原因
- 独立于组件,无视层级关系,简化通信。
- 单向数据流清晰,易于定位 bug。
- 调试工具配套良好,方便调试。
2. Redux 快速体验
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
<script>// 定义reducer函数 function counterReducer (state = { count: 0 }, action) {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 }case 'DECREMENT':return { count: state.count - 1 }default:return state}}const store = Redux.createStore(counterReducer);store.subscribe(() => {document.getElementById('count').innerText = store.getState().count;});const inBtn = document.getElementById('increment');inBtn.addEventListener('click', () => {store.dispatch({ type: 'INCREMENT' });});const dBtn = document.getElementById('decrement');dBtn.addEventListener('click', () => {store.dispatch({ type: 'DECREMENT' });});
</script>
3. Redux 与 React
# 创建React项目
npx create-react-app react-redux # 安装配套工具
npm i @reduxjs/toolkit react-redux # 启动项目
npm run start
-
环境准备
- 安装 Redux Toolkit(RTK)和 react-redux。
- 使用 CRA 创建 React 项目,安装配套工具并启动。
- 设计 store 目录结构,包括单独的 store 目录和内部的 modules 目录,入口文件组合子模块并导出 store。
-
实现 counter
// counterStore.js
import { createSlice } from '@reduxjs/toolkit';const counterStore = createSlice({name: 'counter',initialState: { count: 1 },reducers: {increment(state) {state.count++;},decrement(state) {state.count--;}}
});const { increment, decrement } = counterStore.actions;
const counterReducer = counterStore.reducer;
export { increment, decrement };
export default counterReducer;// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './modules/counterStore';
export default configureStore({reducer: {counter: counterReducer}
});// App.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store';
import { Provider } from 'react-redux';
ReactDOM.createRoot(document.getElementById('root')).render(<Provider store={store}><App /></Provider>
);// 在组件中使用store数据
import { useSelector } from 'react-redux';
const count = useSelector(state => state.counter.count);// 在组件中修改store数据
import { useDispatch } from 'react-redux';
const dispatch = useDispatch();
dispatch(increment());
- 使用 React Toolkit 创建 counterStore,定义模块名称、初始数据和修改数据的同步方法。
- 为 React 注入 store,使用 Provider 组件传递 store 实例。
- React 组件中用 useSelector 获取 store 数据,用 useDispatch 修改 store 数据。
-
提交 action 传参
// reducer函数
function counterReducer(state = { count: 0 }, action) {switch (action.type) {case 'INCREMENT_WITH_PARAM':return { count: action.payload.targetCount };default:return state;}
}// 触发action并传递参数
const targetCount = 10;
store.dispatch({ type: 'INCREMENT_WITH_PARAM', payload: { targetCount } });
- 在 reducers 同步修改方法中添加 action 对象参数,调用 actionCreater 时传递参数到 action 对象的 payload 属性。
-
异步 action 处理
// channelStore.js
import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';const channelStore = createSlice({name: 'channel',initialState: { channelList: [] },reducers: {setChannelList(state, action) {state.channelList = action.payload;}}
});const { setChannelList } = channelStore.actions;
const url = 'http://geek.itheima.net/v1_0/channels';
const fetchChannelList = () => {return async (dispatch) => {const res = await axios.get(url);dispatch(setChannelList(res.data.data.channels));}
};
export { fetchChannelList };
const channelReducer = channelStore.reducer;
export default channelReducer;// App.jsx
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchChannelList } from './store/channelStore';function App() {const { channelList } = useSelector(state => state.channel);useEffect(() => {dispatch(fetchChannelList());}, [dispatch]);return (<div className="App"><ul>{channelList.map(task => <li key={task.id}>{task.name}</li>)}</ul></div>);
}
export default App;
- 创建 store 配置同步方法,单独封装函数,在新函数中封装异步请求获取数据并调用同步 actionCreater 生成 action 对象提交。
4. Redux 调试
- 官方提供调试工具,支持实时 state 信息展示和 action 提交信息查看。
5. 路由快速上手
// 假设已有一个简单的React组件结构
import React from 'react';
import ReactDOM from 'react-dom/client';// 定义两个简单组件
const LoginComponent = () => <div>登录页面内容</div>;
const ArticleComponent = () => <div>文章页面内容</div>;// 这里模拟路由配置(实际使用React Router的配置方式会更复杂)
const routes = [{ path: '/login', component: LoginComponent },{ path: '/article', component: ArticleComponent }
];// 根据当前路径渲染相应组件(这里是简化的逻辑,实际需要根据React Router的机制来实现)
const App = () => {const currentPath = window.location.pathname;const ComponentToRender = routes.find(route => route.path === currentPath)?.component || null;return <>{ComponentToRender}</>;
};ReactDOM.createRoot(document.getElementById('root')).render(<App />);
-
前端路由概念
- 一个路径对应一个组件,访问路径时对应组件在页面渲染。
-
开发环境创建
# 使用CRA创建项目
npm create-react-app react-router-pro# 安装最新的ReactRouter包
npm i react-router-dom# 启动项目
npm run start
- 使用 CRA 创建项目,安装 React Router 包,启动项目。
-
快速开始
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';const router = createBrowserRouter([{path: '/login',element: <div>登录</div>},{path: '/article',element: <div>文章</div>},
]);ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router} />
);
- 使用
createBrowserRouter
创建路由,配置路径和对应组件,通过RouterProvider
渲染路由。
6. 抽象路由模块
// 假设这是一个简单的路由模块文件
import { createBrowserRouter, RouterProvider } from 'react-router-dom';// 定义各个路由组件
const HomeComponent = () => <div>首页</div>;
const AboutComponent = () => <div>关于我们</div>;
const ContactComponent = () => <div>联系我们</div>;// 创建路由配置
const router = createBrowserRouter([{path: '/',element: HomeComponent},{path: '/about',element: AboutComponent},{path: '/contact',element: ContactComponent}
]);// 导出路由配置供其他文件使用
export default router;
- (文档中未详细说明相关内容,可能需要进一步查看实际模块相关代码)
7. 路由导航
-
概念
-
声明式导航
import React from 'react';
import { Link } from 'react-router-dom';const NavMenu = () => (<nav><Link to="/home">首页</Link><Link to="/about">关于</Link><Link to="/contact">联系</Link></nav>
);
- 通过
<Link/>
组件,指定to
属性为路由路径实现跳转,传参可字符串拼接。
-
编程式导航
import React from 'react';
import { useNavigate } from 'react-router-dom';const LoginButton = () => {const navigate = useNavigate();const handleLogin = () => {// 假设这里是登录逻辑,登录成功后进行跳转navigate('/dashboard');};return <button onClick={handleLogin}>登录</button>;
};
- 使用
useNavigate
钩子获取导航方法,调用navigate
方法传入路径实现跳转,更灵活。
8. 导航传参
import React from 'react';
import { useNavigate } from 'react-router-dom';const ProductDetailButton = () => {const navigate = useNavigate();const productId = 123; // 假设这是一个产品IDconst handleNavigate = () => {navigate(`/product/${productId}`);};return <button onClick={handleNavigate}>查看产品详情</button>;
};
- (文档中未详细说明相关内容,可能需要进一步查看实际传参相关代码)
9. 嵌套路由配置
-
概念
- 一级路由中内嵌其他路由为嵌套路由,内嵌的为二级路由。
import React from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';// 定义子路由组件
const SubPage1 = () => <div>子页面1</div>;
const SubPage2 = () => <div>子页面2</div>;// 一级路由组件
const MainPage = () => <div>主页面</div>;// 创建嵌套路由配置
const router = createBrowserRouter([{path: '/main',element: MainPage,children: [{path: 'sub1',element: SubPage1},{path: 'sub2',element: SubPage2}]}
]);ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router} />
);
-
配置步骤
- 使用
children
属性配置嵌套关系,用<Outlet/>
组件配置二级路由渲染位置。 import React from 'react';
import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom';// 定义子路由组件
const SubPage1 = () => <div>子页面1</div>;
const SubPage2 = () => <div>子页面2</div>;// 一级路由组件
const MainPage = () => <div>主页面,这里可以放置一些通用的内容,子页面会在Outlet处渲染</div>;// 创建嵌套路由配置
const router = createBrowserRouter([{path: '/main',element: MainPage,children: [{path: 'sub1',element: SubPage1},{path: 'sub2',element: SubPage2}]}
]);ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router}><MainPage><Outlet /></MainPage></RouterProvider>
);
-
默认二级路由
- 访问一级路由时,二级路由去掉路径,设置
index
属性为true
可默认渲染。 import React from 'react';
import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom';// 定义子路由组件
const SubPage = () => <div>默认子页面</div>;// 一级路由组件
const MainPage = () => <div>主页面,这里可以放置一些通用的内容,子页面会在Outlet处渲染</div>;// 创建嵌套路由配置
const router = createBrowserRouter([{path: '/main',element: MainPage,children: [{path: '',index: true,element: SubPage}]}
]);ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router}><MainPage><Outlet /></MainPage></RouterProvider>
);
-
404 路由配置
- 准备
NotFound
组件,在路由表末尾用*
作为路径配置路由。 import React from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import NotFoundComponent from './NotFoundComponent';// 其他路由配置
const router = createBrowserRouter([//...其他路由{path: '*',element: NotFoundComponent}
]);ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router} />
);
-
路由模式
- 常用
history
模式和hash
模式。 history
模式:url
表现为url/login
,基于history
对象和pushState
事件,需后端支持。 import React from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';// 假设后端已正确配置处理history模式的路由请求const router = createBrowserRouter([{path: '/login',element: <div>登录页面内容</div>},{path: '/article',element: <div>文章页面内容</div>}
]);ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router} />
);
hash
模式:url
表现为url/#/login
,监听hashChange
事件,无需后端支持。 import React from 'react';
import { createHashRouter, RouterProvider } from 'react-router-dom';const router = createHashRouter([{path: '/login',element: <div>登录页面内容</div>},{path: '/article',element: <div>文章页面内容</div>}
]);ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router} />
);