CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vue--composition-api

Provides Vue 3 Composition API compatibility for Vue 2 applications with reactive state management and lifecycle hooks.

Pending
Overview
Eval results
Files

advanced-features.mddocs/

Advanced Features

Effect scopes, app instances, and other advanced composition features for complex state management and application architecture patterns.

Capabilities

Effect Scopes

Advanced effect management system for organizing and cleaning up reactive effects in groups.

/**
 * Creates a new effect scope for organizing effects
 * @param detached - Whether the scope runs independently of parent scope
 * @returns Effect scope instance
 */
function effectScope(detached?: boolean): EffectScope;

/**
 * Gets the currently active effect scope
 * @returns Current effect scope or undefined
 */
function getCurrentScope(): EffectScope | undefined;

/**
 * Registers a disposal callback for the current effect scope
 * @param fn - Cleanup function to run when scope is disposed
 */
function onScopeDispose(fn: () => void): void;

class EffectScope {
  /**
   * Runs a function within this effect scope
   * @param fn - Function to run in scope
   * @returns Result of the function
   */
  run<T>(fn: () => T): T | undefined;
  
  /**
   * Stops and cleans up all effects in this scope
   */
  stop(): void;
}

Usage Examples:

import { effectScope, watchEffect, watch, ref, onScopeDispose } from "@vue/composition-api";

// Basic effect scope usage
const scope = effectScope();

scope.run(() => {
  const count = ref(0);
  
  // This watcher will be collected by the scope
  watchEffect(() => {
    console.log("Count:", count.value);
  });
  
  // Cleanup when scope is disposed
  onScopeDispose(() => {
    console.log("Scope disposed");
  });
});

// Later, stop all effects in the scope
scope.stop();

// Detached scope (won't be stopped when parent stops)
const detachedScope = effectScope(true);

detachedScope.run(() => {
  // Effects here won't be cleaned up by parent scope
  const timer = setInterval(() => {
    console.log("Timer tick");
  }, 1000);
  
  onScopeDispose(() => {
    clearInterval(timer);
  });
});

Advanced Scope Patterns:

import { effectScope, ref, watch, onScopeDispose } from "@vue/composition-api";

// Scope-based service management
class DataService {
  private scope = effectScope();
  private data = ref([]);
  private loading = ref(false);
  
  constructor(private apiUrl: string) {
    this.scope.run(() => {
      // Set up reactive data fetching
      watch(
        () => this.apiUrl,
        async (url) => {
          this.loading.value = true;
          try {
            const response = await fetch(url);
            this.data.value = await response.json();
          } finally {
            this.loading.value = false;
          }
        },
        { immediate: true }
      );
      
      // Cleanup on dispose
      onScopeDispose(() => {
        console.log("DataService disposed");
      });
    });
  }
  
  getData() {
    return readonly(this.data);
  }
  
  isLoading() {
    return readonly(this.loading);
  }
  
  dispose() {
    this.scope.stop();
  }
}

// Usage
const service = new DataService("/api/users");
const users = service.getData();
const loading = service.isLoading();

// Later cleanup
service.dispose();

App Instance (Vue 3 Compatibility)

Vue 3-like app instance creation for better migration compatibility.

/**
 * Creates a Vue 3-like app instance for compatibility
 * @param rootComponent - Root component definition
 * @param rootProps - Props for root component
 * @returns App instance with Vue 3-like API
 */
function createApp(rootComponent: any, rootProps?: any): App;

interface App<T = any> {
  /**
   * Mounts the app to a DOM element
   * @param rootContainer - DOM element or selector
   * @returns Component instance
   */
  mount(rootContainer: Element | string): T;
  
  /**
   * Unmounts the app
   */
  unmount(): void;
  
  /**
   * Provides a value for the entire app
   * @param key - Injection key
   * @param value - Value to provide
   * @returns App instance for chaining
   */
  provide<T>(key: InjectionKey<T> | string, value: T): this;
  
