首页 > javascript相关 > vue教程 > 正文

探秘vue-rx 2.0(推荐)_vue.js

2018-10-30 16:22:04

前一段时间,我写了两篇文章,一篇是对目前前端主流视图框架的思考,一篇是深入使用RxJS控制复杂业务逻辑的,在这两篇中,我分别提到:

  • 期望在复杂业务逻辑方面使用RxJS,更好地进行抽象,但是视图上使用轻量MVVM以达到快速开发的目的。
  • 目前VueJS中,如果要结合RxJS,可能需要手动订阅和取消订阅,写起来还是没有CycleJS方便。

最近,VueJS社区升级了vue-rx这个库,实现了比较方便地把VueJS和RxJS结合的能力。

我们来详细了解一下。

在视图上绑定Observable

VueJS本身不是基于RxJS这一套理念构建的,如果不借助任何辅助的东西,可能我们会需要干这么一些事情:

  • 手动订阅某些Observable,在observer里面,把数据设置到Vue的data上
  • 在视图销毁的时候,手动取消订阅

在业务开发中,我们最常用的是绑定简单的Observable,在vue-rx中,这个需求被很轻松地满足了。

与早期版本不同,vue-rx 2.0在Vue实例上添加了一个subscriptions属性,里面放置各种待绑定的Observable,用的时候类似data。

比如,我们可以这么用它:

rx-simple.vue

<template> <div>  <h4>Single Value</h4>  <div>{{single$}}</div>  <h4>Array</h4>  <ul>   <li v-for="item of arr0$">{{item}}</li>  </ul>  <ul>   <li v-for="item of arr1$">{{item}}</li>  </ul>  <h4>Interval</h4>  <div>{{interval$}}</div>  <h4>High-order</h4>  <div>{{high$}}</div> </div></template><script>import { Observable } from 'rxjs/Observable'import 'rxjs/add/observable/of'import 'rxjs/add/observable/from'import 'rxjs/add/operator/toArray'import 'rxjs/add/observable/interval'import 'rxjs/add/observable/range'import 'rxjs/add/operator/map'import 'rxjs/add/operator/mergeAll'const single$ = Observable.of(Math.PI)const arr0$ = Observable.of([1, 1, 2, 3, 5, 8, 13])const arr1$ = Observable.from([1, 1, 2, 3, 5, 8, 13]).toArray()const interval$ = Observable.interval(1000)const high$ = Observable.range(1, 5) .map(item => Observable.interval(item * 1000)) .mergeAll()export default { name: 'rx-simple', subscriptions: {  single$,  arr0$,  arr1$,  interval$,  high$ }}</script>

这个demo里面,演示了四种不同的Rx数据形态。其中,single$和interval$虽然创建方式不同,但实际上用的时候是一样的,因为,对它们的订阅,都是取其最后一个值,这两者的区别只是,一个不变了,一个持续变,但界面展示的始终是最后那个值。

关于数组,初学者需要稍微注意一下,从同样的数组,分别通过Observable.of和Observable.from出来的形态是大为不同的:

  • of创建的这个,里面只有一个值,这个值是个数组,所以,订阅它,会得到一个数组
  • from创建的这个,里面有若干个值,每个值是由数组中的元素创建的,订阅它,会一次性得到多个值,但展示的时候只会有最后一个,因为前面的都被覆盖掉了

那么,这个high$代表什么呢?

  • range操作,创建了一个流,里面有多个简单数字
  • map操作,把这个流升级为二阶,流里面每个元素又是一个流
  • mergeAll操作,把其中的每个流合并,降阶为一阶流,流里面每个元素是个简单数字

如果说不mergeAll,直接订阅map出来的那个二阶流,结果是不对的,vue-rx只支持一阶订阅绑定,不支持把高阶流直接绑定,如果有业务需要,应当自行降阶,通过各种flat、concat、merge操作,变成一阶流再进行绑定。

将Vue $watcher转换为Observable

上面我们述及的,都是从Observable的数据到Vue的ReactiveSetter和Getter中,这条路径的操作已经很简便了,我们只需把Observable放在vue实例的subscriptions里面,就能直接绑定到视图。

但是,反过来还有一条线,我们可能会需要根据某个数据的变化,让这个数据进入一个数据流,然后进行后续运算。

例如:有一个num属性,挂在data上,还有一个数据num1,表达:始终比num大1这么一件事。

