资讯专栏INFORMATION COLUMN

react+mobx+thrift学习demo

xcc3641 / 1666人阅读

摘要:安装等相关依赖。通过启动项目,进行后续操作。自定义执行状态的改变。任何不在使用状态的计算值将不会更新,直到需要它进行副作用操作时。强烈建议始终抛出错误,以便保留原始堆栈跟踪。

2018-08-14 learning about work

begin:2018-08-13

step 1 熟悉react 写法

step 2 mobx 了解&使用

step 3 thrift接口调用过程

React&JavaScript propsType

propsType官方文档

react可以在引入prop-types,配置propsTypes属性之后进行类型检查。

可以将属性声明为JS原生类型、React元素、某个类的实例,指定类型的对象等,也可以自定义,可以加上isRequired后缀,如果没有提供该信息,会打印警告。

还可以通过配置defaultProps,为props定义默认值。

props.children

react children

class Grid extends React.Component {
  constructor(props) {
    super(props)
    this.state = {}
  }
  render() {
    return (
      // 可以在这里控制子元素是否显示
       
{this.props.children}
// 只显示hello文本,并不显示子元素 //
hello
) } } const Row = ({ name }) => { return (
{name}
) } ReactDom.render( , document.getElementById("root") )
es6 static methods React & MobX 介绍&功能

mobx是一个状态管理器,下图是官网的原理图,看上去感觉跟Vue的双向数据绑定很相似。

通过action来修改组件状态,由于数据与视图双向绑定,一旦数据改变,会触发视图的更新,从而引起组件或者页面的重新渲染。

mobx的computed与vue的计算属性也有类似,都设置了缓存,依赖项没有发生变化的时候,该属性不会重新运行计算,只有在真正需要更新的时候才会更新。设置了computed的方法与普通方法的区别,也类似于vue的computed与method的区别。

感觉简单而言,从视图更新的过程来看,可以抽象成三个部分:action、state、views,mobx单项数据流,可以有下图的描述:

我觉得,State如果类比于MVVM的话,可以理解为ViewModel。

从开发者的角度来看:

本地搭建环境

本地需要搭建一个react-app环境并添加mobx等相关依赖。

step:

create-react-app my-react-app 使用命令行工具创建新的react-app,并进入项目目录

(本地需先使用npm install -g create-react-app 命令安装工具)

安装babel等

npm install --save-dev babel-core babel-cli babel-preset-env babel-preset-react

创建&编写.babelrc文件

(这里的plugins如果不写也可以,关于支持ES7装饰器的配置问题,后面会再讲)

{
    "presets": [
        "env",
        "react",
        "stage-1",
        "es2015"
    ],
    "plugins": [
        "transform-decorators-legacy",
        "transform-decorators"
    ]
}

安装其他依赖,包括style-loaderbabel-loadercss-loader等等。

这里我开始手动安装了webpack,然后安装webpack的时候,没有指定版本号,默认会安装最新版本Webpack4,运行时会报下面错误:

Cannot read property "thisCompilation" of undefined during npm run build

参考这里的解决方式

To solve this problem:

Delete node_modules

Delete package-lock.json if present

If you have react-scripts in package.json, make sure you don"t have webpack in it

Run yarn (or npm install)

Also make sure you don"t have package.json or node_modules in the parent folders of your project

另一种方式是webpack降级到3。可以理解成webpack4与react-scripts不能同时在package.json中存在。

查找资料的时候发现,如果使用Create React App的话,其实是不需要手动再去安装Webpack的。

最后我删除了node_modules,然后package.json中删除了webpack,重新npm install或者yarn一下,问题解决了。

配置装饰器语法支持。

安装babel-plugin-transform-decorators babel-plugin-transform-decorators-legacy等相关依赖。

实际情况是,依赖装完,.babelrc文件中也配置了插件,webpack也配置完成之后,仍然无法识别装饰器语法,最后按照参考中的方法2解决了。但是这种方法需要在node_modules中修改,个人觉得不大好,暂时先这样处理下,后续再查看下。

通过npm run start启动项目,进行后续操作。

核心概念 & 使用

参考文档

参考学习了 egghead.io课程

入门demo:

