Vue RxJS Bindings
Vue-Rx is an official integration library that provides RxJS bindings for Vue.js components, enabling developers to incorporate reactive programming paradigms directly into their Vue applications. It offers a `subscriptions` option for components to declaratively bind observable streams to data properties, and a `v-stream` directive to easily convert DOM events or custom component events into RxJS Observables. The current stable version, 6.2.0, primarily supports RxJS v6 and Vue.js 2.x, with releases focused on compatibility updates and feature enhancements. Its key differentiator is its first-party status within the Vue ecosystem, offering a streamlined, boilerplate-reducing approach to managing reactive state and event handling using RxJS, and it ships with TypeScript typings for enhanced developer experience.
Common errors
-
TypeError: this.mySubject$.pipe is not a function
cause Attempting to use RxJS 6+ pipeable operators (like `pipe`, `map`, `scan`) with an older RxJS v5 `Subject` or without correctly importing the `pipe` method.fixEnsure `rxjs` v6 or later is installed (`npm install rxjs@^6.0.0`). If using `rxjs-compat`, verify all RxJS operators are imported from `rxjs/operators` and `pipe` is used correctly, or update to RxJS v6-style operator usage. -
TypeError: Cannot read properties of undefined (reading 'subscribe')
cause An observable defined within the `subscriptions` option might not be properly initialized, or `this.$observables.myStream` is accessed before the component is fully mounted and the streams are created.fixVerify that all observables are correctly returned from the `subscriptions` function. Access `this.$observables` within appropriate component lifecycle hooks (e.g., `mounted` or `created`) where streams are guaranteed to exist. -
TypeError: Cannot read properties of undefined (reading 'Subject') OR Subject is not a constructor
cause The RxJS `Subject` or `Observable` class is not correctly imported into the JavaScript module, or in a global script environment, `window.rxjs` is not available when `vue-rx.js` is loaded.fixFor module-based projects, ensure explicit imports: `import { Subject } from 'rxjs'`. For global script usage, load `rxjs.umd.js` (or similar UMD build) *before* `vue-rx.js` in your HTML.
Warnings
- breaking Vue-Rx v6+ is exclusively compatible with RxJS v6+. Attempts to use older RxJS versions (v5 or v4) directly will result in runtime errors. For projects requiring RxJS v5 compatibility with `vue-rx` v6, the `rxjs-compat` package must be installed and configured.
- breaking Prior to v4.0.0, errors originating from subscription streams were silently ignored by default. Since v4.0.0, all errors from RxJS subscription streams are now thrown, making the user responsible for catching and handling these potential errors to prevent application crashes.
- breaking TypeScript typings for `vue-rx` were updated in v5.0.0 to align with Vue core 2.5 typings. Projects using Vue 2.4 or older should remain on `vue-rx` 4.x to avoid TypeScript compatibility issues.
- gotcha When using the `v-stream` directive on a custom component, it relies on the component properly emitting custom events. Since v3.2.0, `v-stream` can create observables from custom events, but the child component must explicitly use `this.$emit('eventName', payload)` for `v-stream` to capture them.
Install
-
npm install vue-rx -
yarn add vue-rx -
pnpm add vue-rx
Imports
- VueRx
import { VueRx } from 'vue-rx'import VueRx from 'vue-rx'
- Vue.use
new Vue({ el: '#app', plugins: [VueRx] })Vue.use(VueRx)
- Subject
import { Subject } from 'vue-rx'import { Subject } from 'rxjs' - Observable
import { Observable } from 'vue-rx'import { Observable } from 'rxjs'
Quickstart
import Vue from 'vue';
import VueRx from 'vue-rx';
import { Subject } from 'rxjs';
import { map, startWith, scan } from 'rxjs/operators';
// Install VueRx as a Vue plugin
Vue.use(VueRx);
const app = new Vue({
el: '#app',
// Define reactive subscriptions for component data
subscriptions() {
// Create a Subject to stream click events
this.plus$ = new Subject<{ event: MouseEvent, data?: any }>();
return {
// Bind 'count' data property to an observable stream
count: this.plus$.pipe(
map(() => 1), // Each click adds 1
startWith(0), // Initial value is 0
scan((total, change) => total + change) // Accumulate total
)
};
},
template: `
<div>
<h2>Counter: {{ count }}</h2>
<!-- Use v-stream to pipe DOM click events into the 'plus$' Subject -->
<button v-stream:click="plus$">Increase Count</button>
<button @click="console.log('Resetting state often requires another stream or logic.')">Reset (concept)</button>
</div>
`
});