1. Redux 介绍
javascript
// 创建一个简单的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 快速体验
javascript
<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>
计数器实现
- 定义 reducer 函数,根据 action 返回新状态。
- 使用 createStore 传入 reducer 生成 store 实例。
- 用 store 的 subscribe 方法订阅数据变化。
- 通过 store 的 dispatch 方法提交 action 触发变化。
- 利用 store 的 getState 方法获取最新状态更新视图。
数据流架构
javascript
// 定义state
const initialState = { count: 0 };
// 定义action
const incrementAction = { type: 'INCREMENT' };
// 定义reducer
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
default:
return state;
}
}
- 包含 state(存放数据对象)、action(描述数据修改对象)、reducer(根据 action 更新 state 的函数)三个核心概念。
3. Redux 与 React
javascript
# 创建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
javascript
// 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 传参
javascript
// 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 处理
javascript
// 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. 路由快速上手
javascript
// 假设已有一个简单的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 />);
前端路由概念
- 一个路径对应一个组件,访问路径时对应组件在页面渲染。
开发环境创建
javascript
# 使用CRA创建项目
npm create-react-app react-router-pro
# 安装最新的ReactRouter包
npm i react-router-dom
# 启动项目
npm run start
- 使用 CRA 创建项目,安装 React Router 包,启动项目。
快速开始
javascript
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. 抽象路由模块
javascript
// 假设这是一个简单的路由模块文件
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. 路由导航
概念
- 路由系统中多个路由间需进行跳转及参数传递通信。
声明式导航
javascript
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
属性为路由路径实现跳转,传参可字符串拼接。
编程式导航
javascript
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. 导航传参
javascript
import React from 'react';
import { useNavigate } from 'react-router-dom';
const ProductDetailButton = () => {
const navigate = useNavigate();
const productId = 123; // 假设这是一个产品ID
const handleNavigate = () => {
navigate(`/product/${productId}`);
};
return <button onClick={handleNavigate}>查看产品详情</button>;
};
- (文档中未详细说明相关内容,可能需要进一步查看实际传参相关代码)
9. 嵌套路由配置
概念
-
一级路由中内嵌其他路由为嵌套路由,内嵌的为二级路由。
javascriptimport 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/>
组件配置二级路由渲染位置。javascriptimport 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
可默认渲染。javascriptimport 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
组件,在路由表末尾用*
作为路径配置路由。javascriptimport 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
事件,需后端支持。javascriptimport 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
事件,无需后端支持。javascriptimport 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} /> );
-