前言:
本文基于 React+TypeScript+Mobx+AntDesignMobile 技术栈, 使用 Create-React-App 脚手架进行一个移动端项目搭建,主要介绍项目搭建过程和相关配置, 以及状态管理工具 Mobx 的使用, 最后实现点击按钮数字 + 1 和一个简单的 TodoList 小功能, 希望能对大家有所帮助。GitHub 代码地址
项目搭建具体步骤:
- 安装 Create-React-App 脚手架工具, 已经安装的同学请忽略
npm install create-react-app -g
- 创建初始项目
create-react-app my-app --typescript
注意:
如果同学你是参考 typescript 官方文档进行 react 项目搭建, 里面有用create-react-app my-app --scripts-version=react-scripts-ts
命令创建的, 千万别用, 现在已经过时了,
webpack 版本有问题, 后续配置各种想不到的坑 TypeScript 中文官网 -
引入 AntDesignMobile, 实现组件按需加载
本步骤官网有比较详细的介绍, 我就不一一列举配置过程了, 建议大家不要 eject 暴露所有内建配置, 后续版本升级维护可能比较麻烦, 推荐使用 react-app-rewired 插件即可配置;AntDesignMobile 官网
-
安装 React 路由, 状态管理工具 mobx, 配置 sass
npm install history @types/history react-router-dom @types/react-router-dom // 安装 react 路由 npm install mobx-react mobx // 安装 mobx npm install node-sass // 安装 sass 工具, 安装之后无需另外配置 sass, 脚手架工具已经集成了
- 基本配置完成, 运行项目
npm run start
React 状态管理工具 Mobx 介绍:
Mobx 是一个功能强大,基于面向对象编程方式的一个状态管理工具, 上手相对比较容易。就连 Redux 的作者也曾经向大家推荐过它,对 TypeScript 支持比较友好, 参考官网一张流程图:
Mobx 中文官网
下面介绍几个 Mobx 核心概念
Observable state(可观察的状态)
MobX 为现有的数据结构 (如对象,数组和类实例) 添加了可观察的功能。通过使用 @observable 装饰器 (ES.Next) 来给你的类属性添加注解就可以简单地完成这一切。
import {observable} from "mobx";
class Todo {id = Math.random();
@observable title = "";
@observable finished = false;
}
Computed values(计算值)
使用 MobX,你可以定义在相关数据发生变化时自动更新的值。通过 @computed 装饰器或者利用 (extend)Observable 时调用 的 getter / setter 函数来进行使用, 下面代码中当 queue 或者 refresh 发生变化时,MobX 会监听数据变化确保, 通过 Computed 触发 fooProps 方法自动更新。
import React from 'react';
import {observable, isArrayLike, computed, action, autorun, when, reaction,runInAction} from "mobx";
import {observer} from 'mobx-react';
// 定义数据 Store
class Store {
@observable queue:number = 1;
@action refresh = ():void => {
this.queue += 1;
console.log('this.queue ->', this.queue);
}
@computed get fooProps():any {
return {
queue: this.queue,
refresh: this.refresh
};
}
}
Actions(动作)
任何应用都有动作。动作是任何用来修改状态的东西,mobx 推荐将修改被观测变量的行为放在 action 中。action 只能影响正在运行的函数,而无法影响当前函数调用的异步操作, 参考官方文档用法:
action(fn)
action(name, fn)
@action classMethod() {}
@action(name) classMethod () {}
@action boundClassMethod = (args) => {body}
@action(name) boundClassMethod = (args) => {body}
@action.bound classMethod() {}
action 装饰器 / 函数遵循 javascript 中标准的绑定规则。但是,action.bound 可以用来自动地将动作绑定到目标对象。注意,与 action 不同的是,(@)action.bound 不需要一个 name 参数,名称将始终基于动作绑定的属性。class Ticker {
@observable tick = 0
@action.bound
increment() {this.tick++ // 'this' 永远都是正确的}
}
const ticker = new Ticker()
setInterval(ticker.increment, 1000)
利用 Mobx 作为状态管理, 实现两个小功能
点击按钮 +1
import React from 'react';
import {observable, isArrayLike, computed, action, autorun, when, reaction,runInAction} from "mobx";
import {observer} from 'mobx-react';
import {Button} from 'antd-mobile';
import './index.scss';
// 定义数据 Store, 用 Mobx 作为状态管理工具
class Store {
@observable queue:number = 1;
@action refresh = ():void => {
this.queue += 1;
console.log('this.queue ->', this.queue);
}
@computed get fooProps():any {
return {
queue: this.queue,
refresh: this.refresh
};
}
}
// ts 组件接收父组件传递过来的数据必须定义接口类型, 否则报错
interface BarProps{queue :number}
// @observer 修饰类,Bar 组件接受 Foo 组建传过来的 queue 属性
@observer
class Bar extends React.Component<BarProps>{render() {const {queue} = this.props
return (<div className="queue">{queue}</div>
)
}
}
interface FooProps {
queue: number,
refresh():void}
// Foo 组件接收来自 Add 组件的 store 数据
@observer
class Foo extends React.Component<FooProps>{render() {const {queue,refresh} = this.props;
return (
<div>
<Button type="primary" onClick = {refresh}>Refresh</Button>
<Bar queue = {queue} />
</div>
)
}
}
// 初始化 store 数据, 传递给 Foo 组件
const store = new Store();
class Add extends React.Component{render() {
return (
<div>
<h2 className="add"> hello react-ts-mobx</h2>
<Foo queue = {store.queue} refresh = {store.refresh} />
</div>
)
}
}
export default observer(Add)
TodoList 功能
import React from 'react';
import {observable, isArrayLike, computed, action, autorun, when, reaction,runInAction} from "mobx";
import {observer} from 'mobx-react';
import './index.scss';
// 定义 Todo 数据类型
class Todo {id:number = new Date().getTime();
title:string = '';
finished:boolean = false;
constructor(title:string) {this.title = title;}
}
// Store 数据方法管理
class Store {
@observable title:string = '';
@observable todos:Todo[] =[];
// 添加 Todo 的 Title
@action createTitle (e:any) {console.log('e',e.target.value);
this.title = e.target.value;
}
// 增加 Todo 数据
@action createTodo = () => {this.todos.unshift(new Todo(this.title));
this.title = '';
}
// 删除 Todo
@action delTodo (id:number) {this.todos.forEach((item,index) => {if (item.id === id) {this.todos.splice(index,1)
}
})
}
// 监听 todos 数据变化, 显示剩余待办数量
@computed get unfinished () {return this.todos.filter(todo => !todo.finished).length;
}
}
interface TodoItemProps {
todo:any;
store:any;
}
// 每一条 Todo 数据组件
@observer
class TodoItem extends React.Component<TodoItemProps> {render() {const {todo,store} = this.props
return (
<div className="item">
<span>{todo.title}</span>
<span onClick={()=> store.delTodo(todo.id)}>X</span>
</div>
)
}
}
const store = new Store();
@observer
class TodoList extends React.Component {render() {
return (
<div>
<h2>TodoList</h2>
<header>
<input type="text" placeholder="please input" value={store.title} onChange = {e => store.createTitle(e)} />
<button onClick ={store.createTodo}>add</button>
</header>
<ul>
{store.todos.map((todo)=>{return <li key={todo.id}>
<TodoItem todo={todo} store = {store}/>
</li>
})}
</ul>
<footer>
{store.unfinished} item(s) unfinished
</footer>
</div>
)
}
}
export default TodoList
总结:
本人刚接触 TypeScript 和 Mobx 不久, 总结学习方法: 应该先熟悉一些基本概念后, 慢慢的做一些小功能, 遇到问题认真思考后再查资料或者请教别人, 反复看文档, 加强对知识的理解; 欢迎大佬们留言交流;GitHub 代码地址