创设视图框架无关的数据层,状态管理入门及实例

老树发新芽—使用 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.

即在 十一月5日 AngularJS 发布 1.7.0 版本之后,AngularJS 将进入四个年限 3
年的 LTS 时代。也正是说 2018年十五月1三十一日 起至 2021年一月2二二十三日,AngularJS
不再统一别的会造成 breaking changes 的 features 或
bugfix,只做供给的题材修复。详细消息见这里:Stable AngularJS and Long
Term
Support

探望那则音信时笔者要么感动颇多的,作为本身的前端启蒙框架,作者从 AngularJS
上得出到了要命多的滋养。即使 AngularJS 作为一款能够的前端 MVW
框架已经完美的成功了团结的历史职责,但考虑到就是到了 2018
年,许多集团根据 AngularJS 的项目依旧高居服役阶段,结合本人过去一年多在
mobx 上的探赜索隐和实施,笔者主宰给 AngularJS
强行再续一波命。(搭车求治拖延症良方,3月尾起草的作品5月份才写完,音讯都要晚点了)

听大人讲 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。

AngularJS 配合 ES6/next

现行反革命是二零一八年,使用 ES6 开发应用已经成为事实标准(有恐怕的推荐直接上 TS
)。如何将 AngularJS 搭载上 ES6
那里不再赘述,能够看小编事先的这篇小说:Angular1.x + ES6
开发风格指南

使用

mobx-vue 的选用13分不难,只供给选取 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是1个比redux更好的场合管理包,代码量更少,思路更明显,没有像redux那样复杂的reducer,action
(ps: redux的撰稿人也推荐mobx,某大大告诉自个儿的,并不曾原话链接)

React是一个状态机,由开端的上马状态,通过与用户的互相,导致情状变化,从而再一次渲染UI。

根据组件的施用框架结构

AngularJS 在 1.5.0 版本后新增了一文山会海动人心弦的风味,如 onw-way
bindings、component lifecycle hooks、component definition
等,基于那几个特征,我们能够方便的将 AngularJS
系统构建成四个纯组件化的采取(若是你对那几个特色很熟稔可一向跳过至 AngularJS
搭配
mobx)。大家1个个来看:

  • 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.七日增),写起来差不多长这么:
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.

2014年本身在塑造公司级组件库的时候初叶探究三个标题,我们怎么在代码库基于某一框架的景况下,能以尽力而为小的代价在以后将零件库迁移到任何
框架/库
下?总不能够依据新的技术全部重写2遍呢,那也太浪费生命了。且不说对于基础控件而言,交互/行为
逻辑基本上是可分明的,最多也便是 UI
上的片段调动,而且唯有为了尝试新技巧成本公司人力物力将基础库推导重写也是不行不工作的做法。那么大家不得不承受被框架绑架而不得不沦为某一技术栈从此泥潭深陷吗?对于前端那种框架半衰期特别短的天地而言肯定是不足接受的,结果唯有正是要么自己跑路坑后来人,要么招不到人来3头填坑…
不难的话大家无能为力享受新技巧带来的各样红利。

在 MVVM 架构视角下,越是重型的使用其复杂度越是集中在 M(Model) 跟
VM(ViewModel) 那两层,特别是 Model
层,理论上相应是能脱离上层视图独立运维独立公布独立测试的存在。而相应的不一致视图框架只是使用了不相同绑定语法的动态模板引擎而已,那一个观点作者在头里的几篇小说里都讲述过。所以只要大家将视图层做的很薄,大家迁移的工本自然会降到2个可承受的范畴,甚至有只怕通过工具在编译期自动生成差异框架的视图层代码。

要到位 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 感应流程

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

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();
};

完整代码在那边:

美高梅开户网址 1

但对此复杂的中山高校型应用,引入状态管理库是”供给的”。

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 收藏
评论

美高梅开户网址 2

 2.the core idea

于今走俏的情景管理解决方案Redux,MobX相继进入开发者的视野。

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 协作下,三个争持完整的 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 是每3个应用程序的主干部分,而选择二个不合法范的 State
则是让您的施用充满 bug 和失控的不二法门,大概就是一些变量环绕,让您的
state 失去了三只。有过多框架试图缓解那一个标题,比如采纳不可变的
state,不过这么以来又推动了新的难点,比如数据必须规格化,完整性约束失效等等。并且它变得不容许行使强劲的定义,如原型。

Redux、MobX哪叁个更合乎你的品种?

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 中派生的事物,都会活动的派生。

在react项目中应用redux or mobx?

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 都会被采集起来,那样当下次其中任何三个observable 爆发转移时,都会触发 reaction 的副成效对 scope 进行digest,从而达成自动更新视图的指标。

