news 2026/9/19 21:47:09

TodoMVC React-Redux 示例深度解析:Redux 单向数据流与 Webpack 工程化实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TodoMVC React-Redux 示例深度解析:Redux 单向数据流与 Webpack 工程化实践

TodoMVC React-Redux 示例深度解析:Redux 单向数据流与 Webpack 工程化实践

【免费下载链接】todomvcHelping you select a JavaScript framework - Todo apps for React.js, Angular, Vue and many more项目地址: https://gitcode.com/gh_mirrors/to/todomvc

本文基于 TodoMVC 仓库中的 examples/react-redux/README.md 展开,系统讲解该示例如何用 React + Redux + React-Redux 三件套实现标准 TodoMVC 应用,并结合 store 定义、组件实现 与 Webpack 构建配置 还原其数据流、组件连接关系与构建流程,读完可掌握 Redux Toolkit 的 slice/selectors 组织方式与 TodoMVC 官方应用的工程化构建链路。

应用定位:React、Redux 与 React-Redux 的分工

官方 README 对该示例的定位是:这是一个使用 React 结合 Redux 实现的 TodoMVC 应用,三个核心依赖各有明确职责:

  • React:用于创建用户界面的 JavaScript 库;
  • Redux:负责集中管理应用状态(centralize state);
  • React-Redux:让 Redux 与 React 的组件模型协作,把 store 接入组件树。

从 examples/react-redux/package.json 可以看到当前仓库锁定的实际依赖版本:reactreact-dom为 19.x,react-redux为 9.x,状态管理基于@reduxjs/toolkit2.x,并引入了react-router-dom7.x 用于过滤路由,todomvc-app-csstodomvc-common提供官方统一的界面样式与测试工具。

数据流设计:Redux 视角下的 MVC 映射

README 的“Implementation details”部分给出了这段实现的核心设计哲学:Redux 模式类似于 MVC 模式,主要区别在于 Redux 是单向(unidirectional)数据流。其完整流程为:

  1. Redux 用actions发出一次变更请求;
  2. reducers捕获 action 并更新中央 store;
  3. store 中的状态更新后,View收到新状态并向用户呈现变化。

README 进一步给出了 Redux 与经典 MVC 的逐项对应关系,这是理解该示例代码结构的关键:

角色Redux 实现MVC 中的角色
ModelRedux store维护应用的数据与行为
ViewReact UI 组件在界面中展示模型
ControllerReact connected components + Redux reducers作为视图与模型之间的接口

Store 层:用 createSlice 集中定义状态与动作

按上述映射,"Model" 的全部实现集中在 examples/react-redux/src/store.js。该文件用 Redux Toolkit 的createSlice定义了一个名为todos的 slice,初始状态是空数组[],并包含 6 个 reducer(源码 L3-L36):

Action行为源码细节
addTodo向数组尾部追加新 todo使用prepare回调在派发动作时生成{ id: nanoid(), text, completed: false }载荷(L7-L14),用nanoid生成唯一 id
deleteTodo按 id 过滤删除state.filter((todo) => todo.id !== action.payload)
editTodo更新指定 todo 的文本find找到目标再改写text
toggleTodo翻转单个 todo 的完成状态todo.completed = !todo.completed
toggleAll全部完成则全部取消,否则全部完成通过state.every((todo) => todo.completed)判断当前是否"已全部完成"再批量翻转
clearCompleted清除所有已完成的 todostate.filter((todo) => !todo.completed)

这些 action 在 L38 被统一解构导出,供各组件dispatch调用。store 本身由configureStore创建,todos作为唯一顶层 reducer(L40-L42),印证了 README 所说的"中央 store"设计。

派生状态:过滤器与 selectors

README 强调状态更新后视图能反映变化,TodoMVC 的过滤(All / Active / Completed)正是派生状态的典型场景。store.js 中没有把过滤结果写入 state,而是用 selector 即时计算(L44-L55):

  • FILTERS是路径到过滤函数的映射:"/""/all"返回全部、"/active"只留未完成项、"/completed"只留已完成项;
  • selectVisibleTodos(pathname)返回一个接收state的 selector,根据当前路由路径应用对应过滤函数;
  • selectCompletedCount统计已完成数量,selectTodos直接读取state.todos

这种"原始状态 + 派生 selector"的组织方式,使得过滤逻辑完全由路由路径驱动,组件侧无需维护额外的 UI 状态。

组件层:Provider 挂载与 connected components

入口:把 store 注入组件树

examples/react-redux/src/index.js 是应用入口(L11-L17):

createRoot(document.getElementById("root")).render( <Provider store={store}> <HashRouter> <App /> </HashRouter> </Provider> );

