umi入门

作者: 禹扬 | 来源:发表于2019-11-29 11:32 被阅读0次

    花时间学习了umi,算是对自己的学习总结,写了两个简单的demo:计数器和todoList。

    1. 搭建项目

    通过官网的介绍的脚手架搭建一个简单的项目,选择需要的功能的时候选择antddva后面会用到,完成后能在浏览器中 http://localhost:8000/ 看到项目。

    2. 修改基础项目结构

    1. 在 src/pages 目录下新建2个文件夹:
    • count/index.jsx
    export default function () {
      return (
        <div>count</div>
      )
    }
    
    • todoList/index.jsx
    export default function () {
      return (
        <div>todoList</div>
      )
    }
    
    1. 修改 src/pages/index.js 实现首页到这两个新建页面的跳转
    import styles from './index.css';
    import Link from 'umi/link'
    
    export default function () {
      return (
        <div className={styles.normal}>
          <div><Link to="/count">计数器</Link></div>
          <div><Link to="/todoList">todoList</Link></div>
        </div>
      );
    }
    

    现在在首页点击跳转回到404页面,这是因为还没有配置路由,路由配置在根目录下的 .umirc.js,

    routes: [
        {
          path: '/',
          component: '../layouts/index',
          routes: [
            { path: '/', component: '../pages/index' }
          ]
        }
      ],
    

    尝试使用umi的约定路由方式,umi 会根据 pages 目录自动生成路由配置,
    将.umirc.js的routes配置注释,不注释会以配置文件中的路由配置为主
    ,现在路由就能正常跳转了。

    3. 实现计数器功能

    image

    1. 实现简单的加、减

    1. 实现基本的页面结构,修改count/index.jsx
    import { Button } from 'antd'
    import styles from './index.css'
    
    function Count() {
    return (
      <div>
        <h3>数量:1</h3>
        <div className={styles.buttons}>
          <Button type="primary">加</Button>
          {' '}
          <Button type="primary">减</Button>
        </div>
      </div>
    )
    }
    
    export default Count
    
    1. 在count目录下新建model.js文件,用来进行状态数据的管理
    export default {
      namespace: 'count',
      state: {
        count: 0
      },
      reducers: {
        increment(state) {
          return {
            count: state.count + 1
          }
        },
        decrement(state) {
          return {
            count: state.count - 1
          }
        }
      },
    }
    
    1. 添加点击事件,实现数字的加、减

    count/index.jsx

    import { Button } from 'antd'
    import styles from './index.css'
    import { connect } from 'dva'
    
    function Count({ count, increment, decrement }) {
      return (
        <div>
          <h3>数量:{count}</h3>
          <div className={styles.buttons}>
            <Button type="primary" onClick={() => { increment() }}>加</Button>
            {' '}
            <Button type="primary" onClick={() => { decrement() }}>减</Button>
          </div>
        </div>
      )
    }
    
    const mapStateToProps = state => {
      return {
        count: state.count.count
      }
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        increment: () => dispatch({ type: 'count/increment' }),
        decrement: () => dispatch({ type: 'count/decrement' })
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(Count)
    

    完成上述操作,应该能实现简单的加、减功能了。

    2. 实现带延迟的加、减

    1. 在count/model.js中添加异步操作的管理,实现延迟1s加、延迟2s减。
    export default {
      ...
      effects: {
        *incrementAsync(actions, { put, call }) {
          yield call(delay, 1000)
          yield put({ type: 'increment' })
        },
        *decrementAsync(actions, { put, call }) {
          yield call(delay, 2000)
          yield put({ type: 'decrement' })
        }
      }
    }
    
    function delay(timeout) { // 延迟函数
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve()
        }, timeout)
      })
    }
    
    1. 在 count/index.jsx 中添加异步操作
    import { Button } from 'antd'
    import styles from './index.css'
    import { connect } from 'dva'
    
    function Count({ count, increment, decrement, incrementAsync, decrementAsync }) {
      return (
        <div>
          <h3>数量:{count}</h3>
          <div className={styles.buttons}>
            <Button type="primary" onClick={() => { increment() }}>加</Button>
            {' '}
            <Button type="primary" onClick={() => { decrement() }}>减</Button>
          </div>
          <div className={styles.buttons}>
            <Button type="primary" onClick={() => { incrementAsync() }}>延迟1s加</Button>
            {' '}
            <Button type="primary" onClick={() => { decrementAsync() }}>延迟2s减</Button>
          </div>
        </div>
      )
    }
    
    const mapStateToProps = state => {
      return {
        count: state.count.count
      }
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        increment: () => dispatch({ type: 'count/increment' }),
        decrement: () => dispatch({ type: 'count/decrement' }),
        incrementAsync: () => dispatch({ type: 'count/incrementAsync' }),
        decrementAsync: () => dispatch({ type: 'count/decrementAsync' })
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(Count)
    

    做完以上的步骤一个简单的计数器就基本完成了。

    4. 实现一个todoList

    image

    1. 完成静态页面

    • 在 todoList 新建componets文件夹用来存放页面组件

    components/AddTodo.jsx

    import { Input, Button } from 'antd'
    import styles from './addTodo.css'
    
    function AddTodo() {
    
      return (
        <div className={styles.addDiv}>
          <Input placeholder="请输入相关事项" className={styles.input}  />
          <Button type="primary">添加</Button>
        </div>
      )
    }
    
    
    export default AddTodo
    

    components/Todo.jsx

    import { Typography, Button } from 'antd';
    import styles from './todo.css'
    
    const { Text } = Typography;
    
    function Todo() {
      return (
        <div className={styles.todo}>
          <Text className={styles.text}>123</Text>
          <div className={styles.buttons}>
            <Button type="link">删除</Button>
            <Button type="link">修改</Button>
          </div>
        </div>
      )
    }
    
    export default Todo
    

    todolist.jsx

    import AddTodo from './components/AddTodo'
    import Todo from './components/Todo'
    import { List } from 'antd'
    import styles from './index.css'
    
    function TodoList() {
    
      return (
        <div className={styles.todoList}>
          <AddTodo></AddTodo>
          <List
            className={styles.list}
            bordered
            dataSource={[]}
            renderItem={(item) => (
              <List.Item>
                <Todo ></Todo>
              </List.Item>
            )}
          />
        </div>
      )
    }
    
    export default TodoList
    

    完成静态页面

    image

    2. 修改AddTodo组件,完成输入框输入,点击添加按钮显示输入的内容

    函数组件没有生命周期和state,这里用到了react hook的usestate

    import { Input, Button } from 'antd'
    import styles from './addTodo.css'
    import { useState } from 'react'
    
    function AddTodo() {
      const [inputValue, setInputValue] = useState('')
    
      return (
        <div className={styles.addDiv}>
          <Input placeholder="请输入相关事项" className={styles.input} onChange={(e) => { setInputValue(e.target.value) }} />
          <Button type="primary" onClick={() => { console.log(inputValue) }}>添加</Button>
        </div>
      )
    }
    
    export default AddTodo
    

    在input中输入一段文字就能在控制台看到输入的内容了。

    3. 新增models进行状态管理

    • todoList/models/todoList.js,新建初始state
    export default {
      namespace: 'todoList',
      state: {
        list: [
          {
            content: '123',
            completed: false
          }
        ],
      }
    }
    
    • 在todoList中展示state的内容
    import AddTodo from './components/AddTodo'
    import Todo from './components/Todo'
    import { List } from 'antd'
    import { connect } from 'dva'
    import styles from './index.css'
    
    function TodoList({ list }) {
    
      return (
        <div className={styles.todoList}>
          <AddTodo></AddTodo>
          <List
            className={styles.list}
            bordered
            dataSource={list}
            renderItem={(item) => (
              <List.Item>
                <Todo {...item}></Todo>
              </List.Item>
            )}
          />
        </div>
      )
    }
    
    const mapStateToProps = state => {
      return {
        list: state.todoList.list
      }
    }
    
    export default connect(mapStateToProps)(TodoList)
    

    修改Todo.js

    import { Typography, Button } from 'antd';
    import styles from './todo.css'
    
    const { Text } = Typography;
    
    function Todo({ completed, content }) {
      return (
        <div className={styles.todo}>
          <Text className={styles.text} delete={completed}>{content}</Text>
          <div className={styles.buttons}>
            <Button type="link">删除</Button>
            <Button type="link">修改</Button>
          </div>
        </div>
      )
    }
    
    export default Todo
    

    4. 完成增、删、改的功能

    • 修改 models/todoList.js, 添加reducers
    reducers: {
        addTodo(state, { payload: value }) { // 增
          return {
            list: [...state.list, { content: value, completed: false }]
          }
        },
        removeTodo(state, { payload: index }) { // 删
          let newList = state.list.filter((item, i) => i !== index)
          return {
            list: newList
          }
        },
        modifyTodo(state, { payload: index }) { // 改
          let newList = state.list.map((item, i) => {
            let flag = index === i ? !item.completed : item.completed
            return { ...item, completed: flag }
          })
          return {
            list: newList
          }
        }
      },
    
    • 修改AddTodo.jsx实现添加,将先前打印出的输入文字添加到list中来
    import { Input, Button } from 'antd'
    import styles from './addTodo.css'
    import { useState } from 'react'
    import { connect } from 'dva'
    
    function AddTodo({ addTodo }) {
      const [inputValue, setInputValue] = useState('')
    
      return (
        <div className={styles.addDiv}>
          <Input placeholder="请输入相关事项" className={styles.input} onChange={(e) => { setInputValue(e.target.value) }} />
          <Button type="primary" onClick={() => { addTodo(inputValue) }}>添加</Button>
        </div>
      )
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        addTodo: value => dispatch({ type: 'todoList/addTodo', payload: value })
      }
    }
    
    export default connect(null, mapDispatchToProps)(AddTodo)
    
    • 修改 Todo.jsx,实现删除和修改
    import { Typography, Button } from 'antd';
    import styles from './todo.css'
    import { connect } from 'dva'
    
    const { Text } = Typography;
    
    function Todo({ completed, content, index, removeTodo, modifyTodo }) {
      return (
        <div className={styles.todo}>
          <Text className={styles.text} delete={completed}>{content}</Text>
          <div className={styles.buttons}>
            <Button type="link" onClick={() => { removeTodo(index) }}>删除</Button>
            <Button type="link" onClick={() => { modifyTodo(index) }}>修改</Button>
          </div>
        </div>
      )
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        removeTodo: index => dispatch({ type: 'todoList/removeTodo', payload: index }),
        modifyTodo: index => dispatch({ type: 'todoList/modifyTodo', payload: index })
      }
    }
    
    export default connect(null, mapDispatchToProps)(Todo)
    
    • 在 todoList/index.jsx中Todo添加index
     <List
            className={styles.list}
            bordered
            dataSource={list}
            renderItem={(item, index) => (
              <List.Item>
                <Todo {...item} index={index}></Todo>
              </List.Item>
            )}
          />
    

    5. 使用后台数据替换本地list的数据

    使用umi-request来向后台发送请求,后台使用easy-mock进行数据模拟

    • 安装umi-request
    yarn add umi-request
    
    • 请求放在models/todoList中处理
    effects: {
        *getListAsync(actions, { put, call }) {
          const res = yield call(request, 'http://localhost:7300/mock/5dd4fcaebe523217231cce587/react-api/getList')
          yield put({ type: 'getList', payload: res.data.list })
        }
      }
    
    • 修改 todoList/index.jsx, 这里用到了useEffect
    import { useEffect } from 'react'
    
    function TodoList({ list, getList }) {
      useEffect(() => {
        getList()
      }, [getList])
      
    ......
    
    const mapDisPatchToProps = dispacth => {
      return {
        getList: () => dispacth({ type: 'todoList/getListAsync' })
      }
    }
    
    export default connect(mapStateToProps, mapDisPatchToProps)(TodoList)
    

    至此,两个简单的demo基本实现。

    相关文章

      网友评论

          本文标题:umi入门

          本文链接:https://www.haomeiwen.com/subject/vpwwwctx.html