花時(shí)間學(xué)習(xí)了umi笔链,算是對自己的學(xué)習(xí)總結(jié)珠闰,寫了兩個(gè)簡單的demo:計(jì)數(shù)器和todoList凭需。
1. 搭建項(xiàng)目
通過官網(wǎng)的介紹的腳手架搭建一個(gè)簡單的項(xiàng)目辙谜,選擇需要的功能的時(shí)候選擇antd俺榆、dva后面會用到,完成后能在瀏覽器中 http://localhost:8000/ 看到項(xiàng)目筷弦。
2. 修改基礎(chǔ)項(xiàng)目結(jié)構(gòu)
- 在 src/pages 目錄下新建2個(gè)文件夾:
- count/index.jsx
export default function () {
return (
<div>count</div>
)
}
- todoList/index.jsx
export default function () {
return (
<div>todoList</div>
)
}
- 修改 src/pages/index.js 實(shí)現(xiàn)首頁到這兩個(gè)新建頁面的跳轉(zhuǎn)
import styles from './index.css';
import Link from 'umi/link'
export default function () {
return (
<div className={styles.normal}>
<div><Link to="/count">計(jì)數(shù)器</Link></div>
<div><Link to="/todoList">todoList</Link></div>
</div>
);
}
現(xiàn)在在首頁點(diǎn)擊跳轉(zhuǎn)回到404頁面肋演,這是因?yàn)檫€沒有配置路由,路由配置在根目錄下的 .umirc.js烂琴,
routes: [
{
path: '/',
component: '../layouts/index',
routes: [
{ path: '/', component: '../pages/index' }
]
}
],
嘗試使用umi的約定路由方式,umi 會根據(jù) pages 目錄自動生成路由配置蜕乡,
將.umirc.js的routes配置注釋奸绷,不注釋會以配置文件中的路由配置為主
,現(xiàn)在路由就能正常跳轉(zhuǎn)了层玲。
3. 實(shí)現(xiàn)計(jì)數(shù)器功能
1. 實(shí)現(xiàn)簡單的加号醉、減
- 實(shí)現(xiàn)基本的頁面結(jié)構(gòu),修改count/index.jsx
import { Button } from 'antd'
import styles from './index.css'
function Count() {
return (
<div>
<h3>數(shù)量:1</h3>
<div className={styles.buttons}>
<Button type="primary">加</Button>
{' '}
<Button type="primary">減</Button>
</div>
</div>
)
}
export default Count
- 在count目錄下新建model.js文件辛块,用來進(jìn)行狀態(tài)數(shù)據(jù)的管理
export default {
namespace: 'count',
state: {
count: 0
},
reducers: {
increment(state) {
return {
count: state.count + 1
}
},
decrement(state) {
return {
count: state.count - 1
}
}
},
}
- 添加點(diǎn)擊事件畔派,實(shí)現(xiàn)數(shù)字的加、減
count/index.jsx
import { Button } from 'antd'
import styles from './index.css'
import { connect } from 'dva'
function Count({ count, increment, decrement }) {
return (
<div>
<h3>數(shù)量:{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)
完成上述操作润绵,應(yīng)該能實(shí)現(xiàn)簡單的加线椰、減功能了。
2. 實(shí)現(xiàn)帶延遲的加尘盼、減
- 在count/model.js中添加異步操作的管理憨愉,實(shí)現(xiàn)延遲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) { // 延遲函數(shù)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, timeout)
})
}
- 在 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>數(shù)量:{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)
做完以上的步驟一個(gè)簡單的計(jì)數(shù)器就基本完成了配紫。
4. 實(shí)現(xiàn)一個(gè)todoList
1. 完成靜態(tài)頁面
- 在 todoList 新建componets文件夾用來存放頁面組件
components/AddTodo.jsx
import { Input, Button } from 'antd'
import styles from './addTodo.css'
function AddTodo() {
return (
<div className={styles.addDiv}>
<Input placeholder="請輸入相關(guān)事項(xiàng)" 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
完成靜態(tài)頁面
2. 修改AddTodo組件,完成輸入框輸入午阵,點(diǎn)擊添加按鈕顯示輸入的內(nèi)容
函數(shù)組件沒有生命周期和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="請輸入相關(guān)事項(xiàng)" className={styles.input} onChange={(e) => { setInputValue(e.target.value) }} />
<Button type="primary" onClick={() => { console.log(inputValue) }}>添加</Button>
</div>
)
}
export default AddTodo
在input中輸入一段文字就能在控制臺看到輸入的內(nèi)容了。
3. 新增models進(jìn)行狀態(tài)管理
- todoList/models/todoList.js,新建初始state
export default {
namespace: 'todoList',
state: {
list: [
{
content: '123',
completed: false
}
],
}
}
- 在todoList中展示state的內(nèi)容
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實(shí)現(xiàn)添加奋单,將先前打印出的輸入文字添加到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="請輸入相關(guān)事項(xiàng)" 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锉试,實(shí)現(xiàn)刪除和修改
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. 使用后臺數(shù)據(jù)替換本地list的數(shù)據(jù)
使用umi-request來向后臺發(fā)送請求,后臺使用easy-mock進(jìn)行數(shù)據(jù)模擬
- 安裝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)
至此呆盖,兩個(gè)簡單的demo基本實(shí)現(xiàn)。