这里体现了两个关键决策:

  1. Provider将 store 注入整棵组件树,任何子组件都可通过useDispatch/useSelector访问状态——这就是 README 中"React-Redux 与 React 组件模型协作"的具体落点;
  2. HashRouter使用哈希路由(#/#/active#/completed),与 public/index.html 中静态页面的定位方式一致,无需服务器端路由配置。

入口同时引入todomvc-app-css/index.csstodomvc-common/base.css,保证界面遵循 TodoMVC 官方视觉规范。

App 与页面骨架

app.jsx 结构极简,仅由<Header /><Main />两个区块组成,对应 TodoMVC 规范的输入区与主体区;<section class="todoapp" id="root">作为挂载点声明在 public/index.html 中,并带有data-framework="react-redux"标记。

Header:派发动作的起点

header.jsx 展示了"发出 action"一侧的典型写法:通过useDispatch()获取 dispatch 函数,在输入框保存回调里调用dispatch(addTodo(text)),并对空文本做拦截。数据流由此启动:组件 → action → reducer → store。

Main:消费状态的 connected component

main.jsx 是"View 接收新状态"的典型代表,它同时消费状态(useSelector)与发出动作(useDispatch):

  • useSelector(selectTodos)取全量列表用于计算activeCount与空状态判断;
  • useSelector(selectVisibleTodos(location.pathname))配合useLocation(),让可见列表随路由路径实时切换——过滤器链接点击后 URL 变化,selector 重算,列表随之更新,这正是 README 所述"store 更新 → view 呈现变化"闭环的过滤场景实现;
  • "Toggle All" 复选框的checkedcompletedCount === todos.length派生,onChange派发toggleAll()

Item 与 TextInput:局部状态与 Redux 状态的边界

item.jsx 展示了状态划分的细节:"是否处于编辑态"属于组件私有状态useState管理editing),而 todo 本身的数据变更全部走 Redux(toggleTodo/editTodo/deleteTodo)。编辑保存时的规则值得注意:文本为空则dispatch(deleteTodo(todo.id)),否则dispatch(editTodo({ id, text }))。此外该组件用memo包裹,配合不可变更新减少列表项重渲染。

footer.jsx 消费父组件传入的计数 props 与location.pathname,渲染剩余项数、三个过滤链接(#/#/active#/completed)以及"Clear completed"按钮(派发clearCompleted())。

text-input.jsx 是唯一不直接接触 Redux 的组件,它通过onSave/onCancel回调把职责交给调用方,处理 Enter 提交、Escape 取消(编辑态)与失焦保存等行为,是一个纯 UI 的可复用控件。

存储方案:纯内存数组

README 明确指出:"存储方案使用一个内存数据对象,实现一个简单数组来保存 todos。" 源码完全印证了这一点:

  • store.js 的initialState: []就是唯一的初始状态,没有任何 localStorage 读写或网络同步逻辑;
  • 每次 reducer 执行后数组被替换或改写,状态只存在于 store 生命周期内,刷新页面即清空。

选择纯内存存储是刻意的取舍——TodoMVC 示例的核心目标是演示框架的状态管理模式,而非持久化方案。若要在真实项目中扩展,可在此基础上接入持久化中间件或 Toolkit 的configureStore序列化能力,但当前仓库实现中并不包含这部分。

构建流程:Webpack 三段式配置

README 的"Build steps"说明:构建静态文件使用 webpack,它会压缩并优化输出文件,并把所有必要文件拷贝到dist目录。仓库中这由三个配置文件协作完成:

公共配置:入口、模板与转译

webpack.common.js 定义了基础链路:

  • 入口src/index.js(L5-L7);
  • HTML 模板HtmlWebpackPluginpublic/index.html为模板生成最终页面(L9-L12);
  • 输出dist/[name].bundle.js,并开启clean: true保证每次构建前清空输出目录(L14-L18);
  • 转译babel-loader处理js/jsx文件,配置@babel/preset-env(targets: defaults)与@babel/preset-reactruntime: "automatic",即无需手动import React的新 JSX 运行时)(L24-L36);
  • 静态资源:图片类走asset/resource类型处理(L38-L40)。

开发配置:热更新与内联源码映射

webpack.dev.js 通过webpack-merge合并公共配置后追加:

  • mode: "development"devtool: "inline-source-map",便于调试;
  • CSS 走style-loader+css-loader(以 JS 注入样式,支持热更新);
  • devServer.static: "./dist"指定静态服务目录。

生产配置:压缩、抽离与拷贝

webpack.prod.js 对应 README 中"minifies and optimizes"的描述:

  • MiniCssExtractPlugin把 CSS 从 JS 中抽离为独立的[name].css文件;
  • CopyPluginnode_modules/todomvc-common/base.js拷贝到输出根目录(L17-L21)——这正是 public/index.html 中<script src="./base.js">的来源,用于 TodoMVC 测试工具识别当前框架;
  • optimization.minimize开启压缩,minimizer 为CssMinimizerPluginTerserPlugin,分别负责 CSS 与 JS 的压缩;
  • devtool: "source-map"生成独立映射文件。