当然,我们是可以直接利用computed property去做这件事的,为了使得我们这个例子更有说服力,给它这个加一计算添加一个延时3秒,强行变成异步:始终在num属性确定之后,等3秒,把自己变成比num大1的数字。

这样,computed property就写不出来了,我们可能就要手动去$watch这个num,然后在回调方法中,去延时加一,然后回来赋值给num1。

在vur-rx中,提供了一个从$watch创建Observable的方法,叫做$watchAsObservable,我们来看看怎么用:

rx-watcher.vue

<template> <div>  <h4>Watch</h4>  <div>   <button v-on:click="num++">add</button>   source: {{num}} -> result: {{num$}}  </div> </div></template><script>import 'rxjs/add/operator/pluck'import 'rxjs/add/operator/startWith'import 'rxjs/add/operator/delay'export default { name: 'rx-watch', data() {  return {   num: 1  } }, subscriptions() {  return {   num$: this.$watchAsObservable('num')    .pluck('newValue')    .startWith(this.num)    .map(a => a + 1)    .delay(3000)  } }}</script>

这个例子里面的num$经过这么几步:

  • this.$watchAsObservable('num'),把num属性的变动,映射到一个数据流上
  • 这个数据流的结果是一个对象,里面有newValue和oldValue属性,我们通常情况下,要的都是newValue,所以用pluck把它挑出来
  • 注意,这个检测的只是后续变动,对于已经存在的值,是$watch不到的,所以,用startWith,把当前值放进去
  • 然后是常规的rx运算了

那么,这件事的原理是什么呢?

我们知道,Vue实例中,data上的属性都会存在ReactiveSetter,所以它被赋值的时候,就会触发这个setter,所以,$watchAsObservable的内部只需根据数据变动,生成一个Observable就可以了。

$watchAsObservable的方法签名如下:

$watchAsObservable(expOrFn, [options])

这个options,跟vue的$watch方法的options一样。

有时候,我们会有这样的情况:在组件实例化的时候,数据流由于缺少某些条件,可能还没法创建。

比如说,某个组件,依赖于路由上面的某个参数,这时候,可能你不知道怎么去初始化绑定。

其实,产生这样的想法,本身就错了,因为没有用Rx的理念去思考问题。想一下下面这句话:

数据流的定义,与初始条件是否具备无关。

初始条件其实也只是整个数据流管道中的一节,如果初始不确定的话,我们只要给它留一个数据入口就好了,后续的流转定义可以全部写得出来。

const taskId$ = new Subject()const task$ = taskId$ .distinctUntilChanged() .switchMap(id => this.getInitialData(id))

然后,在路由变更等事件里,往这个taskId$里面next当前的id就可以了。通过这种方式,我们就可以把task$直接绑定到界面上。

或者,taskId$也可以通过在路由上面的watch转化而成,只是不能直接用$watchAsObservable,可以考虑改进一下这种情况。

这样可以实现组件canReuse的情况下,改动路由参数,触发当前页面的数据刷新,实现视图的更轻量级的刷新。

将DOM事件转化为Observable

使用RxJS可以直接把DOM事件转化为Observable,vue-rx也提供了一个类似的方法来做这个事,不过我没理解这两个东西有什么差异?具体参见官方示例吧。

构建优化

关注vue-rx的readme,可以发现,目前推荐使用绑定的方式是这样:

import Vue from 'vue'import Rx from 'rxjs/Rx'import VueRx from 'vue-rx'// tada!Vue.use(VueRx, Rx)

但这样会有一个问题,import的是rxjs/Rx,我们看到,这个文件里把所有可以被挂接到Rx对象上的东西都import进来了,这会导致构建的时候没法tree-shaking,用不到的那些操作符也被构建进来了,一个简单的demo,可能构建结果也有200多k,这还是太大了。

我们查看一下vue-rx的源码,发现传入的这个Rx是怎么使用的呢?

