内容简介:阅读本文章您可能收获如下:Decorator是在说的直白点Decorator就是
阅读本文章您可能收获如下:
- 配置修饰器环境
- 理解响应式编程的概念
- 正确使用mobx关键api达到维护应用程序状态的目标
2.什么是修饰器?
Decorator是在 声明阶段 实现类与类成员注解的一种语法。
说的直白点Decorator就是 添加 或者 修改 类的变量与方法。
2.1 一定需要修饰器吗?
在开始使用mobox,我们还需要纠结一个东西,就是配置环境启用ES7的修饰器语法,当然,如果你不需要修饰器,可以跳过这一部分。
如果是新手的话,建议配置,官方的MobX文档也说明了,不一定要使用修饰器,如果有人说你必须在MobX中使用decorator,那就不是真的,你也可以使用普通函数,如下:
import { decorate, observable } from "mobx"; class Todo { id = Math.random(); title = ""; finished = false; } decorate(Todo, { title: observable, finished: observable }) 复制代码
2.2 使用和不使用修饰符的对比
不使用修饰符如下:
import React, {Componnet} from 'react'; import {observe} from 'mobx-react'; // 没有使用 修饰器 class Test extends Componnet { ... } export default observe(Test); 复制代码
使用修饰符如下:
import React, {Componnet} from 'react'; import {observe} from 'mobx-react'; // 使用 修饰器 @observe class Test extends Componnet{ ... } export default Test; 复制代码
以上代码中,通过observer(App)定义类和@observer class App 装饰一个组件是一样的。
那么多个修饰符组合到一个组件上是怎样的呢?。
不使用修饰符如下:
import React, {Componnet} from 'react'; import {observe, inject} from 'mobx-react'; import {compose} from 'recompose'; // 没有使用 装饰器 class Test extends Componnet { render() { const {foo} = this.props; } } export default compose( observe, inject('foo') )(Test) 复制代码
使用修饰符如下:
import React, {Componnet} from 'react'; import {observe, inject} from 'mobx-react'; // 使用 修饰器 @inject('foo') @observe class Test extends Componnet { render() { const {foo} = this.props; } } export default Test; 复制代码
由上可以看出,如果没有修饰器的话,需要引入recompose,通过compose将多个修饰符组合到Test上,如果使用修饰符的话,则可以直接在class Test前进行修饰,如上面代码中@inject('foo') @observe,两者相比之下,可以看出通过修饰器来修饰的方式会更加简洁易懂些。更多详情,阅读mobx中文文档
2.3 使用修饰符的优缺点
使用修饰符的优点:
- 样板文件最小化,声明式代码。
- 易于使用和阅读。大多数 MobX 用户都在使用。
使用修饰符的缺点:
- ES.next 2阶段特性。
- 需要设置和编译,目前只有 Babel/Typescript 编译器支持。
3.create-react-app+mobx(装饰器)
create-react-app 目前还没有内置的装饰器支持,所以此小结要解决这个问题。
3.1.安装react-app-rewire相关
npm install react-app-rewired --save-dev 复制代码
修改 package.json 里的启动配置
/* package.json */ "scripts": { "start": "node scripts/start.js", "build": "node scripts/build.js", "test": "node scripts/test.js" }, 复制代码
项目根目录创建一个 config-overrides.js 用于修改默认配置,文件位置如下:
+-- your-project | +-- config-overrides.js | +-- node_modules | +-- package.json | +-- public | +-- README.md | +-- src 复制代码
3.2.安装eject
在创建工程项目后,由于没有传统的webpack.config文件首先安装eject生成自定义配置文件(注意,用eject生成webpack.config后该操作不能回滚,注意备份)
npm i eject 复制代码
3.3.安装bable相关
npm install --save-dev @babel/core npm install --save-dev @babel/plugin-proposal-class-properties npm install --save-dev @babel/plugin-proposal-decorators 复制代码
逐条安装完以上命令之后,package.json,如果在这里面设置的话,就不要重复新建 .babelrc文件了,否则会报【重复错误】
//package.json "babel": { "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ], [ "@babel/plugin-proposal-class-properties", { "loose": true } ] ], "presets": [ "react-app" ] } 复制代码
4.小试牛刀看下是否成功?
4.1.实现过程
安装上面的步骤,可以重新启动项目了
npm start 复制代码
假设现在有一个父组件Father,一个子组件Child,在父组件中写被观察的数据、获取数据、设置数据、重置数据的方法,父组件代码如下:
import React, {Component} from 'react'; // 引入 mobx import {observable, computed, action} from "mobx"; // 引入子组件 import Child from "./Child.js"; class VM { @observable firstName = ""; @observable lastName = ""; @computed get fullName() { const {firstName, lastName} = this; if (!firstName && !lastName) { return "Please input your name!"; } else { return firstName + " " + lastName; } } @action.bound setValue(key, event) { this[key] = event.target.value; } @action.bound doReset() { this.firstName = ""; this.lastName = ""; } } const vm = new VM(); export default class Father extends Component { render() { return ( <Child vm={vm}/> ) } } 复制代码
给子组件一个修饰符@observer 子组件代码如下
import React, {Component} from 'react'; import {observer} from "mobx-react"; @observer class Upload extends Component { render(){ // 解构从父组件传来的数据 const {vm} = this.props; return( <div> <h1>This is mobx-react!</h1> <p> First name:{" "} <textarea type="text" value={vm.firstName} onChange={e => vm.setValue("firstName", e)} /> </p> <p> Last name:{" "} <textarea type="text" value={vm.lastName} onChange={e => vm.setValue("lastName", e)} /> </p> <p>Full name: {vm.fullName}</p> <p> <button onClick={vm.doReset}>Reset</button> </p> </div> ) } } 复制代码
4.2.效果
5.mobx常用api
5.1.可观察的数据
observable
observable:一种让数据的变化可以被观察的方法
哪些数据可以被观察?
JS基本数据类型、引用类型、普通对象、类实例、数组和映射
如下代码:
const arr = observable(['a', 'b', 'c']); const map = observable(new Map()); const obj = observable({ a: 1, b: 1 }); 复制代码
observable.box
observable.box:包装数值、布尔值、字符串
如下代码:
let num = observable.box(20); let str = observable.box('hello'); let bool = observable.box(true); // 使用set()设置数值 num.set(50); // get()获取原生数值 console.log(num.get(), str, bool); // 50 // ObservableValue$$1 {name: "ObservableValue@5", isPendingUnobservation: false, isBeingObserved: false, observers: Set(0), diffValue: 0, …} // ObservableValue$$1 {name: "ObservableValue@6", isPendingUnobservation: false, isBeingObserved: false, observers: Set(0), diffValue: 0, …} 复制代码
使用修饰器
不使用修饰器是不是相对麻烦呢?,还要时刻记着变量的类型,而使用修饰器的话,内部会做一些变量判断转换,写法也更加的简洁。代码如下:
class Store { @observable array = []; @observable obj = {}; @observable map = new Map(); @observable string = 'hello'; @observable number=20; @observable bool=false; } 复制代码
5.2.对可观察的数据做出反应
观察数据变化的方式:computed、autorun、when、Reaction
computed:将多个可观察数据组合成一个可观察数据
autorun:重点了解一下,如果应用程序都是 可观察数据 ,而应用程序渲染UI、写入缓存等动作都设置为autorun,我们是不是就可以安心写代码,只与数据状态打交道,从而实现数据统一管理的目标。
when:提供了根据条件执行逻辑,是autorun的一种变种。
reaction:分离可观察数据声明,对autorun做出改进。
四个方法各有特点且互相补充
5.3.修改可观察数据(action)
之前有提到autorun,还有一个问题需要解决,那就是性能问题,如果数据众多,每一次小修改都会触发autorun,如下:
import {observable, isArrayLike, computed, action, runInAction, autorun, when, reaction} from "mobx"; class Store { @observable array = []; @observable obj = {}; @observable map = new Map(); @observable string = 'hello'; @observable number = 20; @observable bool = true; @computed get mixed() { return store.string + ':' + store.number; } } let store = new Store(); reaction(() => [store.string, store.number,store.bool], arr => console.log(arr.join('+'))); store.string = 'word'; store.number = 25; store.bool = true; // 打印结果 word+20+false word+25+false word+25+true 复制代码
我们从上面的结果中得到,每次修改都会触发reaction,那么该怎么解决呢?
可以使用action来解决这个问题。修改代码如下:
import {observable, isArrayLike, computed, action, runInAction, autorun, when, reaction} from "mobx"; class Store { @observable array = []; @observable obj = {}; @observable map = new Map(); @observable string = 'hello'; @observable number = 20; @observable bool = false; @action bar() { store.string = 'word'; store.number = 333; store.bool = true; } } let store = new Store(); reaction(() => [store.string, store.number, store.bool], arr => console.log(arr.join('+'))); store.bar(); // 打印结果 word+333+true 复制代码
使用action后发现,虽然修改了3个数据,但是只调用了一次reaction方法,做到了性能上的优化。所以当数据较多的时,建议使用action来更新数据。
6.mobx实现TodoList
功能如下:
- Todo条目的列表展示
- 增加Todo条目
- 修改完成状态
- 删除Todo条目
首先,创建一个TodoList文件夹,在目录下新建一个store.js文件,这个文件用来做数据的处理。
// store.js import {observable, computed, action} from "mobx"; class Todo { id = Math.random(); @observable title = ''; @observable finished = false; constructor(title) { this.title = title; } @action.bound toggle() { this.finished = !this.finished; } } class Store { @observable todos = []; @action.bound createTodo(title) { this.todos.unshift(new Todo(title)) } @action.bound removeTode(todo) { // remove不是原生的方法,是mobx提供的 this.todos.remove(todo); } @computed get left() { return this.todos.filter(item => !item.finished).length; } } var store = new Store(); export default store; 复制代码
新建一个TodoList.js文件
import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {observer, PropTypes as ObservablePropTypes} from 'mobx-react'; import TodoItem from "./TodoItem"; @observer class TodoList extends Component { // 属性类型要在全局这里定义 static propTypes = { store: PropTypes.shape({ createTodo: PropTypes.func, todos: ObservablePropTypes.observableArrayOf(ObservablePropTypes.observableObject).isRequired }).isRequired }; state = { inputTile: "" }; handleSubmit = (e) => { // 表单提交,阻止整个页面被提交 e.preventDefault(); let {store} = this.props; let {inputTile} = this.state; store.createTodo(inputTile); // 创建完新的条目之后,要清空输入框 this.setState({ inputTile: "" }) }; handleChange = (e) => { var inputTile = e.target.value; this.setState({ inputTile }) }; render() { let {inputTile} = this.state; let {store} = this.props; return <div className="todoList"> <header> <form onSubmit={this.handleSubmit}> <input type="text" onChange={this.handleChange} value={inputTile} placeholder="你想要到哪里去?" className="input" /> </form> </header> <ul> { store.todos.map((item) => { return ( <li key={item.id}> <TodoItem todo={item}/> <span onClick={()=>{store.removeTode(item)}}>删除</span> </li> ) }) } </ul> <footer> {store.left} 项 未完成 </footer> </div> } } export default TodoList 复制代码
新建一个TodoItem.js文件
import React, {Component} from 'react'; import {observer} from 'mobx-react'; import PropTypes from 'prop-types'; @observer class TodoItem extends Component { // 从父组件传来的属性类型要在全局这里定义,做一些限制 static propTypes = { todo: PropTypes.shape({ id: PropTypes.number.isRequired, title: PropTypes.string.isRequired, finished: PropTypes.bool.isRequired }).isRequired }; handleClick = (e) => { let {todo} = this.props; todo.toggle(); } render() { // 这里的Item是一个对象 let {todo} = this.props; return ( <div> <input type="checkbox" checked={todo.finished} onChange={this.handleClick} /> <span className="title">{todo.title}</span> </div> ) } } export default TodoItem; 复制代码
在你组件中引入TodoList组件、以及store.js
import React, {Component} from 'react'; import TodoList from "../../component/TodoList/TodoList"; import store from "../../store/store"; class PictureResources extends Component { render() { return ( <TodoList store={store}/> ) } } export default PictureResources 复制代码
7.使用@inject注入
为了更方便数据管理,我们使用@inject将数据注入,首先找寻根组件,例如APP.js,我们引入mox-react中的Provider作为根容器,再将我们的所有数据引入到APP组件中,营造一种把数据放到全局统一调配的感觉。如果子组件需要的到store数据的话,再按需注入(@inject)如下代码:
import React, {Component} from 'react'; // styles import './scss/style.scss'; // store import {Provider} from 'mobx-react'; import store from './store/store.js'; class App extends Component { render() { return ( <Provider store={store}> <div className="panel"></div> </Provider> ); } } export default App; 复制代码
在需要的子组件中注入数据,注意"store"使用 引号 注入,代码如下:
import React, {Component} from 'react'; import {inject, observer} from 'mobx-react'; @inject("store") @observer class Upload extends Component { render() { return ( <div></div> ) } } export default Upload; 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Netty 入门初体验
- Netty 入门初体验
- 移动开发者的后端开发入门体验
- 降低云游戏延迟优化云游戏体验:贝塞斯达推出Orion技术,还公布了免费体验计划
- PyTorch 初体验
- indexedDB 初体验
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。