import { observable } from "mobx";
import { observer } from "mobx-react";
import { Component } from "react";
import React from "react";
import ReactDOM from "react-dom";

const appState = observable({
  count: 0
})
// 这里不能写成剪头函数 否则数据绑定会失效
appState.increment = function () {
  this.count++;
}
appState.decrement = function () {
  this.count--;
}

@observer class Counter extends Component {
  render() {
    return (
      
Counter {this.props.store.count}
) } handleInc = () => { this.props.store.increment() } handleDec = () => { this.props.store.decrement() } } const rootElement = document.getElementById("root"); ReactDOM.render(, rootElement);
Observable state(可观察的状态)

mobx使用ES7装饰器语法(可选使用)通过给现有属性增加@observable 注解,就可以将属性设定为可观察的属性。使用装饰器属性可以使得书写代码更加简洁.

也可以写成这样

 class appState {
   @observable count = 0;
   increment = function () {
     this.count++;
   }
   decrement = function () {
     this.count--;
   }
 }
 ......
 ReactDOM.render(, rootElement);

不过有个疑惑,下图方法1使用const定义是出于什么目的,官方文档的demo中也有很多是使用const定义的。

如果像下图方法二这样书写,是否有意义?count值也会改变,appState定义为const,其中内容是可以被改变的,如何控制不被改变?实际中是否会有这种情况?

//1. 这里写成const是什么意义?
const appState = observable({
  count: 0
}) 

//2. 这样写是否有意义?const?
 const appState =  {
   @observable count: 0
 }
Computed values

使用@computed 修饰getter方法,计算值延迟更新,只有依赖改变,需要重新计算的时候才会更新。

Reactions(反应)

可以通过@observer将无状态组件变成响应式组件, MobX 会确保组件总是在需要的时重新渲染。

只要需要在状态发生改变时需要更新视图的view上使用@observer修饰,就可以实现自动更新。

自定义 reactions

Actions

actions执行状态的改变。

文档中有这么一段,个人觉得所有衍生同步更新,计算值延迟更新,这两句似乎有些矛盾,这里的所有衍生是否指的是reactions或者action后出发的事件?意思是说不能用计算值来改变状态,而是状态改变之后计算值一定已经变化?有点拗口。。。这里的同步更新和延迟更新到底指的是什么,感觉只能后面有时间看下源码才能了解了

状态改变时,所有衍生任何 源自状态并且不会再有任何进一步的相互作用的东西就是衍生 )都会进行原子级的自动更新。因此永远不可能观察到中间值。

所有衍生默认都是同步更新。这意味着例如动作可以在改变状态之后直接可以安全地检查计算值。

计算值延迟更新的。任何不在使用状态的计算值将不会更新,直到需要它进行副作用(I / O)操作时。 如果视图不再使用,那么它会自动被垃圾回收。

所有的计算值都应该是纯净的。它们不应该用来改变状态

其他注意
observable 相关

通过 observable 传递对象时,后添加到对象的属性无法自动变成可观察的状态

这点有点类似于Vue中的对象数据绑定,如果在最开始定义的时候没有定义某个属性,后面再添加时将无法监控到这个属性的变化,可以使用vue.set来使得操作生效。

当使对象转变成 observable 时,需要记住一些事情:

当通过 observable 传递对象时,只有在把对象转变 observable 时存在的属性才会是可观察的。 稍后添加到对象的属性不会变为可观察的,除非使用 setextendObservable

Array.isArray(observable([]))返回值为false

observable.array 会创建一个人造数组(类数组对象)来代替真正的数组。 实际上,这些数组能像原生数组一样很好的工作,并且支持所有的原生方法,包括从索引的分配到包含数组长度。

请记住无论如何 Array.isArray(observable([])) 都将返回 false ,所以无论何时当你需要传递 observable 数组到外部库时,通过使用 array.slice() 在 observable 数组传递给外部库或者内置方法前创建一份浅拷贝(无论如何这都是最佳实践)总会是一个好主意。 换句话说,Array.isArray(observable([]).slice()) 会返回 true

Array的sort&reverse方法会修改原数组,observableArray则不会