咱俩精晓,angularjs 的属性被广为诟病并不是因为 ‘脏检查’ 本人慢,而是因为
angularjs 在历次异步事件时有发生时都以无脑的从根节点早先向下
digest,从而会促成有的不供给的 loop 造成的。而当大家在搭载上 mobx 的
push-based 的 change propagation
机制时,除非当被视图真正使用的数目产生变化时,相关联的视图才会触发局部digest (能够知道为唯有 observable data 存在 subscriber/observer
时,状态变化才会触发关联重视的重算,从而幸免不要求能源消耗,即所谓的
lazy)
,不一致于异步事件触发即无脑地 $rootScope.$apply,
那种措施鲜明更飞速。

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

正如爱因Stan所说的 “ 让全体事物尽或然的简练,但不要简单”。

更是压榨质量

作者们明白 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!

美高梅开户网址 3

就算让大家来填一填 MobX 的坑。

注意事项/ best practise

在 mobx 合作 angularjs 开发进度中,有部分点大家或者会 遭逢/需求考虑:

  • 避免 TTL
    单向数据流优点很多,超过六分之三情况下大家会事先利用 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,把它便是多个粗略的动态模板引擎
    随正是大家尝试将
    AngularJS 应用 ES6/TS 化依旧引入 mobx
    状态管理库,实际上大家的初衷都以将我们的 Model 甚至 ViewModel
    层做成视图框架无关,在借助 mobx 管理数据的时期的依靠关系的还要,通过
    connector 将 mobx observable data
    与视图连接起来,从而实现视图正视的状态产生变化自动触发视图的换代。在那几个进度中,angularjs
    不再扮演二个框架的剧中人物影响总体系统的架构,而单独是当做多个动态模板引擎提供
    render 能力而已,后续大家全然能够由此配套的 connector,将 mobx
    管理的多寡层连接到分歧的 view library 上。近来 mobx 官方针对
    React/Angular/AngularJS 均有照应的 connector,社区也有针对性 vue
    的缓解方案,并不供给大家从零初始。在借助 mobx
    创设数据层之后,我们就能确实成功专业 MVVM 中讲述的那样,在 Model
    甚至 VIewModel 不改一行代码的前提下轻松适配别的视图。view library
    的语法、机制差距不再成为视图层 升级/替换
    的壁垒,我们能因而改很微量的代码来填平它,究竟只是替换1个动态模板引擎而已。

 

1.入门

美高梅开户网址 4image

对于使用开发中的常见难题,React 和
MobX都提供了最优和新鲜的缓解方案。React 提供了优化UI渲染的体制,
那种机制即使经过行使虚拟DOM来压缩昂贵的DOM变化的数据。MobX
提供了优化利用状态与 React
组件同步的机制,那种机制正是应用响应式虚拟依赖状态图表,它唯有在真的须求的时候才履新还要永远保持是最新的。

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 帮您管理。

  • 先是,有三个 state,它能够是一个object,array,primitives等等任何组成你程序的一部分。你能够把这么些想象成你应用程序的“单元格”。
  • 然后便是 derivations,一般它是指可以从 state
    中一贯计算的来的结果。比如未到位的任务的数码,那个比较简单,也足以稍复杂一些诸如渲染你的职务显示的html。它相仿于您的应用程序中的“公式和图纸”。
  • Reactions 和 derivations 很像,主要的区分在于
    reactions 并不发生多少结果,而是自行达成都部队分职责,一般是和 I/O
    相关的。他们确定保证了 DOM 和 互连网请求会自动适时地出发。
  • 最后是 actions。Actions
    指的是富有会转移 state 的事务,MobX 保证拥有 actions 都会有相应的
    derivations 和 reactions 相伴,保障同步。

2.安装

npm install –save mobx mobx-react

Why Not Redux

Redux 很好,而且社区也有成百上千跟除 React
之外的视图层集成的履行。单纯的可比 Redux 跟 MobX
大约须求再写一篇文章来解说,那里只简单说几点与视图层集成时的出入:

  1. 就算 Redux 本质也是一个阅览者模型,但是在 Redux
    的贯彻下,状态的变更并不是透过数据 diff
    得出而是 dispatch(action) 来手动文告的,而实在的 diff
    则交由了视图层,那不只导致可能的渲染浪费(并不是怀有 library 都有
    vdom),在处理各类要求在变化时触发副成效的面貌也会来得过于繁琐。
  2. 出于第3条 Redux 不做多少
    diff,因而大家鞭长莫及在视图层接手数据前查出哪个局地被更新,进而不可能更高速的采取性更新视图。
  3. Redux 在 store 的安顿上是 opinionated
    的,它推广 单一 store 原则。应用可以完全由气象数据来描述、且景况可管理可回溯
    那或多或少上自笔者从不意见,但并不是只有单一 store这一条出路,多 store
    依然能落得这一对象。显明 mobx 在那或多或少上是 unopinionated
    且灵活性更强。
  4. Redux
    概念太多而本人做的又太少。能够对照一下 ngRedux 跟 mobx-angularjs 看看落成复杂度上的差异。

2.A simple todo store…

  理论说的够多的了,看八个事例可能会更精通一些。大家从1个简单易行的 todo
