@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
JavaScript
;
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