var obs$ = Rx.Observable.create(function (observer) {...// Returns function which disconnects the $watch expressionvar disposableif (Rx.Subscription) { // Rx5 disposable = new Rx.Subscription(unwatch)} else { // Rx4 disposable = Rx.Disposable.create(unwatch)}

这里,其实只是要使用Observable和Subscription这两个东西,所以我们可以改成这样:

import Vue from 'vue'import { Observable } from 'rxjs/Observable'import { Subscription } from 'rxjs/Subscription'import VueRx from 'vue-rx'// tada!Vue.use(VueRx, { Observable, Subscription })

再试试,构建大小只有不到100k了,而且是可以正常运行的。如果用的是Rx 4,需要传入的就是Disposable而不是Subscription。

另外,如果我们使用了$watchAsObservable,还会需要引入另外一个东西:

import 'rxjs/add/operator/publish'

这是因为在$watchAsObservable里面,为了共享Observable,把它pubish之后refCount了,所以要引入,用不到这个方法的话,可以不引。

如果使用了$fromDOMEvent,还需要引入这个:

import 'rxjs/add/observable/empty'

因为$fromDOMEvent里面的这段:

if (typeof window === 'undefined') { return Rx.Observable.empty()}

小结

有了这个库之后,我们就可以比较优雅地结合VueJS和RxJS了。之前,两者之间结合的麻烦点主要在于:

在RxJS体系中,数据的进、出这两头是有些繁琐的。

所以,CycleJS采用了比较极端的做法,把DOM体系也包括进去了,这样,编写代码的时候,数据就没有进出的成本,但这么做,其实是牺牲了一些视图层的编写效率。

而Angular2中,用的是async这个pipe来解决这问题,这也是一种比较方便的办法,在绑定Observable这一点上,跟有了vue-rx之后的Vue是差不多简便的。

React体系里面也有对RxJS的适配,而且还有跟Redux,Mobx对接的适配,感兴趣的可以自行关注。

从个人角度出发,vue-rx这次的升级很好地满足了我对复杂应用开发的需求了。

本文示例代码参见:这里

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

  • 相关标签:vue教程
  • 本文发布HTML5中文学习网 ,转载请注明出处,感谢您!
  • 相关文章


  • 曝网友假装外国人写投诉信 ofo秒退押金并回函致歉
  • 苹果市值缩水逾2000亿美元 遭多家投行下调目标价
  • Asp.net Core与类库读取配置文件信息的方法_实用技巧
  • asp.net在Repeater嵌套的Repeater中使用复选框详解_实用技巧
  • 利用IIS调试ASP.NET网站程序的完整步骤_实用技巧
  • Asp.Net Core轻松学习系列之配置文件_实用技巧
  • ASP.NET 页生命周期概述(小结)_实用技巧
  • 详解ASP.NET Core WebApi 返回统一格式参数_实用技巧
  • 2018年网络流行语有哪些?2018年十大网络流行语盘点
  • 华为首席财务官孟晚舟被暂扣 深圳市政府要求加方立即放人!
  • 独孤九贱(4)_PHP视频教程

    江湖传言:PHP是世界上最好的编程语言。真的是这样吗?这个梗究竟是从哪来的?学会本课程,你就会明白了。 PHP中文网出品的PHP入门系统教学视频,完全从初学者的角度出发,绝不玩虚的,一切以实用、有用...

    独孤九贱(5)_ThinkPHP5视频教程

    ThinkPHP是国内最流行的中文PHP开发框架,也是您Web项目的最佳选择。《php.cn独孤九贱(5)-ThinkPHP5视频教程》课程以ThinkPHP5最新版本为例,从最基本的框架常识开始,将...

    独孤九贱(1)_HTML5视频教程

    《php.cn原创html5视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了HTML知识。 ...

    ThinkPHP5实战之[教学管理系统]

    本套教程,以一个真实的学校教学管理系统为案例,手把手教会您如何在一张白纸上,从零开始,一步一步的用ThinkPHP5框架快速开发出一个商业项目。

    PHP入门视频教程之一周学会PHP

    所有计算机语言的学习都要从基础开始,《PHP入门视频教程之一周学会PHP》不仅是PHP的基础部分更主要的是PHP语言的核心技术,是学习PHP必须掌握的内容,任何PHP项目的实现都离不开这部分的内容,通...

    作者信息

    kevin

    永远在学习的路上!

    相关教程

  • javascript初级视频教程 javascript初级视频教程
  • jquery 基础视频教程 jquery 基础视频教程
  • javascript三级联动视频教程 javascript三级联动视频教程
  • 独孤九贱(3)_JavaScript视频教程 独孤九贱(3)_JavaScript视频教程
  • 独孤九贱(6)_jQuery视频教程 独孤九贱(6)_jQuery视频教程
  • 热门教程