基于 MobX 构建视图框架无关的数据层-与 Vue 的结

2019-10-11 12:48 来源:未知

使用

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>

三个月前看了vue源码来分析如何做到响应式数据的, 文章名字叫vue源码之响应式数据, 最后分析到, 数据变化后会调用Watcher的update()方法. 那么时隔三月让我们继续看看update()做了什么. (这三个月用react-native做了个项目, 也无心总结了, 因为好像太简单了).

基于组件的应用架构

AngularJS 在 1.5.0 版本后新增了一系列激动人心的特性,如 onw-way bindings、component lifecycle hooks、component definition 等,基于这些特性,我们可以方便的将 AngularJS 系统打造成一个纯组件化的应用(如果你对这些特性很熟悉可直接跳过至 AngularJS 搭配 mobx)。我们一个个来看:

  • 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/$compile#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 的同学应该已经发现了 
TAG标签: JavaScript
版权声明:本文由彩民之家高手论坛发布于前端知识,转载请注明出处:基于 MobX 构建视图框架无关的数据层-与 Vue 的结