老树发新芽,十分钟介绍mobx与react

老树发新芽—使用 mobx 加快你的 AngularJS 应用

2018/05/23 · JavaScript
· AngularJS,
mobx

原作出处:
kuitos   

十二月首的时候,Angular 官方博客公布了一则新闻:

AngularJS is planning one more significant release, version 1.7,
and on July 1, 2018 it will enter a 3 year Long Term Support period.

即在 4月二十七日 AngularJS 宣布 1.7.0 版本之后,AngularJS 将进入三个年限 3
年的 LTS 时期。也正是说 二零一八年一月二三十一日 起至 2021年5月十六日,AngularJS
不再统一别的会导致 breaking changes 的 features 或
bugfix,只做要求的标题修复。详细消息见那里:Stable AngularJS and Long
Term
Support

看来那则音讯时自小编如故感动颇多的,作为小编的前端启蒙框架,小编从 AngularJS
上搜查缉获到了卓殊多的营养。即便 AngularJS 作为一款不错的前端 MVW
框架已经不错的姣好了上下一心的历史任务,但考虑到正是到了 2018
年,许多公司按照 AngularJS 的品类依然高居服役阶段,结合自身过去一年多在
mobx 上的琢磨和执行,小编说了算给 AngularJS
强行再续一波命。(搭车求治推延症良方,5月首起草的作品10月份才写完,音信都要晚点了)

基于 MobX 创设视图框架非亲非故的多少层-与 Vue 的组成

2018/07/09 · JavaScript
· mobx

原来的书文出处:
kuitos   

mobx-vue 最近已跻身 mobxjs
官方组织,欢迎试用求 star!

几周前本身写了一篇小说描述了 mobx 与 angularjs 结合使用的法子及目的(老树发新芽—使用 mobx 加速你的 AngularJS
应用),此次介绍一下哪些将
MobX 跟 Vue 结合起来。

初稿地址:

第二章 React + MobX
状态管理入门及实例(二)

预备干活

在始发在此之前,大家需求给 AngularJS 搭配上一些现代化 webapp
开发套件,以便前面能更便于地装载上 mobx 引擎。

安装

npm i mobx-vue -S

1
npm i mobx-vue -S

写在前面:自个儿泰语水平有限,首假如写给自身看的,若有哪位同学看来了分外的地方,请为笔者提出,十分多谢;

前言

当今最看好的前端框架,毫无疑问是React。

React是三个状态机,由起首的先河状态,通过与用户的交互,导致情状变化,从而再一次渲染UI。

对于小型应用,引入状态管理库是”奢侈的”。

老树发新芽,十分钟介绍mobx与react。但对于复杂的中山大学型应用,引入状态管理库是”须要的”。

今昔紧俏的景况管理化解方案Redux,MobX相继进入开发者的视野。

Redux、MobX哪贰个更符合您的类型?

在react项目中采纳redux or
mobx?

正如爱因Stan所说的 “ 让整体育赛事物尽恐怕的简约,但决不简单”。

固然让我们来填一填 MobX 的坑。

AngularJS 配合 ES6/next

明天是二零一八年,使用 ES6 开发使用已经成为事实标准(有恐怕的推荐介绍直接上 TS
)。如何将 AngularJS 搭载上 ES6
那里不再赘言,能够看本人在此以前的那篇文章:Angular1.x + ES6
开发风格指南

使用

mobx-vue 的运用格外不难,只要求采纳 connect 将你用 mobx 定义的 store 跟
vue component 连接起来即可:

<template> <section> <p v-text=”amount”></p>
<p v-for=”user in users” :key=”user.name”>{{user.name}}</p>
</section> </template> <script lang=”ts”> import {
Connect } from “mobx-vue”; import Vue from “vue”; import Component from
“vue-class-component”; class ViewModel { @observable users = [];
@computed get amount() { return this.users.length } <a
href=’;
fetchUsers() {} } @Connect(new ViewModel()) @Component() export default
class App extends Vue { mounted() { this.fetchUsers(); } }
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
    <section>
        <p v-text="amount"></p>
        <p v-for="user in users" :key="user.name">{{user.name}}</p>
    </section>
</template>
 
<script lang="ts">
    import { Connect } from "mobx-vue";
    import Vue from "vue";
    import Component from "vue-class-component";
    class ViewModel {
        @observable users = [];
        @computed get amount() { return this.users.length }
        <a href=’http://www.jobbole.com/members/Francesco246437′>@action</a> fetchUsers() {}
    }
 
    @Connect(new ViewModel())
    @Component()
    export default class App extends Vue {
        mounted() {
            this.fetchUsers();
        }
    }
</script>

mobx是一个比redux更好的状态管理包,代码量更少,思路更清晰,没有像redux那样复杂的reducer,action
(ps: redux的小编也援引mobx,某大大告诉笔者的,并从未原话链接)

介绍

基于组件的利用架构

AngularJS 在 1.5.0 版本后新增了一连串动人心弦的特色,如 onw-way
bindings、component lifecycle hooks、component definition
等,基于那么些特色,大家得以方便的将 AngularJS
系统营造成1个纯组件化的利用(倘诺你对那么些特色很通晓可一贯跳过至 AngularJS
搭配
mobx)。大家3个个来看:

  • onw-way bindings 单向绑定
    AngularJS
    中使用 来定义组件的单向数据绑定,例如我们这样定义一个组件:
angular .module('app.components', \[\]) .directive('component', ()
=&gt; ({ restrict: 'E', template: '&lt;p&gt;count:
{{$ctrl.count}}&lt;/p&gt;&lt;button ng-click="$ctrl.count =
$ctrl.count + 1"&gt;increase&lt;/button&gt;' scope: { count: '&lt;'
}, bindToController: true, controllerAs: '$ctrl', })

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f2c952921585-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f2c952921585-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f2c952921585-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f2c952921585-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f2c952921585-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f2c952921585-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f2c952921585-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f2c952921585-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f2c952921585-9">
9
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f2c952921585-10">
10
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f2c952921585-11">
11
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f2c952921585-1" class="crayon-line">
angular
</div>
<div id="crayon-5b8f6aab02f2c952921585-2" class="crayon-line crayon-striped-line">
    .module('app.components', [])
