React+Mobx+Antd前端项目案例
一、React简介
React 起源于 Facebook 的内部项目,目前已经开源。
特点:
- 声明式设计
- 高效、灵活
- 支持Jsx语法扩展,tsx通过–template typescript 创建也可以支持
- 使用组件概念,通过组件构建整个前端项目生态,OOP思想,容易维护和复用
- 单向响应的数据流、绑定简单,易理解
- 虚拟Dom,更加高效
安装 create-react-app
npm install -g create-react-app
yarn add -g create-react-app
创建项目(npm npx yarn都可以,方式一样)
create-react-app app-name 注意命名方式
npm create-react-app app-name
yarn create react-app app-name
二、Mobx简介:1. MobX 介绍 · MobX 中文文档
-
react状态管理
在react项目中,作为前端项目的核心,就是各个行为、动作等状态的维护、同步管理,被广泛使用的有redux和mobx,两者各有特点,均可以实现react项目中的状态管理
-
mobx简介
MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展(官方)。
- React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而MobX提供机制来存储和更新应用状态供 React 使用。
- React 提供了优化UI渲染的机制, 这种机制就是通过使用虚拟DOM来减少昂贵的DOM变化的数量。MobX 提供了优化应用状态与 React 组件同步的机制,这种机制就是使用响应式虚拟依赖状态图表,它只有在真正需要的时候才更新并且永远保持是最新的。
-
mobx核心要点
-
定义状态并使其可观察
// 通过observable可以轻松创建一个可监听、观察的状态 import {observable} from 'mobx'; var appState = observable({ timer: 0 });
-
创建视图以响应状态的变化
// observer: 创建观察者,可以监听状态并做出响应、更新 import {observer} from 'mobx-react'; @observer class TimerView extends React.Component { render() { return ( <button onClick={this.onReset.bind(this)}> Seconds passed: {this.props.appState.timer} </button> ); } // 更新状态 onReset() { this.props.appState.resetTimer(); } };
-
更新、维护状态
// action: 行为、动作定义,用于更新状态 appState.resetTimer = action(function reset() { appState.timer = 0; }); setInterval(action(function tick() { appState.timer += 1; }), 1000);
-
三、Antd组件库
- Ant Design 是一个致力于提升『用户』和『设计者』使用体验的设计语言 ;旨在统一中台项目的前端 UI 设计,屏蔽不 必要的设计差异和实现成本,解放设计和前端的研发资源; 包含很多设计原则和配套的组件库。
- 地址:Ant Design - 一套企业级 UI 设计语言和 React 组件库 (gitee.io)
四、Demo-TodoApp
-
创建项目
yarn create react-app Todo-App --template typescript // Todo-App是项目名称 // --template typescript 使用typescript
-
项目核心结构
. |-- MToDo // Mobx实现Todo的目录 | |-- MFilter // 过滤操作组件目录 | | |-- Filter.tsx // 过滤操作组件 | | `-- style.css // css样式 | |-- TodoOperate.tsx // 添加等操作组件 | |-- TodoView.tsx // Todo页面渲染组件 | `-- style.css // css组件 |-- MTodoApp.tsx // MTodoApp组件,对外暴露 |-- model.ts // 存放交互数据模型 `-- todoStore.ts // store存储模型 ... index.tsx //位于最外层的入口文件
-
功能组件介绍
-
todoStore.ts:核心交互数据中心,用于store存储模型,行为定义,供给全局共享和方法调用,是整个项目的数据核心
@observable: 创建被监听的对象
@action: 数据模型相应是操作方法
/* 文件目录 store/index.js */ import {observable, action, makeObservable} from 'mobx'; import moment from "moment"; import {FilterTypes, setVisibleTodos, TodoModel} from "@/pages/Mobx-ToDo/model"; class TodoStore { constructor() { makeObservable(this); //mbox 6后需要添加这个组件才会更新 } @observable // table 加载状态 loading: boolean = false; @observable // 事件列表长度 todoLen = 0; @observable // 事件列表 todos:TodoModel[] = []; // Filter组件store状态 @action filterTodo(filter:string) { if (filter === ""){ filter = FilterTypes.ALL } const newTodos = setVisibleTodos(filter, this.todos); this.loading = true; return newTodos; } // 添加todo事务方法 @action addTodo(todo: TodoModel) { this.todos.push(todo); this.todoLen++; this.loading = false; } // 更新todo事务方法 @action updateTodo = (todo:TodoModel, checked:boolean) => { this.todos.forEach((value,index,) => { if (value.id === todo.id) { this.todos[index].completed = checked; } }); this.loading = false; } // 删除todo事务方法 @action deleteTodo(todo: TodoModel) { this.todos.forEach((value,index,) => { if (value.id === todo.id) { delete this.todos[index]; this.todoLen--; } }); this.loading = false; } // 重置清空数据 @action resetTodo() { this.todos = []; this.todoLen = 0; this.loading = false; } // 触发实时更新渲染 @action getNow() { // 设置阻止实时刷新,否则会消耗CPU资源 this.loading = false; } } const todoStore = new TodoStore() setInterval(() => { todoStore.getNow() }) export default todoStore;
-
model.ts: 基本数据模型、依赖行为定义
1.定义store操作的对象数据结构,以及相关的依赖方法
2.是整个功能的操作对象模型中心,提供给store操作
export const ADD_TODO:string = 'TODO_ADD'; export const UPDATE_TODO:string = 'TODO_UPDATE'; export const REMOVE_TODO:string = 'TODO_REMOVE'; export const RESET_TODO:string = 'TODO_RESET'; export const SET_FILTER = 'FILTER/SET'; // 全局变量 export const FilterTypes = { ALL: '全部', COMPLETED: '已完成', UNCOMPLETED: '未完成' } export const setFilter = (filterType:string) => ({ type: SET_FILTER, filter: filterType }); export interface TodoModel { id?: number; key?: number; title?: string; completed: boolean; type: string; colorType?: string } export const setVisibleTodos = (filter:string, todos:TodoModel[]) => { switch (filter) { case FilterTypes.ALL: return todos.filter((item:TodoModel) => item.completed||!item.completed); case FilterTypes.COMPLETED: return todos.filter((item:TodoModel) => item.completed); case FilterTypes.UNCOMPLETED: return todos.filter((item:TodoModel) => !item.completed); default: return todos; } }
-
MTodoApp:对外暴露todoView组件,一般是index.tsx(用户自行更改)
1.对外暴露功能,外部访问的门户
2.可以支持/提供懒加载功能,如代码所示
import {lazy} from "react"; const TodoTable = lazy(() => import('@/pages/Mobx-ToDo/MToDo/TodoView') ) export default TodoTable;
-
MFilter: 过滤组件,筛选过滤todo状态:完成、待定、全部
图示:
代码:
本模块主要是封装一个函数组件,props绑定一个父组件的filterData方法,然后传入对应的过滤参数,进行过滤,在单击对应按钮后触发。
css为同级目录下的页面样式调整文件
import React from 'react'; import {FilterTypes} from "@/pages/Mobx-ToDo/model"; import {Button} from "antd"; import './style.css'; const FilterView = (props:any) => { const {filterData} = props return ( <p className="filterView"> 过滤选项: <Button className={"button-style"} onClick={()=>filterData(FilterTypes.ALL)}>全部</Button> <Button className={"button-style"} onClick={()=>filterData(FilterTypes.COMPLETED)}>完成</Button> <Button className={"button-style"} onClick={()=>filterData(FilterTypes.UNCOMPLETED)}>待定</Button> </p> ); } export default FilterView;
-
TodoOperate:用于todo事务的添加,重置等功能
图示:
代码:
主要提供两个组件,由于功能不复杂,因此代码简单,主要用于介绍本文功能
-
OperateTodoItem:用于顶部的添加事务、重置事务
-
UpdateTodoItem: 用于选择、删除事务功能
import React, {useState} from "react"; import {Button, Checkbox, Form, Input} from "antd"; import {ADD_TODO, REMOVE_TODO, RESET_TODO, UPDATE_TODO} from "@/pages/Mobx-ToDo/model"; export const OperateTodoItem = (props:any) => { const {addData} = props; const [value, setValue] = useState("默认事项"); const onInputChange = (event:any) => { setValue(event.target.value); } return ( <div> <Form className={"from-cell"}> <Input className={"input-area"} onChange={onInputChange} value={value}/> <Button className={"button-area"} type="primary" htmlType="submit" onClick={() => { addData(ADD_TODO,value) setValue("") }}> 添加 </Button> <Button className={"button-area"} type="primary" onClick={() => addData(RESET_TODO)}>重置</Button> </Form> </div> ) } export const UpdateTodoItem = (props:any) => { const {updateData, data} = props; const checkedProp = data.completed ? {checked: true} : {checked:false}; return ( <div> <Checkbox {...checkedProp} onChange={(event:any) =>{ event.preventDefault(); updateData(UPDATE_TODO, data, event.target.checked); }} /> <Button key={data.index} onClick={(event:any)=>{ event.preventDefault(); updateData(REMOVE_TODO, data, false) }}>删除</Button> </div> ) }
-
-
TodoView: 核心渲染页面,结合前面几个组件完成整个功能页面的渲染
图示
代码
使用antd组件库,使用Table组件完成todo列表的渲染,结合FilterView,OperateTodoItem组件完成完整功能
- 定义表格数据基本结构:columns,其中操作列使用< UpdateTodoItem / >,完成每条记录的选中、删除等功能
- OperateTodoItem: 数据添加、重置
- FilterView: 数据过滤
- Table: 数据展示
import React, {useEffect, useState} from "react"; import {inject, observer} from "mobx-react"; import {OperateTodoItem, UpdateTodoItem} from "@/pages/Mobx-ToDo/MToDo/TodoOperate"; import {ADD_TODO, REMOVE_TODO, RESET_TODO, UPDATE_TODO, TodoModel, FilterTypes} from "@/pages/Mobx-ToDo/model"; import FilterView from "@/pages/Mobx-ToDo/MToDo/MFilter/Filter"; import './style.css' import {ColumnsType} from "antd/es/table"; import {Table} from "antd"; const TodoView = inject("store")( observer( (props:any)=>{ let { store } = props const [dataSource,setDataSource] = useState([]) const [filterType, setFilterType] = useState("") const columns: ColumnsType<TodoModel> = [ { title: "事项id", dataIndex: "id", key: "id", align: "center", }, { title: "事项名称", dataIndex: "title", key: "title", align: "center", }, { title: "完成状态", key: "completed", dataIndex: "completed", align: "center", render: (text:boolean, record:any, index:number) => { if (text) { return "完成" }else { return "未完成" } } }, { title: "类型", dataIndex: "type", key: "type", align: "center", }, { title: "操作", dataIndex: "operate", key: "operate", align: "center", render: (text:any, record:TodoModel) => ( <div> <UpdateTodoItem data={record} updateData ={updateData} /> </div> ) } ]; const addData = (type:string, title:string) => { switch (type) { case ADD_TODO: let index = store.todos.length>= 0? store.todos.length:0; let todo: TodoModel = { id: index, key: index, type: ADD_TODO, title: `${title}`, completed: false, colorType: "", } store.addTodo(todo) setDataSource(store.filterTodo(filterType)) break; case RESET_TODO: store.resetTodo() setDataSource(store.filterTodo(filterType)) break; default: } } const updateData = (type:any, todo: TodoModel, checked: boolean=false) => { switch (type) { case REMOVE_TODO: store.deleteTodo(todo) setDataSource(store.filterTodo(filterType)) break; case UPDATE_TODO: store.updateTodo(todo, checked) setDataSource(store.filterTodo(filterType)) break; } } const filterData = (type:string) => { if (type === ""){ type = FilterTypes.ALL } setFilterType(type) setDataSource(store.filterTodo(filterType)) } const refresh = () => { if (store.loading){ setDataSource(store.filterTodo(filterType)) } } useEffect( () =>{ refresh() } ) return ( <> <div className={"header-cell"} /> <OperateTodoItem addData ={ addData } /> <FilterView filterData ={ filterData }/> <Table className={"table-cell"} dataSource= { dataSource } columns={ columns } rowClassName={() => 'editable-row'} /> </> ) }) ) export default TodoView;
-
index.tsx
位于最外层的入口文件
import React from 'react'; import ReactDOM from 'react-dom/client'; import reportWebVitals from './reportWebVitals'; import {Provider} from "mobx-react"; import todoStore from "@/pages/Mobx-ToDo/todoStore"; import MTodoApp from "@/pages/Mobx-ToDo/MTodoApp"; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <Provider store={todoStore}> <MTodoApp /> </Provider> </React.StrictMode> ); reportWebVitals();
-
代码仓库:Lxy0707s/todo-app: 基于react自学的todo待办列表管理示例,包含mobx和redux版本,其中mobx版本对接了antd完成了二次美化 (github.com)
-