笔记来源:拉勾教育 - 大前端就业集训营
文章内容:学习过程中的笔记、感悟、和经验
提示:项目实战类文章资源无法上传,仅供参考
MobX版todo案例
准备工作
- 创建并初始化一个新项目
- 创建所需要的组件,拷贝需要HTML代码、拷贝并引入CSS文件
- 将三个子组件引入到app组件中
─ src
│ ├─ components(新建)
│ │ ├─ App.js(移动)
│ │ ├─ Foot.js(新建)
│ │ ├─ Header.js(新建)
│ │ └─ Main.js(新建)
│ ├─ index.css(新建)
│ └─ index.js
// src/components/App.js 引入三个子组件
import Foot from './Foot'
import Header from './Header'
import Main from './Main'
function App() {
return (
<section className='todoapp'>
<Header />
<Main />
<Foot />
</section>
)
}
export default App
// src/index.js 引入全局css样式
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
import './index.css'
ReactDOM.render(<App />, document.getElementById('root'))
// src/components/Header.js 顶部添加任务组件
import React, { Component } from 'react'
export class Header extends Component {
render() {
return (
<header className='header'>
<h1>todos</h1>
<input className='new-todo' placeholder='What needs to be done?' />
</header>
)
}
}
export default Header
// src/components/Main.js 任务列表展示组件
import React, { Component } from 'react'
export class Main extends Component {
render() {
return (
<section className='main'>
<input className='toggle-all' type='checkbox' />
<ul className='todo-list'>
<li className='completed'>
<div className='view'>
<input className='toggle' type='checkbox' />
<label>Taste JavaScript</label>
<button className='destroy'></button>
</div>
<input className='edit' />
</li>
<li>
<div className='view'>
<input className='toggle' type='checkbox' />
<label>Buy a unicorn</label>
<button className='destroy'></button>
</div>
<input className='edit' />
</li>
</ul>
</section>
)
}
}
export default Main
// src/components/Foot.js 底部操作组件
import React, { Component } from 'react'
export class Foot extends Component {
render() {
return (
<footer className='footer'>
<span className='todo-count'>
<strong>0</strong> item left
</span>
<ul className='filters'>
<li>
<button className='selected'>All</button>
</li>
<li>
<button>Active</button>
</li>
<li>
<button>Completed</button>
</li>
</ul>
<button className='clear-completed'>Clear completed</button>
</footer>
)
}
}
export default Foot
构建MobX工作流
-
安装modx和mobx-react依赖
-
src目录下创建mobx专属目录和文件
-
打通整个mobx流程,三个子组件都需要
-
注意vscode编辑器需要配置装饰器语法支持
-
下面的所有操作都需要注意修饰器的使用
// src/MobX/todo.js 创建mobx仓库
import { observable } from 'mobx'
// mobx类
class todoData {
// 使用修饰器设置状态未可被观察
@observable list = 1
}
// 创建实例
const todoList = new todoData()
export default todoList
// src/index.js 获取仓库并传递下去
import { Provider } from 'mobx-react'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
import './index.css'
import todoList from './MobX/todo'
ReactDOM.render(
// 使用 Provider 传递仓库
<Provider todoList={todoList}>
<App />
</Provider>,
document.getElementById('root')
)
// src/components/ 三个子组件引入状态仓库
import { inject, observer } from 'mobx-react'
// 装饰器
@inject('todoList')
@observer
................
实现任务添加功能
-
顶部添加事件文本框添加键盘抬起事件,判断是否是回车键、是否不为空(trim)
-
条件满足将任务内容添加到mobx的任务列表状态中
-
添加完成后清除文本框内容
// src/MobX/todo.js 添加任务列表,添加任务action
import { action, observable } from 'mobx'
// mobx类
class todoData {
// 使用修饰器设置状态未可被观察
@observable list = []
// 添加任务
@action.bound add(todo) {
this.list.push(todo)
}
}
// 创建实例
const todoList = new todoData()
export default todoList
// src/components/Header.js 给文本框添加事件,满足条件调用添加任务action
import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'
// 装饰器
@inject('todoList')
@observer
// 顶部组件
class Header extends Component {
// 任务输入框键盘抬起事件
addtodo(e) {
// 获取任务名
const name = e.target.value.trim()
// 触发条件判定
if (e.key === 'Enter' && name.length > 0) {
// 调用action,传递参数
this.props.todoList.add({ name, completed: false })
// 重置输入框
e.target.value = ''
}
}
render() {
return (
<header className='header'>
<h1>todos</h1>
<input
className='new-todo'
placeholder='书写您要添加的任务'
// 添加键盘抬起事件
onKeyUp={e => this.addtodo(e)}
/>
</header>
)
}
}
export default Header
实现任务列表展示功能
-
可以先书写一些虚拟数据
-
列表组件获取全部任务,进行数据遍历绑定
// src/MobX/todo.js 书写一些测试数据
import { action, observable } from 'mobx'
// mobx类
class todoData {
// 使用修饰器设置状态未可被观察,写一些虚拟数据
@observable list = [
{ name: '吃饭', completed: false },
{ name: '睡觉', completed: false },
{ name: '打豆豆', completed: true },
]
// 添加任务
@action.bound add(todo) {
this.list.push(todo)
}
}
// 创建实例
const todoList = new todoData()
export default todoList
// src/components/Main.js 利用获取到的状态数据,遍历创建结构展示出来
import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'
// 装饰器
@inject('todoList')
@observer
// 中间列表组件
class Main extends Component {
render() {
// 解构对象
const { todoList } = this.props
return (
<section className='main'>
<input className='toggle-all' type='checkbox' />
<ul className='todo-list'>
{/* 遍历全部任务常见结构 */}
{todoList.list.map((todo, index) => (
// 动态设置类名
<li className={todo.completed ? 'completed' : ''} key={index}>
<div className='view'>
<input className='toggle' type='checkbox' />
<label>{todo.name}</label>
<button className='destroy'></button>
</div>
<input className='edit' />
</li>
))}
</ul>
</section>
)
}
}
export default Main
实现删除任务功能
- 点击删除按钮后触发事件,调用删除任务的action,并传递参数
// src/MobX/todo.js 添加删除时间action
// 删除任务
@action.bound remove(index) {
this.list.splice(index, 1)
}
// src/components/Main.js 给删除按钮添加点击事件,直接触发删除action
<button
className='destroy'
// 添加点击事件删除任务,直接调用action
onClick={() => todoList.remove(index)}>
</button>
实现切换任务完成状态功能
- 当点击任务前面的单选框时触发事件,调用修改状态的action,传递参数
// src/MobX/todo.js 添加修改任务状态action
// 修改任务状态
@action.bound changeCompleted(index, state) {
this.list[index].completed = state
}
// src/components/Main.js 设置动态展示任务选中状态,添加状态修改事件直接触发action
<input
className='toggle'
type='checkbox'
// 动态设置选中状态
defaultChecked={todo.completed ? true : false}
// 添加事件,直接触发action
onChange={e =>
todoList.changeCompleted(index, e.target.checked)
}
/>
计算未完成任务数量
- 使用mobx的计算值,计算未完成任务数量,并绑定给底部数量统计
// src/MobX/todo.js 添加计算未完成任务的计算值
// 统计未完成任务
@computed get completedNum() {
return this.list.filter(item => item.completed === false).length
}
// src/components/Foot.js 绑定计算值数据
<span className='todo-count'>
{/* 绑定mobx计算值 */}
<strong>{this.props.todoList.completedNum}</strong> item left
</span>
实现任务筛选功能
-
同样需要使用mobx计算值功能,添加一个筛选标记状态
-
点击筛选条件时触发action修改条件
-
根据筛选标记筛选出相应的展示列表
-
最后把原来展示任务的列表数据换绑为筛选后的计算值
-
三个筛选条件按钮根据筛选条件状态设置类名
// src/MobX/todo.js 添加筛选计算值、更晒筛选条件action和筛选条件状态值
import { action, computed, observable } from 'mobx'
// mobx类
class todoData {
// 使用修饰器设置状态未可被观察,写一些虚拟数据
@observable list = [
{ name: '吃饭', completed: false },
{ name: '睡觉', completed: false },
{ name: '打豆豆', completed: true },
]
// 筛选条件
@observable filterName = 'All'
// 添加任务
@action.bound add(todo) {
this.list.push(todo)
}
// 删除任务
@action.bound remove(index) {
this.list.splice(index, 1)
}
// 修改任务状态
@action.bound changeCompleted(index, state) {
this.list[index].completed = state
}
// 统计未完成任务
@computed get completedNum() {
return this.list.filter(item => item.completed === false).length
}
// 计算值 - 按条件筛选
@computed get filterList() {
switch (this.filterName) {
case 'all':
return this.list
case 'Active':
return this.list.filter(item => item.completed === false)
case 'Completed':
return this.list.filter(item => item.completed === true)
default:
return this.list
}
}
// 修改筛选条件
@action.bound changeFilterName(name) {
this.filterName = name
}
}
// 创建实例
const todoList = new todoData()
export default todoList
// src/components/Foot.js 三个筛选添加点击事件并动态设置类名
import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'
// 装饰器
@inject('todoList')
@observer
// 底部组件
class Foot extends Component {
render() {
return (
<footer className='footer'>
<span className='todo-count'>
{/* 绑定mobx计算值 */}
<strong>{this.props.todoList.completedNum}</strong> item left
</span>
<ul className='filters'>
{/* 三个筛选按钮添加点击事件。直接触发action修改筛选条件 */}
<li onClick={() => this.props.todoList.changeFilterName('All')}>
<button
// 三个按钮动态设置类名
className={
this.props.todoList.filterName === 'All' ? 'selected' : ''
}>
All
</button>
</li>
<li onClick={() => this.props.todoList.changeFilterName('Active')}>
<button
className={
this.props.todoList.filterName === 'Active' ? 'selected' : ''
}>
Active
</button>
</li>
<li onClick={() => this.props.todoList.changeFilterName('Completed')}>
<button
className={
this.props.todoList.filterName === 'Completed' ? 'selected' : ''
}>
Completed
</button>
</li>
</ul>
<button className='clear-completed'>Clear completed</button>
</footer>
)
}
}
export default Foot
// src/components/Main.js 展示数据更改为筛选后的数据 - 计算值
import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'
// 装饰器
@inject('todoList')
@observer
// 中间列表组件
class Main extends Component {
render() {
// 解构对象
const { todoList } = this.props
return (
<section className='main'>
<input className='toggle-all' type='checkbox' />
<ul className='todo-list'>
{/* 遍历全部任务常见结构,这里遍历筛选后的数据,而不是筛选前的数据 */}
{todoList.filterList.map((todo, index) => (
// 动态设置类名
<li className={todo.completed ? 'completed' : ''} key={index}>
<div className='view'>
<input
className='toggle'
type='checkbox'
// 动态设置选中状态
defaultChecked={todo.completed ? true : false}
// 添加事件,直接触发action
onChange={e =>
todoList.changeCompleted(index, e.target.checked)
}
/>
<label>{todo.name}</label>
<button
className='destroy'
// 添加点击事件删除任务,直接调用action
onClick={() => todoList.remove(index)}></button>
</div>
<input className='edit' />
</li>
))}
</ul>
</section>
)
}
}
export default Main
结尾
课程项目结束,但是还有一些bug可以解决,例如非All的筛选条件下更改状态会出现错误