</div>
<div id="crayon-5b8f6aab02f2c952921585-3" class="crayon-line">
    .directive('component', () =&gt; ({
</div>
<div id="crayon-5b8f6aab02f2c952921585-4" class="crayon-line crayon-striped-line">
        restrict: 'E',
</div>
<div id="crayon-5b8f6aab02f2c952921585-5" class="crayon-line">
        template: '&lt;p&gt;count: {{$ctrl.count}}&lt;/p&gt;&lt;button ng-click=&quot;$ctrl.count = $ctrl.count + 1&quot;&gt;increase&lt;/button&gt;'
</div>
<div id="crayon-5b8f6aab02f2c952921585-6" class="crayon-line crayon-striped-line">
        scope: {
</div>
<div id="crayon-5b8f6aab02f2c952921585-7" class="crayon-line">
            count: '&lt;'
</div>
<div id="crayon-5b8f6aab02f2c952921585-8" class="crayon-line crayon-striped-line">
        },
</div>
<div id="crayon-5b8f6aab02f2c952921585-9" class="crayon-line">
        bindToController: true,
</div>
<div id="crayon-5b8f6aab02f2c952921585-10" class="crayon-line crayon-striped-line">
        controllerAs: '$ctrl',
</div>
<div id="crayon-5b8f6aab02f2c952921585-11" class="crayon-line">
    })
</div>
</div></td>
</tr>
</tbody>
</table>


使用时:



{{app.count}} component count="app.count"&gt;component&gt;

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f35150522417-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f35150522417-2">
2
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f35150522417-1" class="crayon-line">
{{app.count}}
</div>
<div id="crayon-5b8f6aab02f35150522417-2" class="crayon-line crayon-striped-line">
component count=&quot;app.count&quot;&gt;component&gt;
</div>
</div></td>
</tr>
</tbody>
</table>

当我们点击组件的 increase 按钮时,可以看到组件内的 count 加 1
了,但是 `app.count`并不受影响。

区别于 AngularJS
赖以成名的双向绑定特性 `scope: { count: '='}`,单向数据绑定能更有效的隔离操作影响域,从而更方便的对数据变化溯源,降低
debug 难度。  
双向绑定与单向绑定有各自的优势与劣势,这里不再讨论,有兴趣的可以看我这篇回答:[单向数据绑定和双向数据绑定的优缺点,适合什么场景?](https://www.zhihu.com/question/49964363/answer/136022879)
  • component lifecycle hooks 组件生命周期钩子1.5.3
    起首新增了多少个零部件的生命周期钩子(目标是为更方便人民群众的向 Angular2+
    迁移),分别是 $onInit $onChanges $onDestroy $postLink $doCheck(1.5.8扩充),写起来差不离长这么:
class Controller { $onInit() { // initialization }
$onChanges(changesObj) { const { user } = changesObj; if(user &&
!user.isFirstChange()) { // changing } } $onDestroy() {} $postLink()
{} $doCheck() {} } angular .module('app.components', \[\])
.directive('component', () =&gt; ({ controller: Controller, ... }))

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-9">
9
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-10">
10
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-11">
11
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-12">
12
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-13">
13
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-14">
14
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-15">
15
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-16">
16
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-17">
17
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-18">
18
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-19">
19
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-20">
20
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-21">
21
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-22">
22
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-23">
23
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-24">
24
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3a441625873-25">
25
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3a441625873-26">
26
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f3a441625873-1" class="crayon-line">
class Controller {
</div>
<div id="crayon-5b8f6aab02f3a441625873-2" class="crayon-line crayon-striped-line">
    
</div>
<div id="crayon-5b8f6aab02f3a441625873-3" class="crayon-line">
    $onInit() {
</div>
<div id="crayon-5b8f6aab02f3a441625873-4" class="crayon-line crayon-striped-line">
        // initialization
</div>
<div id="crayon-5b8f6aab02f3a441625873-5" class="crayon-line">
    }
</div>
<div id="crayon-5b8f6aab02f3a441625873-6" class="crayon-line crayon-striped-line">
    
</div>
<div id="crayon-5b8f6aab02f3a441625873-7" class="crayon-line">
    $onChanges(changesObj) {
</div>
<div id="crayon-5b8f6aab02f3a441625873-8" class="crayon-line crayon-striped-line">
        const { user } = changesObj;
</div>
<div id="crayon-5b8f6aab02f3a441625873-9" class="crayon-line">
        if(user &amp;&amp; !user.isFirstChange()) {
</div>
<div id="crayon-5b8f6aab02f3a441625873-10" class="crayon-line crayon-striped-line">
            // changing
</div>
<div id="crayon-5b8f6aab02f3a441625873-11" class="crayon-line">
        }
</div>
<div id="crayon-5b8f6aab02f3a441625873-12" class="crayon-line crayon-striped-line">
    }
</div>
<div id="crayon-5b8f6aab02f3a441625873-13" class="crayon-line">
    
</div>
<div id="crayon-5b8f6aab02f3a441625873-14" class="crayon-line crayon-striped-line">
    $onDestroy() {}
</div>
<div id="crayon-5b8f6aab02f3a441625873-15" class="crayon-line">
    
</div>
<div id="crayon-5b8f6aab02f3a441625873-16" class="crayon-line crayon-striped-line">
    $postLink() {}
</div>
<div id="crayon-5b8f6aab02f3a441625873-17" class="crayon-line">
    
</div>
<div id="crayon-5b8f6aab02f3a441625873-18" class="crayon-line crayon-striped-line">
    $doCheck() {}   
</div>
<div id="crayon-5b8f6aab02f3a441625873-19" class="crayon-line">
}
</div>
<div id="crayon-5b8f6aab02f3a441625873-20" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f6aab02f3a441625873-21" class="crayon-line">
angular
</div>
<div id="crayon-5b8f6aab02f3a441625873-22" class="crayon-line crayon-striped-line">
    .module('app.components', [])
</div>
<div id="crayon-5b8f6aab02f3a441625873-23" class="crayon-line">
    .directive('component', () =&gt; ({
</div>
<div id="crayon-5b8f6aab02f3a441625873-24" class="crayon-line crayon-striped-line">
     controller: Controller,
</div>
<div id="crayon-5b8f6aab02f3a441625873-25" class="crayon-line">
     ...
</div>
<div id="crayon-5b8f6aab02f3a441625873-26" class="crayon-line crayon-striped-line">
 }))
</div>
</div></td>
</tr>
</tbody>
</table>

事实上在 1.5.3
之前,我们也能借助一些机制来模拟组件的生命周期(如 `$scope.$watch`、`$scope.$on('$destroy')`等),但基本上都需要借助`$scope`这座‘‘桥梁’’。但现在我们有了框架原生
lifecycle 的加持,这对于我们构建更纯粹的、框架无关的 ViewModel
来讲有很大帮助。更多关于 lifecycle 的信息可以看官方文档:[AngularJS
lifecycle
hooks](https://code.angularjs.org/1.6.7/docs/api/ng/service/%24compile#life-cycle-hooks)
  • component definitionAngularJS 1.5.0
    后扩充了 component 语法用于更便宜清楚的概念三个零件,如上述例子中的组件大家得以用component语法改写成:
JavaScript

angular .module('app.components', \[\]) .component('component', {
template: '&lt;p&gt;count: {{$ctrl.count}}&lt;/p&gt;&lt;button
ng-click="$ctrl.onUpdate({count: $ctrl.count +
1})"&gt;increase&lt;/button&gt;' bindings: { count: '&lt;',
onUpdate: '&' }, })

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f3e495620996-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3e495620996-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3e495620996-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3e495620996-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3e495620996-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3e495620996-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3e495620996-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f3e495620996-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f3e495620996-9">
9
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f3e495620996-1" class="crayon-line">
angular
</div>
<div id="crayon-5b8f6aab02f3e495620996-2" class="crayon-line crayon-striped-line">
    .module('app.components', [])
</div>
<div id="crayon-5b8f6aab02f3e495620996-3" class="crayon-line">
    .component('component', {
</div>
<div id="crayon-5b8f6aab02f3e495620996-4" class="crayon-line crayon-striped-line">
        template: '&lt;p&gt;count: {{$ctrl.count}}&lt;/p&gt;&lt;button ng-click=&quot;$ctrl.onUpdate({count: $ctrl.count + 1})&quot;&gt;increase&lt;/button&gt;'
</div>
<div id="crayon-5b8f6aab02f3e495620996-5" class="crayon-line">
        bindings: {
</div>
<div id="crayon-5b8f6aab02f3e495620996-6" class="crayon-line crayon-striped-line">
            count: '&lt;',
</div>
<div id="crayon-5b8f6aab02f3e495620996-7" class="crayon-line">
     onUpdate: '&amp;'
</div>
<div id="crayon-5b8f6aab02f3e495620996-8" class="crayon-line crayon-striped-line">
        },
</div>
<div id="crayon-5b8f6aab02f3e495620996-9" class="crayon-line">
    })
</div>
</div></td>
</tr>
</tbody>
</table>

本质上`component`就是`directive`的语法糖,bindings
是 `bindToController + controllerAs + scope` 的语法糖,只不过`component`语法更简单语义更明了,定义组件变得更方便,与社区流行的风格也更一致(熟悉
vue 的同学应该已经发现了😆)。更多关于 AngularJS 组件化开发的 best
practice,可以看官方的开发者文档:[Understanding
Components](https://code.angularjs.org/1.6.7/docs/guide/component)

Why MobX/mobx-vue

咱俩知晓,mobx 跟 vue 都以依据 数据恐吓&重视收集
的措施来落到实处响应式机制的。mobx 官方也反复事关
inspired by vue,那么我们为什么还要将五个大概等同的东西组成起来呢?

Yes, it’s weird.

贰零壹伍年小编在营造企业级组件库的时候开首盘算三个题目,我们什么样在代码库基于某一框架的景况下,能以尽量小的代价在未来将零件库迁移到任何
框架/库
下?总不能够依据新的技术全体重写一遍呢,那也太浪费生命了。且不说对于基础控件而言,交互/行为
逻辑基本上是可明确的,最多也正是 UI
上的局地调动,而且只有为了品尝新技巧花费集团人力物力将基础库推导重写也是不行不工作的做法。那么大家只可以承受被框架绑架而不得不沦为某一技术栈从此泥潭深陷吗?对于前端那种框架半衰期特别短的小圈子而言肯定是不可接受的,结果唯有便是要么本人跑路坑后来人,要么招不到人来一块填坑…
不难的话大家鞭长莫及享用新技巧带来的各样红利。

在 MVVM 架构视角下,越是重型的使用其复杂度越是集中在 M(Model) 跟
VM(ViewModel) 那两层,尤其是 Model
层,理论上相应是能脱离上层视图独立运作独立发表独立测试的存在。而相应的不相同视图框架只是利用了不相同绑定语法的动态模板引擎而已,这些观点笔者在日前的几篇小说里都讲述过。所以只要大家将视图层做的很薄,大家迁移的基金自然会降到一个可承受的层面,甚至有恐怕通过工具在编写翻译期自动生成分歧框架的视图层代码。

要成功 Model 甚至 ViewModel
独立可复用,咱们须求的是一种能够协助大家描述各数据模型间信赖关系图且框架中立的通用状态管理方案。那中间我尝试过
ES6 accessor、redux、rxjs 等方案,但都不如愿。accessor
过于底层且异步不谐和、redux
开发体验太差(参考Redux数据流管理架构有哪些致命缺点,未来会怎么着革新?)、rxjs
过重等等。直到后来来看 MobX:MobX 语法丰富简单、弱主张(unopinioned)、oop
向、框架中立等风味恰恰符合本人的供给。

在过去的一年多里,作者分别在 react、angularjs、angular 上尝试过基于 MobX
创设 VM/M
层,当中有四个上线项目,贰个民用项目,实践意义基本上也达到了自己的预料。在架设上,大家只需求选取相应的
connector,就能依据相同数据层,在不一致框架下自如的切换。那样看来,那套思路未来就剩
Vue 没有被表明了。

在 mobx-vue 在此以前,社区早已有一些绝妙的 connector 达成,如
movue
vue-modex 等,但大旨都以依照 vue
的插件机制且 inspired by
vue-rx,除了行使起来相对繁琐的难点外,最大的难点是其完毕中央都是依靠
Vue.util.defineReactive 来做的,也正是说照旧基于 Vue
自有的响应式机制,那在肯定程度不仅浪费了 MobX 的reactive
能力,而且会为搬迁到任何视图框架下埋下了不明确的种子(毕竟你不只怕担保是
Vue 如故 MobX 在响应状态变化)。

参考:why mobx-vue

美艳图景下相应是由 mobx 管理数据的信赖关系,vue 针对 mobx 的响应做出
re render 动作即可,vue 只是二个唯有的动态模板渲染引擎,就好像 react
一样。

在那样的二个背景下,mobx-vue
诞生了。

1.mobx 反应流程

1.入门

澳门葡京 1

image

对此利用开发中的常见难题,React 和
MobX都提供了最优和独特的消除方案。React 提供了优化UI渲染的体制,
那种体制固然通过运用虚拟DOM来压缩昂贵的DOM变化的数目。MobX
提供了优化利用状态与 React
组件同步的机制,那种机制就是选取响应式虚拟正视状态图表,它只有在真的须求的时候才履新还要永远保持是最新的。

AngularJS 搭配 mobx

准备工作做了一堆,我们也该起来进入本文的宗旨,即怎么样给 AngularJS 搭载上
mobx 引擎(本文纵然你对 mobx
中的基础概念已经有一定水准的询问,即使不打听能够先活动 mobx
repo mobx official
doc):

mobx-vue 是怎么样运维的

既是我们的目标是将 vue 变成一个一味的模版渲染引擎(vdom),并且使用 mobx
响应式机制取代 vue 的响应式,那么一旦我们威逼到 Vue
的零部件装载及创新方法,然后在组件装载的时候收集信赖,在依靠爆发变更时更新组件即可。

以下内容与其叫做 mobx-vue 是哪些运维的,不如叫 Vue 源码解析:

大家了解 Vue 日常是那样开首化的:

new Vue({ el: ‘#app’, render: h => h(App)});

1
new Vue({ el: ‘#app’, render: h => h(App)});

那么找到 Vue 的构造函数,

function Vue (options) { …… this._澳门葡京 ,init(options) }

1
2
3
4
function Vue (options) {
  ……
  this._init(options)
}

跟进到_init方式,除了一密密麻麻组件开头化行为外,最要紧是最终一有个其他
$mount 逻辑:

if (vm.$options.el) { vm.$mount(vm.$options.el) }

1
2
3
if (vm.$options.el) {
  vm.$mount(vm.$options.el)
}

跟进 $mount 方法,以 web runtime 为例:

if (process.env.NODE_ENV !== ‘production’ && config.performance &&
mark) { updateComponent = () => { … } } else { updateComponent = ()
=> { vm._update(vm._render(), hydrating) } } vm._watcher = new
Watcher(vm, updateComponent, noop)

1
2
3
4
5
6
7
8
9
10
11
if (process.env.NODE_ENV !== ‘production’ && config.performance && mark) {
    updateComponent = () => {
        …
    }
} else {
    updateComponent = () => {
        vm._update(vm._render(), hydrating)
    }
}
 
vm._watcher = new Watcher(vm, updateComponent, noop)

从那里能够看来,updateComponent 方法将是组件更新的要害入口,跟进
Watcher 构造函数,看 Vue 怎么调用到那几个法子的:

constructor ( vm: Component, expOrFn: string | Function, cb: Function,
options?: Object ) { … this.expression = process.env.NODE_ENV !==
‘production’ ? expOrFn.toString() : ” // parse expression for getter if
(typeof expOrFn === ‘function’) { this.getter = expOrFn } else {
this.getter = parsePath(expOrFn) … } this.value = this.lazy ?
undefined : this.get()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: Object
  ) {
    …
    this.expression = process.env.NODE_ENV !== ‘production’
      ? expOrFn.toString()
      : ”
    // parse expression for getter
    if (typeof expOrFn === ‘function’) {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      …
    }
    this.value = this.lazy
      ? undefined
      : this.get()

get () { … try { value = this.getter.call(vm, vm) } catch (e) { … }

1
2
3
4
5
6
7
get () {
    …
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      …
  }

看来那里,大家能觉察,组件 装载/更新 的发起者是:
value = this.getter.call(vm, vm) ,而作者辈只要通过 vm._watcher.getter
的点子就能获得相应的主意引用, 即
updateComponent := vm._watcher.getter。所以大家借使在 $mount 前将
MobX 管理下的数目植入组件上下文供组件直接动用,在$mount 时让 MobX
收集相应的依赖,在 MobX 检查和测试到依靠更新时调用 updateComponent
即可。那样的话既能让 MobX 的响应式机制通过一种容易的措施 hack 进 Vue
种类,同时也能保障组件的原生行为不面临震慑(生命周期钩子等)。

主干思想正是用 MobX 的响应式机制接管 Vue 的 沃特cher,将 Vue
降级成一个彻头彻尾的装载 vdom 的零部件渲染引擎。

着力达成相当的粗略:

const { $mount } = Component.prototype; Component.prototype.$mount =
function (this: any, …args: any[]) { let mounted = false; const
reactiveRender = () => { reaction.track(() => { if (!mounted) {
$mount.apply(this, args); mounted = true; } else {
this._watcher.getter.call(this, this); } }); return this; }; const
reaction = new Reaction(`${name}.render()`, reactiveRender); dispose =
reaction.getDisposer(); return reactiveRender(); };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const { $mount } = Component.prototype;
 
Component.prototype.$mount = function (this: any, …args: any[]) {
    let mounted = false;
    const reactiveRender = () => {
        reaction.track(() => {
            if (!mounted) {
                $mount.apply(this, args);
                mounted = true;
            } else {
                this._watcher.getter.call(this, this);
            }
        });
 
        return this;
    };
    const reaction = new Reaction(`${name}.render()`, reactiveRender);
    dispose = reaction.getDisposer();
    return reactiveRender();
};

总体代码在此间:

澳门葡京 2

2.安装

npm install –save mobx mobx-react

1. mobx-angularjs

引入 mobx-angularjs 库连接
mobx 和 angularjs 。

npm i mobx-angularjs -S

1
npm i mobx-angularjs -S

最后

尤大大在此以前说过:mobx + react 是更繁琐的 Vue,本质上来看真就是那般的,mobx

  • react 组合提供的能力恰好是 Vue 与生俱来的。而 mobx-vue
    做的政工则刚刚相反:将 Vue 降级成 react 然后再同盟 MobX 升级成 Vue
    。那的确很奇幻。但自作者想说的是,大家的初衷并不是说 Vue
    的响应式机制落实的不佳从而要用 MobX 替换掉,而是希望借助 MobX
    这一个相对中立的情形管理平台,面向差别视图层技术提供一种相对通用的数据层编制程序范式,从而尽量抹平不一样框架间的语法及技术栈差别,以便为开发者提供更多的视图技术的决策权及恐怕,而不至于被某一框架绑架裹挟。

PS: 那篇是无穷无尽小说的第①篇,前边将有越来越多关于
如何基于 MobX 构建视图框架无关的数据层
的架构范式及实施的内容,敬请期待!

1 赞 1 收藏
评论

澳门葡京 3

 2.the core idea

3.大旨概念

1.state(状态)
情状是驱动应用的数码。

2.observable(value) && @observable
Observable
值能够是JS基本数据类型、引用类型、普通对象、类实例、数组和照耀。其修饰的state会暴暴光来供观看者使用。

const map = observable.map({ key: "value"});
map.set("key", "new value");

const list = observable([1, 2, 4]);
list[2] = 3;

const person = observable({
    firstName: "Clive Staples",
    lastName: "Lewis"
});
person.firstName = "C.S.";

const temperature = observable(20);
temperature.set(25);

3.observer(观察者)
被observer修饰的零部件,将会依据组件内采用到的被observable修饰的state的扭转而机关心重视新渲染

import {observer} from "mobx-react";

var timerData = observable({
    secondsPassed: 0
});

setInterval(() => {
    timerData.secondsPassed++;
}, 1000);

@observer class Timer extends React.Component {
    render() {
        return (Seconds passed: { this.props.timerData.secondsPassed }  )
    }
};

React.render(<Timer timerData={timerData} />, document.body);

4.action(动作)
唯有在 actions 中,才方可修改 Mobx 中 state 的值。
只顾:当您选拔装饰器格局时,@action 中的 this
没有绑定在最近以此实例上,要用过 @action.bound 来绑定 使得 this
绑定在实例对象上。

@action.bound setName () {
  this.myName = 'HUnter'
}

actions ——> state ——> view

5.computed
计算值(computed values)是足以遵照现有的事态或别的计算值衍生出的值。
getter:获得总计获得的新state并重返。
setter: 不能够用来直接改动计算属性的值,不过它们得以用来作“逆向”衍生。

class Foo {
    @observable length = 2;
    @computed get squared() {
        return this.length * this.length;
    }
    set squared(value) { // 这是一个自动的动作,不需要注解
        this.length = Math.sqrt(value);
    }
}

6.autorun
这一般是当您必要从反应式代码桥接到命令式代码的情况,例如打字与印刷日志、持久化可能更新UI的代码。

var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));

var disposer = autorun(() => console.log(sum.get()));
// 输出 '6'
numbers.push(4);
// 输出 '10'

disposer();
numbers.push(5);
// 不会再输出任何值。`sum` 不会再重新计算。

经验法则:假使你有叁个函数应该自行运营,但不会时有爆发贰个新的值,请使用autorun。
别的情形都应该使用 computed。

7.reactions
Reactions
和总计值很像,但它不是发出五个新的值,而是会发生部分副功效,比如打字与印刷到控制台、互连网请求、递增地创新React 组件树以修补DOM、等等。 一言以蔽之,reactions 在
响应式编制程序和命令式编制程序里面确立联络的桥梁。

2. 定义 ViewModel

在正儿八经的 MVVM 架构里,ViewModel/Controller
除了营造视图本人的图景数据(即局地情状)外,作为视图跟工作模型之间关系的桥梁,其主要任务是将业务模型适配(转换/组装)成对视图更友好的数据模型。因而,在
mobx 视角下,ViewModel 重要由以下几部分组成:

  • 视图(局部)状态对应的 observable data
class ViewModel { @observable isLoading = true; @observable
isModelOpened = false; }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f45684167140-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f45684167140-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f45684167140-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f45684167140-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f45684167140-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f45684167140-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f45684167140-7">
7
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f45684167140-1" class="crayon-line">
class ViewModel {
</div>
<div id="crayon-5b8f6aab02f45684167140-2" class="crayon-line crayon-striped-line">
    @observable
</div>
<div id="crayon-5b8f6aab02f45684167140-3" class="crayon-line">
    isLoading = true;
</div>
<div id="crayon-5b8f6aab02f45684167140-4" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f6aab02f45684167140-5" class="crayon-line">
 @observable
</div>
<div id="crayon-5b8f6aab02f45684167140-6" class="crayon-line crayon-striped-line">
 isModelOpened = false;
</div>
<div id="crayon-5b8f6aab02f45684167140-7" class="crayon-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

可观察数据(对应的 observer 为
view),即视图需要对其变化自动做出响应的数据。在 mobx-angularjs
库的协助下,通常 observable data 的变化会使关联的视图自动触发
rerender(或触发网络请求之类的副作用)。ViewModel 中的 observable data
通常是视图状态(UI-State),如 isLoading、isOpened 等。

  • 由 应用/视图 状态衍生的 computed data

    Computed values are values that can be derived from the existing
    state or other computed values.

class ViewModel { @computed get userName() { return
\`${this.user.firstName} ${this.user.lastName}\`; } }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f49065322680-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f49065322680-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f49065322680-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f49065322680-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f49065322680-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f49065322680-6">
6
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f49065322680-1" class="crayon-line">
class ViewModel {
</div>
<div id="crayon-5b8f6aab02f49065322680-2" class="crayon-line crayon-striped-line">
    @computed
</div>
<div id="crayon-5b8f6aab02f49065322680-3" class="crayon-line">
    get userName() {
</div>
<div id="crayon-5b8f6aab02f49065322680-4" class="crayon-line crayon-striped-line">
        return `${this.user.firstName} ${this.user.lastName}`;
</div>
<div id="crayon-5b8f6aab02f49065322680-5" class="crayon-line">
    }
</div>
<div id="crayon-5b8f6aab02f49065322680-6" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

计算数据指的是由其他 observable/computed data
转换而来,更方便视图直接使用的衍生数据(derived
data)。 **在重业务轻交互的 web 类应用中(通常是各种企业服务软件),
computed data 在 ViewModel 中应该占主要部分,且基本是由业务 store
中的数据(即应用状态)转换而来。** computed
这种数据推导关系描述能确保我们的应用遵循 single source of truth
原则,不会出现数据不一致的情况,这也是 RP 编程中的基本原则之一。
  • action
    ViewModel 中的 action
    除了一小部分变动视图状态的作为外,大多数应当是一贯调用 Model/Store
    中的 action 来形成业务情形的漂流。建议把持有对 observable data
    的操作都放置被 aciton 装饰的点子下实行。

mobx 合作下,3个针锋绝对完整的 ViewModel 差不多长这么:

import UserStore from ‘./UserStore’; class ViewModel {
@inject(UserStore) store; @observable isDropdownOpened = false;
@computed get userName() { return `${this.store.firstName}
${this.store.lastName}`; } <a
href=”;
toggel() { this.isDropdownOpened = !isDropdownOpened; }
updateFirstName(firstName) { this.store.updateFirstName(firstName); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import UserStore from ‘./UserStore’;
  
class ViewModel {
      
    @inject(UserStore)
    store;
    @observable
    isDropdownOpened = false;
 
@computed
get userName() {
     return `${this.store.firstName} ${this.store.lastName}`;
}
  
<a href="http://www.jobbole.com/members/Francesco246437">@action</a>
toggel() {
     this.isDropdownOpened = !isDropdownOpened;
}
      
    updateFirstName(firstName) {
        this.store.updateFirstName(firstName);
    }
}

  State 是每多少个应用程序的大旨部分,而接纳多个非法范的 State
则是让你的运用充满 bug 和失控的不二法门,或许正是部分变量环绕,让你的
state 失去了联合。有众多框架试图缓解这么些难点,比如动用不可变的
state,但是那样的话又带来了新的标题,比如数据必须规格化,完整性约束失效等等。并且它变得不容许使用强劲的定义,如原型。

4.采取实例

1.TodoList

技术栈:react + react-router(v4)+mobx+webpack

效果图:

澳门葡京 4

mobx-demo.gif

很简短的叁个小demo,那里不分析了,源码里有一对注释支持了然。
github地址:https://github.com/zhaoyu69/mobx-demo

3. 连接 AngularJS 和 mobx

<section mobx-autorun> <counter
value=”$ctrl.count”></counter> <button type=”button”
ng-click=”$ctrl.increse()”>increse</button> </section>

1
2
3
4
<section mobx-autorun>
<counter value="$ctrl.count"></counter>
    <button type="button" ng-click="$ctrl.increse()">increse</button>
</section>

import template from ‘./index.tpl.html’; class ViewModel { @observable
count = 0; <a
href=”;
increse() { this.count++; } } export default angular .module(‘app’,
[]) .component(‘container’, { template, controller: Controller, })
.component(‘counter’, { template:
‘<section><header>{{$ctrl.count}}</header></section>’
bindings: { value: ‘<‘ } }) .name;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import template from ‘./index.tpl.html’;
class ViewModel {
    @observable count = 0;
<a href="http://www.jobbole.com/members/Francesco246437">@action</a> increse() {
     this.count++;
}
}
 
export default angular
    .module(‘app’, [])
    .component(‘container’, {
     template,
     controller: Controller,
})
    .component(‘counter’, {
     template: ‘<section><header>{{$ctrl.count}}</header></section>’
     bindings: { value: ‘<‘ }
})
.name;

能够看出,除了寻常的依据 mobx 的 ViewModel
定义外,大家只要求在模板的根节点加上 mobx-autorun 指令,大家的
angularjs 组件就能很好的运作的 mobx 的响应式引擎下,从而自动的对
observable state 的变动执行 rerender。

  MobX 让全部工作又变不难了:它不容许爆发失控的
state。它的见识也一点也不细略:全数能够从 state 中派生的东西,都会自动的派生。

mobx-angularjs 加速应用的魔法

从上文的以身作则代码中大家得以看来,将 mobx 跟 angularjs
衔接运营起来的是 mobx-autorun指令,我们翻下 mobx-angularjs 代码:

const link: angular.IDirectiveLinkFn = ($scope) => { const {
$$watchers = [] } = $scope as any const debouncedDigest =
debounce($scope.$digest.bind($scope), 0); const dispose = reaction( ()
=> […$$watchers].map(watcher => watcher.get($scope)), () =>
!$scope.$root.$$phase && debouncedDigest() ) $scope.$on(‘$destroy’,
dispose) }

1
2
3
4
5
6
7
8
9
10
11
12
const link: angular.IDirectiveLinkFn = ($scope) => {
 
  const { $$watchers = [] } = $scope as any
  const debouncedDigest = debounce($scope.$digest.bind($scope), 0);
 
  const dispose = reaction(
    () => […$$watchers].map(watcher => watcher.get($scope)),
    () => !$scope.$root.$$phase && debouncedDigest()
  )
 
  $scope.$on(‘$destroy’, dispose)
}

能够见到 核心代码 其实就三行:

reaction( () => […$$watchers].map(watcher =>
watcher.get($scope)), () => !$scope.$root.$$phase &&
debouncedDigest()

1
2
3
reaction(
    () => […$$watchers].map(watcher => watcher.get($scope)),
    () => !$scope.$root.$$phase && debouncedDigest()

思路格外简单,即在指令 link 之后,遍历三次当前 scope 上挂载的 watchers
并取值,由于那么些动作是在 mobx reaction 执行上下文中开始展览的,因而 watcher
里依赖的有所 observable 都会被采访起来,那样当下次当中任何3个observable 发生改变时,都会触发 reaction 的副作用对 scope 进行digest,从而达到自动更新视图的指标。

我们领会,angularjs 的质量被广为诟病并不是因为 ‘脏检查’ 自个儿慢,而是因为
angularjs 在历次异步事件发生时都以无脑的从根节点起始向下
digest,从而会导致部分不供给的 loop 造成的。而当大家在搭载上 mobx 的
push-based 的 change propagation
机制时,唯有当被视图真正使用的数额产生变化时,相关联的视图才会触发局地digest (能够知晓为唯有 observable data 存在 subscriber/observer
时,状态变化才会接触关联依赖的重算,从而制止不须求能源消耗,即所谓的
lazy)
,分裂于异步事件触发即无脑地 $rootScope.$apply,
那种方式鲜明更高效。

  从概念上讲,Mobx会像Excel 表格一样处理你的应用程序。

更为压榨品质

咱俩理解 angularjs 是通过吓唬各个异步事件然后从根节点做 apply
的,那就招致只要大家用到了会被 angularjs 要挟的风味就会触发
apply,别的的比如说 $http $timeout 都好说,我们有那个替代方案,可是 ng-click 那类事件监听指令大家鞭长莫及制止,仿佛上文例子中相同,若是大家能杜绝潜藏的根节点
apply,想必使用的品质升高能更为。

思路一点也不细略,大家若是把 ng-click 之流替换到不触发 apply
的本子即可。比如把原来的 ng event
实现这般改一下:

forEach( ‘click dblclick mousedown mouseup mouseover mouseout mousemove
mouseenter mouseleave keydown keyup keypress submit focus blur copy cut
paste’.split(‘ ‘), function(eventName) { var directiveName =
directiveNormalize(‘native-‘ + eventName);
ngEventDirectives[directiveName] = [‘$parse’, ‘$rootScope’,
function($parse, $rootScope) { return { restrict: ‘A’, compile:
function($element, attr) { var fn = $parse(attr[directiveName], /*
interceptorFn */ null, /* expensiveChecks */ true); return function
ngEventHandler(scope, element) { element.on(eventName, function(event) {
fn(scope, {$event:event}) }); }; } }; }]; } );

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
forEach(
  ‘click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste’.split(‘ ‘),
  function(eventName) {
    var directiveName = directiveNormalize(‘native-‘ + eventName);
    ngEventDirectives[directiveName] = [‘$parse’, ‘$rootScope’, function($parse, $rootScope) {
      return {
        restrict: ‘A’,
        compile: function($element, attr) {
          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
          return function ngEventHandler(scope, element) {
            element.on(eventName, function(event) {
              fn(scope, {$event:event})
            });
          };
        }
      };
    }];
  }
);

时刻监听的回调中只是简短触发一下绑定的函数即可,不再 apply,bingo!

澳门葡京 5

注意事项/ best practise

在 mobx 协作 angularjs 开发进度中,有一对点大家兴许会 蒙受/须要考虑:

  • 避免 TTL
    单向数据流优点很多,抢先59%场景下大家会先行采纳 one-way binding
    形式定义组件。平时你会写出如此的代码:
class ViewModel { @computed get unCompeletedTodos() { return
this.store.todos.filter(todo =&gt; !todo.compeleted) } }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f63385526810-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f63385526810-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f63385526810-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f63385526810-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f63385526810-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f63385526810-6">
6
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f63385526810-1" class="crayon-line">
class ViewModel {
</div>
<div id="crayon-5b8f6aab02f63385526810-2" class="crayon-line crayon-striped-line">
    @computed
</div>
<div id="crayon-5b8f6aab02f63385526810-3" class="crayon-line">
    get unCompeletedTodos() {
</div>
<div id="crayon-5b8f6aab02f63385526810-4" class="crayon-line crayon-striped-line">
        return this.store.todos.filter(todo =&gt; !todo.compeleted)
</div>
<div id="crayon-5b8f6aab02f63385526810-5" class="crayon-line">
    }
</div>
<div id="crayon-5b8f6aab02f63385526810-6" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>



&lt;section mobx-autorun&gt; &lt;todo-panel
todos="$ctrl.unCompeletedTodos"&gt;&lt;/todo-panel&gt;
&lt;/section&gt;

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f67732098935-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f67732098935-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f67732098935-3">
3
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f67732098935-1" class="crayon-line">
&lt;section mobx-autorun&gt;
</div>
<div id="crayon-5b8f6aab02f67732098935-2" class="crayon-line crayon-striped-line">
    &lt;todo-panel todos=&quot;$ctrl.unCompeletedTodos&quot;&gt;&lt;/todo-panel&gt;
</div>
<div id="crayon-5b8f6aab02f67732098935-3" class="crayon-line">
&lt;/section&gt;
</div>
</div></td>
</tr>
</tbody>
</table>

`todo-panel` 组件使用单向数据绑定定义:



angular .module('xxx', \[\]) .component('todoPanel', { template:
'&lt;ul&gt;&lt;li ng-repeat="todo in $ctrl.todos track by
todo.id"&gt;{{todo.content}}&lt;/li&gt;&lt;/ul&gt;' bindings: {
todos: '&lt;' } })

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f6a192201026-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f6a192201026-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f6a192201026-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f6a192201026-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f6a192201026-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f6a192201026-6">
6
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f6a192201026-1" class="crayon-line">
angular
</div>
<div id="crayon-5b8f6aab02f6a192201026-2" class="crayon-line crayon-striped-line">
    .module('xxx', [])
</div>
<div id="crayon-5b8f6aab02f6a192201026-3" class="crayon-line">
    .component('todoPanel', {
</div>
<div id="crayon-5b8f6aab02f6a192201026-4" class="crayon-line crayon-striped-line">
     template: '&lt;ul&gt;&lt;li ng-repeat=&quot;todo in $ctrl.todos track by todo.id&quot;&gt;{{todo.content}}&lt;/li&gt;&lt;/ul&gt;'
</div>
<div id="crayon-5b8f6aab02f6a192201026-5" class="crayon-line">
     bindings: { todos: '&lt;' }
</div>
<div id="crayon-5b8f6aab02f6a192201026-6" class="crayon-line crayon-striped-line">
 })
</div>
</div></td>
</tr>
</tbody>
</table>

看上去没有任何问题,但是当你把代码扔到浏览器里时就会收获一段
angularjs 馈赠的 TTL
错误:`Error: $rootScope:infdigInfinite $digest Loop`。实际上这并不是
mobx-angularjs 惹的祸,而是 angularjs 目前未实现 one-way binding 的
deep comparison
导致的,由于每次 `get unCompeletedTodos` 都会返回一个新的数组引用,而`又是基于引用作对比,从而每次 prev === current` 都是
false,最后自然报 TTL 错误了(具体可以看这里 [One-way bindings +
shallow
watching](https://github.com/angular/angular.js/issues/14039) )。

不过好在 mobx
优化手段中恰好有一个方法能间接的解决这个问题。我们只需要给 computed
加一个表示要做深度值对比的 modifier 即可:



@computed.struct get unCompeletedTodos() { return
this.store.todos.filter(todo =&gt; !todo.compeleted) }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f6e965400248-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f6e965400248-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f6e965400248-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f6e965400248-4">
4
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f6e965400248-1" class="crayon-line">
@computed.struct
</div>
<div id="crayon-5b8f6aab02f6e965400248-2" class="crayon-line crayon-striped-line">
get unCompeletedTodos() {
</div>
<div id="crayon-5b8f6aab02f6e965400248-3" class="crayon-line">
    return this.store.todos.filter(todo =&gt; !todo.compeleted)
</div>
<div id="crayon-5b8f6aab02f6e965400248-4" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

本质上还是对 unCompeletedTodos 的
memorization,只不过对比基准从默认的值对比(===)变成了结构/深度
对比,因而在第一次 get unCompeletedTodos
之后,只要计算出来的结果跟前次的结构一致(只有当 computed data 依赖的
observable 发生变化的时候才会触发重算),后续的 getter
都会直接返回前面缓存的结果,从而不会触发额外的 diff,进而避免了 TTL
错误的出现。
  • $onInit 和 $onChanges 触发顺序的题材
    平常情状下大家希望在 ViewModel 中凭借组件的 lifecycle
    钩子做一些作业,比如在 $onInit 中触发副效率(网络请求,事件绑定等),在 $onChanges 里监听传入数据变化做视图更新。
class ViewModel { $onInit() { this.store.fetchUsers(this.id); }
$onChanges(changesObj) { const { id } = changesObj; if(id &&
!id.isFirstChange()) { this.store.fetchUsers(id.currentValue) } } }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f71226980952-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f71226980952-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f71226980952-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f71226980952-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f71226980952-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f71226980952-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f71226980952-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f71226980952-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f71226980952-9">
9
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f71226980952-10">
10
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f71226980952-11">
11
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f71226980952-12">
12
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f71226980952-13">
13
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f71226980952-1" class="crayon-line">
class ViewModel {
</div>
<div id="crayon-5b8f6aab02f71226980952-2" class="crayon-line crayon-striped-line">
    
</div>
<div id="crayon-5b8f6aab02f71226980952-3" class="crayon-line">
    $onInit() {
</div>
<div id="crayon-5b8f6aab02f71226980952-4" class="crayon-line crayon-striped-line">
     this.store.fetchUsers(this.id);  
</div>
<div id="crayon-5b8f6aab02f71226980952-5" class="crayon-line">
    }
</div>
<div id="crayon-5b8f6aab02f71226980952-6" class="crayon-line crayon-striped-line">
    
</div>
<div id="crayon-5b8f6aab02f71226980952-7" class="crayon-line">
    $onChanges(changesObj) {
</div>
<div id="crayon-5b8f6aab02f71226980952-8" class="crayon-line crayon-striped-line">
        const { id } = changesObj;
</div>
<div id="crayon-5b8f6aab02f71226980952-9" class="crayon-line">
        if(id &amp;&amp; !id.isFirstChange()) {
</div>
<div id="crayon-5b8f6aab02f71226980952-10" class="crayon-line crayon-striped-line">
            this.store.fetchUsers(id.currentValue)
</div>
<div id="crayon-5b8f6aab02f71226980952-11" class="crayon-line">
        }
</div>
<div id="crayon-5b8f6aab02f71226980952-12" class="crayon-line crayon-striped-line">
    }
</div>
<div id="crayon-5b8f6aab02f71226980952-13" class="crayon-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

可以发现其实我们在 `$onInit` 和 `$onChanges` 中做了重复的事情,而且这种写法也与我们要做视图框架无关的数据层的初衷不符,借助
mobx 的 observe 方法,我们可以将上面的代码改造成这种:



import { ViewModel, postConstruct } from 'mmlpx'; @ViewModel class
ViewModel { @observable id = null; @postConstruct onInit() {
observe(this, 'id', changedValue =&gt;
this.store.fetchUsers(changedValue)) } }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f6aab02f74018689164-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f74018689164-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f74018689164-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f74018689164-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f74018689164-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f74018689164-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f74018689164-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f74018689164-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f74018689164-9">
9
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f74018689164-10">
10
</div>
<div class="crayon-num" data-line="crayon-5b8f6aab02f74018689164-11">
11
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f6aab02f74018689164-12">
12
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f6aab02f74018689164-1" class="crayon-line">
import { ViewModel, postConstruct } from 'mmlpx';
</div>
<div id="crayon-5b8f6aab02f74018689164-2" class="crayon-line crayon-striped-line">
@ViewModel
</div>
<div id="crayon-5b8f6aab02f74018689164-3" class="crayon-line">
class ViewModel {
</div>
<div id="crayon-5b8f6aab02f74018689164-4" class="crayon-line crayon-striped-line">
    
</div>
<div id="crayon-5b8f6aab02f74018689164-5" class="crayon-line">
    @observable
</div>
<div id="crayon-5b8f6aab02f74018689164-6" class="crayon-line crayon-striped-line">
    id = null;
</div>
<div id="crayon-5b8f6aab02f74018689164-7" class="crayon-line">
    
</div>
<div id="crayon-5b8f6aab02f74018689164-8" class="crayon-line crayon-striped-line">
    @postConstruct
</div>
<div id="crayon-5b8f6aab02f74018689164-9" class="crayon-line">
    onInit() {
</div>
<div id="crayon-5b8f6aab02f74018689164-10" class="crayon-line crayon-striped-line">
        observe(this, 'id', changedValue =&gt; this.store.fetchUsers(changedValue))
</div>
<div id="crayon-5b8f6aab02f74018689164-11" class="crayon-line">
    }
</div>
<div id="crayon-5b8f6aab02f74018689164-12" class="crayon-line crayon-striped-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

熟悉 angularjs 的同学应该能发现,事实上 observe
做的事情跟 `$scope.$watch` 是一样的,但是为了保证数据层的 UI
框架无关性,我们这里用 mobx 自己的观察机制来替代了 angularjs 的
watch。
  • 遗忘您是在写
    AngularJS,把它就是2个简单易行的动态模板引擎
    无论是我们尝试将
    AngularJS 应用 ES6/TS 化依然引入 mobx
    状态管理库,实际上咱们的初衷都是将大家的 Model 甚至 ViewModel
    层做成视图框架毫不相关,在借助 mobx 管理数据的之间的重视性关系的还要,通过
    connector 将 mobx observable data
    与视图连接起来,从而达成视图正视的情景产生变化自动触发视图的翻新。在那一个历程中,angularjs
    不再扮演2个框架的剧中人物影响全部系统的框架结构,而只有是作为二个动态模板引擎提供
    render 能力而已,后续大家一齐能够经过配套的 connector,将 mobx
    管理的数量层连接到分歧的 view library 上。近年来 mobx 官方针对
    React/Angular/AngularJS 均有对应的 connector,社区也有指向 vue
    的消除方案,并不供给大家从零开头。在借助 mobx
    创设数据层之后,大家就能真正达成规范 MVVM 中讲述的那样,在 Model
    甚至 VIewModel 不改一行代码的前提下轻松适配别的视图。view library
    的语法、机制差别不再成为视图层 升级/替换
    的界限,咱们能经过改很微量的代码来填平它,毕竟只是替换3个动态模板引擎而已。

 

Why MobX

React and MobX together are a powerful combination. React renders the
application state by providing mechanisms to translate it into a tree
of renderable components. MobX provides the mechanism to store and
update the application state that React then uses.

Both React and MobX provide optimal and unique solutions to common
problems in application development. React provides mechanisms to
optimally render UI by using a virtual DOM that reduces the number of
costly DOM mutations. MobX provides mechanisms to optimally
synchronize application state with your React components by using a
reactive virtual dependency state graph that is only updated when
strictly needed and is never stale.

MobX 官方的介绍,把地点一段介绍中的 React 换到自由其余(
Vue/Angular/AngularJS ) 视图框架/库(VDOM 部分适当调整一下)
也都适用。得益于 MobX
的定义简单及独立性,它非凡适合营为视图中立的场合管理方案。简言之是视图层只做拿多少渲染的劳作,状态流转由
MobX 帮您管理。

  • 先是,有3个 state,它能够是贰个object,array,primitives等等任何组成你程序的有些。你能够把那些想象成你应用程序的“单元格”。
  • 然后正是 derivations,一般它是指能够从 state
    中央直机关接计算的来的结果。比如未到位的天职的多寡,那么些比较简单,也足以稍复杂一些诸如渲染你的职务展现的html。它相仿于你的应用程序中的“公式和图纸”。
  • Reactions 和 derivations 很像,主要的分别在于
    reactions 并不产生多少结果,而是自行达成都部队分义务,一般是和 I/O
    相关的。他们确定保证了 DOM 和 网络请求会活动适时地出发。
  • 末段是 actions。Actions
    指的是兼备会改变 state 的事情,MobX 保险全部 actions 都会有对应的
    derivations 和 reactions 相伴,保证同步。

Why Not Redux

Redux 很好,而且社区也有无数跟除 React
之外的视图层集成的实践。单纯的比较 Redux 跟 MobX
差不离须求再写一篇小说来阐释,那里只简简单单说几点与视图层集成时的差别:

  1. 即使 Redux 本质也是贰个观看者模型,不过在 Redux
    的落到实处下,状态的成形并不是通过数量 diff
    得出而是 dispatch(action) 来手动布告的,而真的的 diff
    则交给了视图层,那不但导致恐怕的渲染浪费(并不是兼具 library 都有
    vdom),在拍卖各样要求在变更时触发副功效的意况也会显得过于繁琐。
  2. 鉴于第③条 Redux 不做多少
    diff,因而大家鞭长莫及在视图层接手数据前搜查缴获哪个局地被更新,进而无法更飞速的选用性更新视图。
  3. Redux 在 store 的安排上是 opinionated
    的,它推广 单一 store 原则。应用能够完全由气象数据来讲述、且情形可治本可回溯
    那或多或少上自小编从未看法,但并不是唯有单一 store这一条出路,多 store
    仍然能达到这一对象。明显 mobx 在那或多或少上是 unopinionated
    且灵活性更强。
  4. Redux
    概念太多而自作者做的又太少。能够对照一下 ngRedux 跟 mobx-angularjs 看看完成复杂度上的反差。

2.A simple todo store…

  理论说的够多的了,看2个事例大概会更精通一些。大家从叁个简短的 todo
程序开首。

   为了原创性,让大家从3个分外简单的ToDoStore先导。上边是3个要命直接的TodoStore,它爱戴着贰个todos的聚众。没有MobX出席。

class TodoStore {
    todos = [];

    get completedTodosCount() {
        return this.todos.filter(
            todo => todo.completed === true
        ).length;
    }

    report() {
        if (this.todos.length === 0)
            return "<none>";
        return `Next todo: "${this.todos[0].task}". ` + 
            `Progress: ${this.completedTodosCount}/${this.todos.length}`; 
    }

    addTodo(task) {
        this.todos.push({ 
            task: task,
            completed: false,
            assignee: null
        });
    }
}

const todoStore = new TodoStore();

  大家恰好创建了一个暗含todos集合的todoStore实例。用有个别指标填充todoStore。为了确保咱们看看大家的改动的影响,我们在历次变更后调用todoStore.report并记下它。

  请留意,报表有意总是只打字与印刷第1个职责。它使这些例子有点人工,但正如你将见到上面它很好地示范了MobX的依靠关系跟踪是动态的。

澳门葡京 6 ===》 澳门葡京 7

3.Becoming reactive

   到最近截止,那段代码没有啥越发之处。不过如果大家能够不再手动调用
report
方法,只注明大家希望在每一回状态更改时调用它?我们只必要在想要的地点修改这么些state,全体的申报都自动来做。

   幸运的是,那就是MobX能够为您做的。自动执行完全在于状态的代码。那样大家的报表功用就会自动更新,就好像电子表格中的图表一样。为了兑现这或多或少,TodoStore必须成为可观看的(observable),以便MobX能够跟踪全部正在拓展的转移。让咱们改变类就能够完毕它。

  同时,completedTodosCount
属性应该被活动派生。通过采纳@observable和@computed装饰器,我们可以在对象上引入observable属性:

class ObservableTodoStore {
    @observable todos = [];
    @observable pendingRequests = 0;

    constructor() {
        mobx.autorun(() => console.log(this.report));
    }

    @computed get completedTodosCount() {
        return this.todos.filter(
            todo => todo.completed === true
        ).length;
    }

    @computed get report() {
        if (this.todos.length === 0)
            return "<none>";
        return `Next todo: "${this.todos[0].task}". ` + 
            `Progress: ${this.completedTodosCount}/${this.todos.length}`; 
    }

    addTodo(task) {
        this.todos.push({ 
            task: task,
            completed: false,
            assignee: null
        });
    }
}


const observableTodoStore = new ObservableTodoStore();

 

   至此!大家将有个别天性标记为@observable,以提醒MobX那么些值可以随时间改变。总计用@computed来点缀,以识别那几个足以从气象导出。

   pendingRequests和assignee属性到方今截至未接纳,但将在本教程前面使用。为了简洁,本页上的具备示例都应用ES6,JSX和装饰器。但并非顾虑,全部的装修在MobX有多个ES5写法。

  在构造函数中,咱们创设了三个report()将其包装在mobx.autorun()。mobx.autorun()创建二个运维三回的reaction,然后每当在函数内部使用的别的可观望数据产生变化时,自动重国民党的新生活运动行。因为report()用observable
todos属性,所以它会在适当时打字与印刷表格。那在下3个列表中示范。只需按下运行按钮:

澳门葡京 8==>澳门葡京 9

   纯函数,对吗?report自动打字与印刷了,同步并且没有败露风声的中间值。假若你细心查阅运营结果的话,你会发觉大家的第六句语句没有发出输出,因为大家修改了todos[1]的数量,而小编辈在report中指明的数目,并不曾todos[1]的变更而产生变化。而第四句话修改了todos[0]的数量则输出了。这么些事例很好的印证了,autorun不是简单的监视了todos,而是规范到了切实的一项。

 4.Making React reactive

   Ok, so far we made a silly report reactive. Time to build a
reactive user interface around this very same store. React components
are (despite their name) not reactive out of the box.
The @observer decorator from the mobx-react package fixes that by
wrapping the React component render method in autorun, automatically
keeping your components in sync with the state. That is conceptually not
different from what we did with the report before.

   好了,到最近截止大家做了一个傻乎乎的 report
reactive。是时候用分外相像的store成立二个reactive了。React
components(就算他们的名字)没有影响开箱。

  mobx-react包中的@observer装饰器通过在mobx.autorun()包装React组件的render()方法来完结,自动保持组件与气象同步。那在概念上与我们在此在此之前的告诉并未怎么两样

   上边包车型地铁清单定义了多少个React组件。在那边唯一属于MobX的东西是@observer装饰器。那可以保险每一种组件在有关数据变动时独立再次render。您不必要再调用setState,也不要驾驭哪些接纳必要配置的选用器或更尖端的机件来(subscribe
:redux中有 )订阅应用程序状态的适宜部分。基本上,全数组件都变得聪明。然则,它们是以死板的,评释性的方法定义的。

@observer
class TodoList extends React.Component {
  render() {
    const store = this.props.store; 
    return (
      <div>
        { store.report }
        <ul>
        { store.todos.map(
          (todo, idx) => <TodoView todo={ todo } key={ idx } />
        ) }
        </ul>
        { store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null }
        <button onClick={ this.onNewTodo }>New Todo</button>
        <small> (double-click a todo to edit)</small>
        <RenderCounter />
      </div>
    );
  }

  onNewTodo = () => { 
    this.props.store.addTodo(prompt('Enter a new todo:','coffee plz')); 
  } 
}

@observer
class TodoView extends React.Component {
  render() {
    const todo = this.props.todo;
    return (
      <li onDoubleClick={ this.onRename }>
        <input 
          type='checkbox'
          checked={ todo.completed }
          onChange={ this.onToggleCompleted } 
        />
        { todo.task }
        { todo.assignee 
          ? <small>{ todo.assignee.name }</small> 
          : null
        }
        <RenderCounter />
      </li>
    ); 
  }

  onToggleCompleted = () => {
    const todo = this.props.todo;
    todo.completed = !todo.completed;
  }

  onRename = () => {
    const todo = this.props.todo;
    todo.task = prompt('Task name', todo.task) || todo.task; 
  } 
}

ReactDOM.render(
  <TodoList store={ observableTodoStore } />, 
  document.getElementById('reactjs-app')
);

  下1个清单很好地呈现,大家只须求变更大家的多寡,而不做任何其余业务。
MobX将电动从存款和储蓄中的状态重新导出和换代用户界面包车型地铁有关部分。

澳门葡京 10

  运维结果:

澳门葡京 11

 

最后

除去给 AngularJS
搭载上更敏捷、精确的飞跃引擎之外,我们最关键的目标也许为了将
业务模型层甚至 视图模型层(统称为使用数据层) 做成 UI
框架非亲非故,那样在面对差异的视图层框架的迁徙时,才恐怕成功轻车熟路。而
mobx 在这几个工作上是3个很好的选项。

终极想说的是,假使条件允许的话,依旧建议将 angularjs 系统升级成
React/Vue/Angular
之一,终归超过四分之二时候基于新的视图技术开发应用是能带来真正的收入的,如
质量提高、开发成效提高 等。即使你长期内无法交替掉
angularjs(各样要素,比如曾经依据 angularjs 开发/使用
了一套完整的零部件库,代码体积太大改造资金过高),你依然能够在一部分使用
mobx/mobx-angularjs 改造使用或开发新作用,在 mobx-angularjs
帮忙您升官利用质量的同时,也给您继承的升级布置成立了大概。

PS: mobx-angularjs 近期由本身和另3个US 小哥全力保障,若是有其余利用上的难题,欢迎随时联系。

1 赞 收藏
评论

澳门葡京 12

5.Working with references

  到近期结束,大家早就创办了observable对象(原型对象和plain对象),数组和基元(primitives)。你或然想清楚,在MobX中哪些处理引用?作者的state是不是同意形成图表?在上二个列表中,您也许已经注意到todos有四个assignee 属性。让大家给他俩一些值,通过引入另一个“store”(它只是八个glorified数组)包蕴人,和分配给她们的天职。

 澳门葡京 13  澳门葡京 14

 

  那么难题来了,observableTodoStore.todos本来正是@observable的,奈何有要再扩充二个吗?

   答:大致因为改变的时候方便?

 

  We now have two independent stores. One with people and one with
todos. To assign an assignee to a person from the people store, we
just assigned a reference. These changes will be picked up automatically
by the TodoView. With MobX there is no need to normalize data first
and to write selectors to make sure our components will be updated. In
fact, it doesn’t even matter where the data is stored. As long as
objects are made observable, MobX will be able to track them. Real
JavaScript references will just work. MobX will track them automatically
if they are relevant for a derivation. To test that, just try changing
your name in the next input box (make sure you have pressed the
above Run code button first!).

  大家未来有七个独立的专营商。一人和三个todos。要从people
store中分红一个person的代办,我们只必要分配了2个参照。那些改动将由TodoView自动采取。使用MobX,没有须求首先规范化数据,并且编写接纳器以确认保证大家的零部件将被更新。事实上,数据存款和储蓄的职位还是都不主要。只要对象是可寓指标,MobX将能够跟踪它们。真正的JavaScript引用将健康办事。
MobX将机关跟踪它们,假使它们与派生相关。要测试,只需尝试在下二个输入框中更改您的称号(确定保证您已经按上边的Run代码按钮!)。

   澳门葡京 15

 

 

6.Asynchronous actions

   因为大家的小Todo应用程序中的一切都以从气象派生而来的,所以当状态改变时它并不重庆大学。那使得创制异步操作真的很简短。只需按上面的按钮(数十三遍)来效仿异步加载新的todo项:

   前边的代码真的相当的粗略。大家从立异存款和储蓄属性pendingRequests早先,让UI反映当前的加载状态。一旦加载成功,大家创新商店的todos并再度回落pendingRequests计数器。只需将此代码段与前期的TodoList定义进行比较,即可理解哪些利用pendingRequests属性

 澳门葡京 16

7.Conclusion

   就这么!没有样板。只是某些UI组件中的简单的注脚性组件,形成我们的总体的UI。那个是一心地,反应性地从大家的state中拿走。以后,您可以起来在协调的应用程序中央银行使mobx和mobx-react包。您到近期甘休学到的事物的简短摘要:

  • 应用@observable装饰器或observable(obj or
    array)函数令对象可以被MobX追踪。
  • @computed装饰器能够用来成立可以从气象自动导出其值的函数。
  • 应用autorun运维注重于有些可旁观情况的函数。那对log,互连网请求等很有用.
  • 选用来源mobx-react包的@observer装饰器,使你的React组件真正被动。他们将自动和有效性地立异。就算用于全数大批量数码的重型复杂应用程序。

   (小编那边是老大的)

  能够肆意行使方面包车型大巴可编写制定代码块玩一段时间,以获得MobX对富有改变的反应的大旨感觉。例如,您能够向报告函数添加一条日志语句,以查看它被调用的年月。或然根本不显得报告,看看它什么影响TodoList的表现。或仅在一定情景下突显…

 

8.MobX is not a state container

   人们平日应用MobX作为Redux的替代品。但请小心,MobX只是2个库来缓解技术难点,而不是2个框架结构或甚至状态容器本人。在那么些意思上,上边包车型大巴例证是布置性的,并且建议利用方便的工程实施,如在点子中封装逻辑,在铺子或控制器等组织它们。大概,正如哈克erNews上的某人所说:

澳门葡京 17

  “MobX,它在任什么地点方被提到,但作者必须夸奖它。在MobX中编辑意味着使用控制器/调度程序/操作/管理程序或另一种样式的治本数据流重返到2个架构难点,您能够情势化您的应用程序的内需,而不是暗中同意情状下必要的别样东西比1个Todo应用程序。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website