React+Mobx+Antd前端项目实战

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组件库
四、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)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玉言心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值