UNPKG

@kalxjs/core

Version:

A modern JavaScript framework for building user interfaces with reactive state, composition API, and built-in performance optimizations

323 lines (281 loc) 7.95 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); // packages/core/src/reactivity/reactive.js // Current active effect let activeEffect = null; /** * Creates a reactive object * @param {Object} target - Object to make reactive * @returns {Proxy} Reactive object */ function reactive(target) { const handlers = { get(target, key, receiver) { const result = Reflect.get(target, key, receiver); track(target, key); return result; }, set(target, key, value, receiver) { const oldValue = target[key]; const result = Reflect.set(target, key, value, receiver); if (oldValue !== value) { trigger(target, key); } return result; } }; return new Proxy(target, handlers); } /** * Creates a reactive reference * @param {any} value - Initial value * @returns {Object} Reactive reference */ function ref(value) { const r = { _value: value, get value() { track(r, 'value'); return this._value; }, set value(newValue) { if (this._value !== newValue) { this._value = newValue; trigger(r, 'value'); } } }; return r; } /** * Creates a computed property * @param {Function} getter - Getter function * @returns {Object} Computed property */ function computed(getter) { let value; let dirty = true; const runner = effect(getter, { lazy: true, scheduler: () => { if (!dirty) { dirty = true; trigger(computedRef, 'value'); } } }); const computedRef = { get value() { if (dirty) { value = runner(); dirty = false; } track(computedRef, 'value'); return value; } }; return computedRef; } /** * Creates a reactive effect * @param {Function} fn - Effect function * @param {Object} options - Effect options * @returns {Function} Effect runner */ function effect(fn, options = {}) { const effect = createReactiveEffect(fn, options); if (!options.lazy) { effect(); } return effect; } // Internal helpers const targetMap = new WeakMap(); function track(target, key) { // Only track if we have an active effect and a valid target if (activeEffect && target) { let depsMap = targetMap.get(target); if (!depsMap) { targetMap.set(target, (depsMap = new Map())); } let dep = depsMap.get(key); if (!dep) { depsMap.set(key, (dep = new Set())); } dep.add(activeEffect); } } function trigger(target, key) { // Check if target is valid if (!target) return; const depsMap = targetMap.get(target); if (!depsMap) return; const effects = depsMap.get(key); if (effects) { // Create a new set to avoid infinite loops if an effect triggers itself const effectsToRun = new Set(effects); effectsToRun.forEach(effect => { if (effect.scheduler) { effect.scheduler(); } else { effect(); } }); } } function createReactiveEffect(fn, options) { const effect = function reactiveEffect() { if (!effect.active) return fn(); if (!effectStack.includes(effect)) { cleanup(effect); try { effectStack.push(effect); activeEffect = effect; return fn(); } finally { effectStack.pop(); // Set activeEffect to the previous effect in the stack or null if empty activeEffect = effectStack.length > 0 ? effectStack[effectStack.length - 1] : null; } } }; effect.active = true; effect.deps = []; effect.options = options; return effect; } const effectStack = []; function cleanup(effect) { const { deps } = effect; if (deps.length) { for (let i = 0; i < deps.length; i++) { deps[i].delete(effect); } deps.length = 0; } } // @kalxjs/core - Component instance management for Composition API // Current component instance let currentInstance = null; /** * Sets the current component instance * @param {Object} instance - Component instance */ function setCurrentInstance(instance) { currentInstance = instance; } /** * Gets the current component instance * @returns {Object} Current component instance */ function getCurrentInstance() { if (!currentInstance) { console.warn('getCurrentInstance() can only be used inside setup()'); return {}; } return currentInstance; } // @kalxjs/core - Composition API /** * Creates a reactive object that can be used in the setup function * @param {Object} target - Object to make reactive * @returns {Proxy} Reactive object */ function useReactive(target) { return reactive(target); } /** * Creates a reactive reference that can be used in the setup function * @param {any} value - Initial value * @returns {Object} Reactive reference */ function useRef(value) { return ref(value); } /** * Creates a computed property that can be used in the setup function * @param {Function} getter - Getter function * @returns {Object} Computed property */ function useComputed(getter) { return computed(getter); } /** * Watches for changes in a reactive source and runs a callback * @param {Object|Function} source - Reactive object or getter function * @param {Function} callback - Callback function * @param {Object} options - Watch options * @returns {Function} Function to stop watching */ function watch(source, callback, options = {}) { const { immediate = false } = options; // Handle ref or reactive objects const getter = typeof source === 'function' ? source : () => { // Handle ref if (source && 'value' in source) { return source.value; } // Handle reactive object return source; }; let oldValue; const runner = effect(() => getter(), { lazy: true, scheduler: () => { const newValue = runner(); callback(newValue, oldValue); oldValue = newValue; } }); if (immediate) { oldValue = runner(); callback(oldValue, undefined); } else { oldValue = runner(); } return () => { // Cleanup effect runner.active = false; }; } /** * Runs a callback once when the component is mounted * @param {Function} callback - Callback function */ function onMounted(callback) { getCurrentInstance().mounted.push(callback); } /** * Runs a callback before the component is unmounted * @param {Function} callback - Callback function */ function onUnmounted(callback) { getCurrentInstance().unmounted.push(callback); } /** * Runs a callback before the component is updated * @param {Function} callback - Callback function */ function onBeforeUpdate(callback) { getCurrentInstance().beforeUpdate.push(callback); } /** * Runs a callback after the component is updated * @param {Function} callback - Callback function */ function onUpdated(callback) { getCurrentInstance().updated.push(callback); } exports.getCurrentInstance = getCurrentInstance; exports.onBeforeUpdate = onBeforeUpdate; exports.onMounted = onMounted; exports.onUnmounted = onUnmounted; exports.onUpdated = onUpdated; exports.setCurrentInstance = setCurrentInstance; exports.useComputed = useComputed; exports.useReactive = useReactive; exports.useRef = useRef; exports.watch = watch; //# sourceMappingURL=composition.cjs.js.map