  /**
   * Registers a global component
   * @param name - Component name
   * @param component - Component definition
   * @returns App instance for chaining
   */
  component(name: string, component: Component): this;
  
  /**
   * Registers a global directive
   * @param name - Directive name
   * @param directive - Directive definition
   * @returns App instance for chaining
   */
  directive(name: string, directive: Directive): this;
  
  /**
   * Installs a plugin
   * @param plugin - Plugin to install
   * @param options - Plugin options
   * @returns App instance for chaining
   */
  use(plugin: Plugin, ...options: any[]): this;
  
  /**
   * Sets a global property
   * @param key - Property key
   * @param value - Property value
   * @returns App instance for chaining
   */
  config: {
    globalProperties: Record<string, any>;
  };
}

Usage Examples:

import { createApp, defineComponent, ref } from "@vue/composition-api";

const RootComponent = defineComponent({
  setup() {
    const message = ref("Hello Vue 3 App!");
    
    return {
      message,
    };
  },
  template: `<h1>{{ message }}</h1>`,
});

// Create and configure app
const app = createApp(RootComponent);

// Global provide
app.provide("appName", "My Vue App");

// Global component
app.component("GlobalButton", {
  template: `<button><slot /></button>`,
});

// Global directive
app.directive("focus", {
  inserted(el) {
    el.focus();
  },
});

// Install plugin
app.use(SomePlugin, { option: "value" });

// Mount app
const vm = app.mount("#app");

// Later unmount
app.unmount();

Advanced Reactive Utilities

Advanced reactive programming utilities for complex state management scenarios.

/**
 * Gets the current scope's Vue instance for internal operations
 * @returns Vue instance or null
 */
function getCurrentScopeVM(): ComponentInstance | null;

/**
 * Records effect scope for tracking (internal utility)
 * @param effect - Effect to record
 * @param scope - Target scope
 */
function recordEffectScope(effect: ReactiveEffect, scope?: EffectScope): void;

Advanced Reactive Patterns:

import { effectScope, ref, computed, watch, onScopeDispose } from "@vue/composition-api";

// Advanced store pattern with scoped effects
class ReactiveStore<T> {
  private scope = effectScope();
  private state = ref<T>({} as T);
  private computedCache = new Map();
  private watchers = new Set<() => void>();
  
  constructor(initialState: T) {
    this.scope.run(() => {
      this.state.value = initialState;
      
      onScopeDispose(() => {
        this.watchers.forEach(stop => stop());
        this.computedCache.clear();
      });
    });
  }
  
  getState(): Readonly<Ref<T>> {
    return readonly(this.state);
  }
  
  setState(updater: (state: T) => T): void {
    this.state.value = updater(this.state.value);
  }
  
  select<R>(selector: (state: T) => R): ComputedRef<R> {
    const key = selector.toString();
    
    if (!this.computedCache.has(key)) {
      const computed = this.scope.run(() => 
        computed(() => selector(this.state.value))
      );
      this.computedCache.set(key, computed);
    }
    
    return this.computedCache.get(key);
  }
  
  subscribe(callback: (state: T) => void): () => void {
    const stop = this.scope.run(() =>
      watch(this.state, callback, { deep: true })
    );
    
    if (stop) {
      this.watchers.add(stop);
      return () => {
        stop();
        this.watchers.delete(stop);
      };
    }
    
    return () => {};
  }
  
  destroy(): void {
    this.scope.stop();
  }
}

// Usage
interface AppState {
  user: { name: string; id: number } | null;
  theme: "light" | "dark";
  notifications: Array<{ id: string; message: string }>;
}

const store = new ReactiveStore<AppState>({
  user: null,
  theme: "light",
  notifications: [],
});

// Select derived state
const isAuthenticated = store.select(state => !!state.user);
const notificationCount = store.select(state => state.notifications.length);

// Subscribe to changes
const unsubscribe = store.subscribe(state => {
  console.log("State changed:", state);
});

// Update state
store.setState(state => ({
  ...state,
  user: { name: "Alice", id: 1 },
}));