运行要求与本地预览

环境要求

README 给出的最低环境要求为:

Node (min version: 18.13.0) NPM (min version: 8.19.3)

唯一前提是安装 Node,以便安装依赖并运行脚本启动本地服务器。需要补充一点:package.json 的engines字段当前声明为node >=20.19.0npm >=10.0.0(L6-L9),比 README 写的下限更严格;按 npm 的 engines 校验行为,建议在 Node 20+ 环境下运行,以避免与锁定依赖(如 React 19、Webpack 5.106)出现兼容性问题。

npm scripts 一览

package.json 定义了三个脚本(L10-L14):

脚本命令作用
npm run buildwebpack --config webpack.prod.js走生产配置,产出压缩后的dist静态文件
npm run devwebpack serve --open --config webpack.dev.js启动 webpack-dev-server 并自动打开浏览器
npm run servehttp-server ./dist -p 7002 -c-1 --cors用 http-server 以 7002 端口、禁用缓存(-c-1)、开启 CORS 的方式静态托管构建产物

本地预览步骤

README 给出的标准流程:

terminal: 1. npm install 2. npm run dev browser: 1. http://localhost:7001/

即在examples/react-redux目录下安装依赖后启动开发服务器,浏览器访问本地地址即可查看应用。其中dev脚本使用webpack serve --open会自动打开页面;注意 webpack.dev.js 的devServer配置只显式指定了static: "./dist"目录、未显式写死端口,实际访问端口以启动时 dev-server 控制台输出或项目约定为准,README 记录的入口地址为http://localhost:7001/。构建后如需静态托管,可运行npm run serve直接服务dist目录。

小结

这个示例完整地演示了 README 所述的设计原则:Redux 单向数据流替代了传统 MVC 的双向耦合——store.js 以 slice + selectors 承担 Model,header.jsx、main.jsx、item.jsx、footer.jsx 作为 connected components 承担 Controller 与 View 的混合职责;存储采用纯内存数组,刻意剥离持久化复杂度;构建侧则用 webpack 三段式配置(common/dev/prod)实现了开发期热更新与生产期压缩抽离的分离。对学习者而言,它可以作为理解 Redux Toolkit slice 组织、selector 派生状态、React-Redux hooks 连接方式以及 TodoMVC 规范 UI 结构的一个小而完整的参照实现。

【免费下载链接】todomvcHelping you select a JavaScript framework - Todo apps for React.js, Angular, Vue and many more项目地址: https://gitcode.com/gh_mirrors/to/todomvc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 21:46:45

论文降重技术解析:语义改写与查重优化实践

1. 论文降重的技术痛点与行业现状学术论文写作中&#xff0c;查重率过高一直是困扰研究者的难题。传统降重方法主要依赖同义词替换、语序调整等表面修改手段&#xff0c;效果有限且容易破坏原文的学术严谨性。更棘手的是&#xff0c;随着查重系统算法的不断升级&#xff0c;简单…

作者头像 李华
网站建设 2026/9/19 21:44:37

Windows下nvm-windows完全指南:安装、切换与镜像加速

我在 Windows 上折腾 Node.js 这些年前前后后换过几十个版本。有一阵子全靠官网安装包来回卸载重装&#xff0c;结果就是各种环境冲突&#xff0c;直到用上 nvm-windows 才算是真正解脱。这篇就把 Windows 下 nvm 的完整玩法一次讲清楚&#xff0c;从安装、环境变量&#xff0c…

作者头像 李华
网站建设 2026/9/19 21:42:59

HTML即视频:HyperFrames实现确定性MP4生成原理

1. 项目概述&#xff1a;当HTML不再是静态页面&#xff0c;而是一台视频生成引擎你有没有试过&#xff0c;在浏览器里写一段<div>Hello World</div>&#xff0c;刷新一下&#xff0c;页面就出来了&#xff1b;但这次&#xff0c;你写完 HTML&#xff0c;点个按钮&a…

作者头像 李华
网站建设 2026/9/19 21:42:49

Excel筛选与高级筛选:从基础操作到条件区域的完整指南

想清楚这个问题的人&#xff0c;基本都能把Excel从“记事本”用成“数据库”。数据筛选和高级筛选&#xff0c;看着只是点几下鼠标&#xff0c;实际背后是一套完整的过滤逻辑。日常工作里&#xff0c;无论是面对上千行的销售明细&#xff0c;还是从一堆考勤记录里挑出异常人员&…

作者头像 李华