不同于 sort 和 reverse 函数的内置实现,observableArray.sort 和 observableArray.reverse 不会改变数组本身,而只是返回一个排序过/反转过的拷贝。
computed

computed&autorun并不一样。

二者都是响应式调用的衍生,但是computed可以理解为一个纯函数(即调用时刻的输出只由该时刻的输入决定,而不依赖于系统状态),如果使用过程中依赖没有被修改,则不会重新计算。autorun的使用场景更像是产生效果,例如对数据进行过滤操作(而不是产生数据),或者数据监控到数据变化之后的通知等副作用操作(这点与vue中的method并不一样,不要混淆)。

如果你想响应式的产生一个可以被其它 observer 使用的,请使用 @computed,如果你不想产生一个新值,而想要达到一个效果,请使用 autorun。 举例来说,效果是像打印日志、发起网络请求等这样命令式的副作用。

可以通过将computed作为一个函数,来获取在box中的计算值(即基本数据类型值)

错误处理

如果计算值在其计算期间抛出异常,则此异常将捕获并在读取其值时重新抛出。 强烈建议始终抛出“错误”,以便保留原始堆栈跟踪。抛出异常不会中断跟踪,所有计算值可以从异常中恢复。
const x = observable(3)
const y = observable(1)
const divided = computed(() => {
    if (y.get() === 0)
        throw new Error("Division by zero")
    return x.get() / y.get()
})

divided.get() // 返回 3

y.set(0) // OK
divided.get() // 报错: Division by zero
divided.get() // 报错: Division by zero

y.set(2)
divided.get() // 已恢复; 返回 1.5

autorun

autorun函数具有响应式功能,但是该函数不具有观察者。

autorun函数会立即触发,然后每次依赖关系发生改变时会再次触发。computed创建的函数,只有当它有自己的观察者时才会重新计算。

observer

简单来说: 所有渲染 observable 数据的组件都需要使用@observer

在 reaction 中使用的特定 props 一定要间接引用(例如 const myProp = props.myProp)。不然,如果你在 reaction 中引用了 props.myProp,那么 props 的任何改变都会导致 reaction 的重新运行。

MobX 追踪属性访问,而不是值,可以理解为追踪的是引用,当引用的内存空间发生变化时触发响应行为,如果只是内存空间中的值发生变化,是不会被追踪的。

陷阱

const message = observable({ title: "hello" })
autorun(() => {
    // 错误
    console.log(message)
    // 正确
    console.log(message.title)
})

// 不会触发重新运行
message.title = "Hello world"

其他解决方案:

autorun(() => {
    console.log(message.title) // 很显然, 使用了 `.title` observable
})
autorun(() => {
    console.log(mobx.toJS(message)) // toJS 创建了深克隆,从而读取消息
})
autorun(() => {
    console.log({...message}) // 创建了浅克隆,在此过程中也使用了 `.title`
})
autorun(() => {
    console.log(JSON.stringify(message)) // 读取整个结构
})

action

对于修改状态的函数使用@action

runInAction 是个简单的工具函数,它接收代码块并在(异步的)动作中执行。

仔细了解了异步Action这一部分,注意书写方式。

练习Demo

今天使用react+mobx 写了个todolist的demo,目前实现了添加和删除的功能。熟悉一下开发方式和书写方式。

地址

主要代码:

import React, { Component } from "react"
import { observable, computed, observe, action } from "mobx";
import ReactDOM from "react-dom";
import { inject, Provider, observer } from "mobx-react"
import "./index.css";
import { debug } from "util";


class Todo {
  constructor(content) {
    this.content = content
  }
  id = Math.random()
  @observable content
  @observable completed = false
}

class TodoListStore {
  @observable todos = []

  @computed get todosLength() {
    return this.todos.length
  }

  @computed get completedLength() {
    return this.todos.filter(item => item.completed).length
  }

  @computed get uncompletedLength() {
    return this.todosLength - this.completedLength
  }

  @action
  addTodo(todo) {
    this.todos.push(todo)
  }

  @action
  deleteTodo(index) {
    this.todos.splice(index, 1)
    // console.log(e)
  }
}