Global Property Registration

Utilities for registering global properties and methods.

/**
 * Development warning utility (internal)
 * @param message - Warning message
 */
function warn(message: string): void;

Global Registration Patterns:

import Vue from "vue";
import { ref, reactive } from "@vue/composition-api";

// Extend Vue with global properties
declare module "vue/types/vue" {
  interface Vue {
    $api: ApiService;
    $eventBus: EventBus;
  }
}

// Global API service
class ApiService {
  private baseUrl: string;
  
  constructor(baseUrl: string) {
    this.baseUrl = baseUrl;
  }
  
  async get<T>(endpoint: string): Promise<T> {
    const response = await fetch(`${this.baseUrl}${endpoint}`);
    return response.json();
  }
}

// Global event bus
class EventBus {
  private events = reactive<Record<string, Array<Function>>>({});
  
  on(event: string, callback: Function): void {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
  
  emit(event: string, ...args: any[]): void {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(...args));
    }
  }
  
  off(event: string, callback?: Function): void {
    if (!this.events[event]) return;
    
    if (callback) {
      const index = this.events[event].indexOf(callback);
      if (index > -1) {
        this.events[event].splice(index, 1);
      }
    } else {
      this.events[event] = [];
    }
  }
}

// Install globally
Vue.prototype.$api = new ApiService("/api");
Vue.prototype.$eventBus = new EventBus();

// Usage in composition API
export default defineComponent({
  setup() {
    const instance = getCurrentInstance();
    const api = instance?.proxy?.$api;
    const eventBus = instance?.proxy?.$eventBus;
    
    onMounted(() => {
      eventBus?.on("refresh", () => {
        // Handle refresh event
      });
    });
    
    onBeforeUnmount(() => {
      eventBus?.off("refresh");
    });
    
    return {};
  },
});

Types

interface EffectScope {
  active: boolean;
  effects: ReactiveEffect[];
  cleanups: (() => void)[];
  parent: EffectScope | undefined;
  
  run<T>(fn: () => T): T | undefined;
  stop(): void;
}

interface App<T = any> {
  version: string;
  config: AppConfig;
  
  mount(rootContainer: Element | string): T;
  unmount(): void;
  provide<T>(key: InjectionKey<T> | string, value: T): this;
  component(name: string, component?: Component): this | Component;
  directive(name: string, directive?: Directive): this | Directive;
  use(plugin: Plugin, ...options: any[]): this;
}

interface AppConfig {
  errorHandler?: (err: Error, instance: ComponentPublicInstance | null, info: string) => void;
  warnHandler?: (msg: string, instance: ComponentPublicInstance | null, trace: string) => void;
  globalProperties: Record<string, any>;
  isCustomElement?: (tag: string) => boolean;
}

interface ReactiveEffect<T = any> {
  (): T;
  _isEffect: true;
  id: number;
  active: boolean;
  raw: () => T;
  deps: Array<Dep>;
  options: ReactiveEffectOptions;
  allowRecurse: boolean;
}

interface ReactiveEffectOptions {
  lazy?: boolean;
  scheduler?: (job: ReactiveEffect) => void;
  onTrack?: (event: DebuggerEvent) => void;
  onTrigger?: (event: DebuggerEvent) => void;
  onStop?: () => void;
  allowRecurse?: boolean;
}

type Dep = Set<ReactiveEffect> & {
  cleanup: () => void;
  computed?: ComputedRef<any>;
};

interface DebuggerEvent {
  effect: ReactiveEffect;
  target: object;
  type: TrackOpTypes | TriggerOpTypes;
  key: any;
  newValue?: any;
  oldValue?: any;
  oldTarget?: Map<any, any> | Set<any>;
}

Install with Tessl CLI

npx tessl i tessl/npm-vue--composition-api

docs

advanced-features.md

component-utilities.md

computed.md

dependency-injection.md

index.md

lifecycle.md

reactive-state.md

types.md

watchers.md

tile.json