Vue Meteor Tracker

2.0.0 · active · verified Sun Apr 19

Vue Meteor Tracker is an integration library that seamlessly brings Meteor's powerful reactive data layer, based on `Tracker` computations, into Vue 3 and Vue 2.7+ applications. The current stable version is 2.0.0, which focuses on providing a more streamlined API compatible with modern Vue setups, including Vite. It allows developers to declare Meteor subscriptions and reactive data queries directly within Vue components' options API, ensuring that components automatically re-render when underlying Meteor collections or reactive sources change. A key differentiator is its automatic lifecycle management for Meteor subscriptions, stopping them when components are unmounted. This library simplifies building real-time UIs with Meteor and Vue by abstracting away manual Tracker computations and subscription management, offering a robust solution for reactive UIs in the Meteor ecosystem.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to install `vue-meteor-tracker`, integrate it into a Vue application using `app.use(VueMeteor)`, and define reactive Meteor subscriptions and data queries within a Vue component's `meteor` option, automatically reacting to changes.

import { createApp } from 'vue';
import App from './App.vue';
import { VueMeteor } from 'vue-meteor-tracker';

// Mock Meteor for standalone demonstration if not in a Meteor environment.
// In a real Meteor app, `Meteor` and `Mongo.Collection` are globally available.
declare global {
  namespace Meteor {
    const subscribe: (name: string, ...args: any[]) => { ready: () => boolean };
    const Collection: new (name: string) => { find: (query?: any) => { fetch: () => any[] }, findOne: (query?: any) => any };
  }
}

if (typeof (globalThis as any).Meteor === 'undefined') {
  (globalThis as any).Meteor = {
    subscribe: (name: string, ...args: any[]) => {
      console.log(`Mock Meteor: Subscribing to ${name} with params: ${JSON.stringify(args)}`);
      return { ready: () => true }; // Always ready for mock
    },
    Collection: function(name: string) {
      console.log(`Mock Meteor: Creating Collection ${name}`);
      const data: any[] = [];
      if (name === 'Notes') {
        data.push({ _id: 'note1', text: 'First Note' });
        data.push({ _id: 'note2', text: 'Second Note' });
      }
      if (name === 'Threads') {
        data.push({ _id: 'thread1', title: 'Main Thread' });
      }
      return {
        find: (query?: any) => ({ fetch: () => data }),
        findOne: (query?: any) => data.find(item => Object.keys(query).every(key => item[key] === query[key])) || null
      };
    } as any
  };
}

// Example Vue component (App.vue)
import { defineComponent, ref } from 'vue';

const Notes = (globalThis as any).Meteor.Collection('Notes');
const Threads = (globalThis as any).Meteor.Collection('Threads');

const AppVueComponent = defineComponent({
  name: 'MeteorTrackerExample',
  data() {
    return {
      selectedThreadId: 'thread1',
      myReactiveParam: 'paramValue',
    };
  },
  meteor: {
    $subscribe: {
      'notesSubscription': () => [],
      'threadsSubscription': function() {
        return [this.selectedThreadId];
      },
    },
    notes: function() {
      return Notes.find({}).fetch();
    },
    selectedThread: function() {
      return Threads.findOne({ _id: this.selectedThreadId });
    },
  },
  mounted() {
    console.log('Component mounted. Initial notes:', this.notes);
    this.$subscribe('anotherSub', ['arg1', this.myReactiveParam]);
  },
  template: `
    <div>
      <h1>Vue Meteor Tracker Example</h1>
      <p v-if="!$subReady.notesSubscription">Loading notes...</p>
      <div v-else>
        <h2>Notes:</h2>
        <ul>
          <li v-for="note in notes" :key="note._id">{{ note.text }}</li>
        </ul>
      </div>

      <p v-if="!$subReady.threadsSubscription">Loading thread...</p>
      <div v-else>
        <h2>Selected Thread:</h2>
        <p v-if="selectedThread">{{ selectedThread.title }} (ID: {{ selectedThread._id }})</p>
        <p v-else>No thread selected or found.</p>
      </div>

      <button @click="selectedThreadId = 'newThreadId'">Change Thread ID (mock)</button>
      <p>Subscription 'anotherSub' ready: {{ $subReady.anotherSub }}</p>
    </div>
  `,
});

const app = createApp(AppVueComponent);
app.use(VueMeteor);
app.mount('#app');

view raw JSON →