// const TodoItem = observer(({ todo }) => (
//   
  • // (todo.completed = !todo.completed)} // /> // {todo.content} // //
  • // )); @observer class TodoItem extends Component { constructor(props) { super(props) } render() { const {todo, index} = this.props return (
  • (todo.completed = !todo.completed)} /> {todo.content}
  • ) } } @observer class TodoInput extends Component { constructor(props) { super(props) this.state = new Todo() } addTodo() { let content = this.refs.content.value let item = new Todo(content) this.props.store.addTodo(item) console.log(this.props.store.todos) this.refs.content.value = "" } render() { return (
    新增todo:
    ) } } @observer class TodoList extends Component { constructor(props) { super(props) this.state = { todos: this.props.store.todos, index: -1 } } delete(index) { this.props.store.deleteTodo(index) } render() { // let todos = [...this.props.store.todos] return (
    事项清单:
      {this.state.todos.map((todo, index) => ( this.delete(index)}/> ) )}
    ) } } @observer class TodoArchive extends Component { render() { let store = this.props.store return (
    总计:{store.todosLength}项 已完成:{store.completedLength}项 未完成:{store.uncompletedLength}项
    ) } } class TodoListView extends Component { render() { let store = this.props.store return (
    ) } } let todolist = new TodoListStore() ReactDOM.render(, document.getElementById("root"));

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/97385.html

    相关文章

    • mobx——rudex的简单替代品

      摘要:是一个的简单可扩展的状态管理库。它的副作用是自动更新。该函数返回一个值,当返回值为的时候,才会继续触发第一个函数。当返回值为时,不再继续监听。包含一个,该值用来区分执行事件的类型。 mobx 能干什么 使用 react 写小型应用,数据、业务逻辑和视图的模块划分不是很细是没有问题的。在这个阶段,引入任何状态管理库,都算是奢侈的。但是随着页面逻辑的复杂度提升,在中大型应用中,数据、业务逻...

      DevWiki 评论0 收藏0
    • React全家桶环境搭建过程

      摘要:环境搭建从零开始搭建开发环境引入安装依赖新建修改引入并支持安装依赖打包时将样式模块化,我们可以通过或引入样式,并且相互不冲突。修改,引入创建使用语法报错修改引入状态管理使用装饰器语法修改修改源码 环境搭建 1.从零开始搭建webpack+react开发环境 2.引入Typescript 安装依赖 npm i -S @types/react @types/react-domnpm i -...

      Genng 评论0 收藏0
    • React + MobX 入门及实例(一)

      摘要:前言现在最热门的前端框架,毫无疑问是。对于小型应用,引入状态管理库是奢侈的。但对于复杂的中大型应用,引入状态管理库是必要的。现在热门的状态管理解决方案,相继进入开发者的视野。获得计算得到的新并返回。 前言 现在最热门的前端框架,毫无疑问是React。 React是一个状态机,由开始的初始状态,通过与用户的互动,导致状态变化,从而重新渲染UI。 对于小型应用,引入状态管理库是奢侈的。 但...

      simon_chen 评论0 收藏0
    • Mobx重构React WebApp

      摘要:发现很有趣,所以我把这个项目用重构了一次。旧的版本是用全家桶,就是构建的在的的分支上。其次就是性能优化的问题。就是无论如何,只要和发生了变化,就要发生一次。因为和数据已经解耦了。会检测被观察的数据,只要数据发生改变,它就会去重新渲染。 背景 前一阵子,我刚写了篇React全家桶实战,介绍了下我用react全家桶构建一个react webapp的中遇到的一些问题。后来,我发现了mobx。...

      cartoon 评论0 收藏0
    • context来了,也许该放手redux or mobx...

      摘要:官方推荐使用的情况是当需要用到全局数据的时候,比如主题,多语言制或者用户登录授权等等。 老铁,学不动了?不要慌,耽误不了你几分钟...(说谎脸,汗) long long ago 使用react的同胞们,也许都苦恼过其状态管理以及组件之间的数据传递和共享(笨重的方式通过props依次往子组件传递)。 这时候,redux(mobx类似)出现了,我们累死累活的从水深火热中解放了(第三方的库相...

      bingo 评论0 收藏0

    发表评论

    0条评论

    xcc3641

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <