Provides Vue 3 Composition API compatibility for Vue 2 applications with reactive state management and lifecycle hooks.
—
Effect scopes, app instances, and other advanced composition features for complex state management and application architecture patterns.
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();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 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 },
}));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 {};
},
});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