程序开端。

   为了原创性,让我们从3个相当简单的ToDoStore开首。上边是多少个丰盛直白的TodoStore,它吝惜着2个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并记下它。

  请小心,报表有意总是只打印第四个职分。它使那个例子有点人工,但正如您将见到上面它很好地示范了MobX的依靠关系跟踪是动态的。

美高梅开户网址 5 ===》 美高梅开户网址 6

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属性,所以它会在适当时打字与印刷表格。那在下1个列表中示范。只需按下运维按钮:

美高梅开户网址 7==>美高梅开户网址 8

   纯函数,对吗?report自动打字与印刷了,同步并且没有败露的中间值。若是你精心翻看运维结果的话,你会意识我们的第4句语句没有发生输出,因为大家修改了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中有 )订阅应用程序状态的11分部分。基本上,全部组件都变得通晓。可是,它们是以鸠拙的,注明性的格局定义的。

@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')
);

  下七个清单很好地显示,我们只须要变更大家的数额,而不做任何其余事情。
MobX将自行从存储中的状态重新导出和更新用户界面包车型客车相干部分。

美高梅开户网址 9

  运营结果:

美高梅开户网址 10

 

3.宗旨概念

1.state状态是驱动应用的多寡。

2.observable && @observableObservable
值能够是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;temperature.set;

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 { // 这是一个自动的动作,不需要注解 this.length = Math.sqrt; }}

6.autorun那平常是当您须要从反应式代码桥接到命令式代码的情景,例如打印日志、持久化只怕更新UI的代码。

var numbers = observable;var sum = computed => numbers.reduce => a + b, 0));var disposer = autorun => console.log));// 输出 '6'numbers.push;// 输出 '10'disposer();numbers.push;// 不会再输出任何值。`sum` 不会再重新计算。

经验法则:如若你有三个函数应该自行运维,但不会生出2个新的值,请使用autorun。
其他意况都应该利用 computed。

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

最后

除去给 AngularJS
搭载上更急迅、精确的飞跃引擎之外,我们最要紧的目标照旧为了将
业务模型层甚至 视图模型层(统称为运用数据层) 做成 UI
框架非亲非故,那样在直面区别的视图层框架的迁移时,才恐怕完毕万分熟悉。而
mobx 在那一个事情上是八个很好的挑选。

最后想说的是,借使基准允许的话,照旧建议将 angularjs 系统升级成
React/Vue/Angular
之一,毕竟超过一半时候基于新的视图技术开发应用是能带来真正的收益的,如
质量提高、开发效用升高 等。固然你长期内不能够交替掉
angularjs(二种要素,比如曾经依据 angularjs 开发/使用
了一套完整的机件库,代码容积太大改造资金过高),你依然能够在一部分使用
mobx/mobx-angularjs 改造使用或开发新作用,在 mobx-angularjs
扶助您升官利用品质的同时,也给你继续的升官安插创立了大概。

PS: mobx-angularjs 近期由本人和另2个US 小哥全力珍爱,如果有任何利用上的标题,欢迎随时联系。

1 赞 收藏
评论

美高梅开户网址 11

5.Working with references

  到近来截止,我们已经创办了observable对象(原型对象和plain对象),数组和基元(primitives)。你可能想清楚,在MobX中哪些处理引用?笔者的state是或不是同意形成图表?在上1个列表中,您或然曾经注意到todos有三个assignee 属性。让大家给他们有些值,通过引入另3个“store”(它只是二个glorified数组)包罗人,和分配给他俩的任务。

 美高梅开户网址 12  美高梅开户网址 13

 

  那么难题来了,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!).

  大家今日有多少个独立的公司。1位和多少个todos。要从people
store中分配二个person的代理,大家只必要分配了四个参照。那几个改变将由TodoView自动选择。使用MobX,没有须求首先规范化数据,并且编写选用器以保障大家的组件将被更新。事实上,数据存款和储蓄的职责依旧都不首要。只要对象是可观看标,MobX将能够跟踪它们。真正的JavaScript引用将健康办事。
MobX将自动跟踪它们,如若它们与派生相关。要测试,只需尝试在下3个输入框中更改您的名称(确定保障您已经按上边包车型客车Run代码按钮!)。

   美高梅开户网址 14

 

 

4.用到实例

1.TodoList

技术栈:react + react-router+mobx+webpack

效果图:

美高梅开户网址 15mobx-demo.gif

很简短的一个小demo,这里不分析了,源码里有部分注释辅助领悟。github地址:

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只是一个库来缓解技术难点,而不是三个架构或甚至状态容器本人。在那个意思上,上面的例子是布置性的,并且提议利用方便的工程实施,如在点子中封装逻辑,在铺子或控制器等协会它们。或然,正如哈克erNews上的某人所说:

美高梅开户网址 17

  “MobX,它在其他地点被波及,但自小编必须赞赏它。在MobX中编辑意味着使用控制器/调度程序/操作/管理程序或另一种样式的管理数据流重返到一个架构难点,您能够格局化您的应用程序的须求,而不是暗许境况下需求的别的东西比3个Todo应用程序。

发表评论

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

网站地图xml地图