@brinzl/observer
v0.1.0
Published
A lightweight, zero-dependency reactive object observation library.
Readme
observer.js
A tiny, zero-dependency reactive object observation library using ES6 Proxies. Observe changes to JavaScript objects and arrays with automatic nested tracking and microtask batching.
Why?
While exploring how Vue.js handle reactivity, and I came across observer pattern that powers their reactive systems. This library was built as a project to understand the fundamentals of reactive programming from first principles.
Inspired by libraries like object-observer, icaro, and micro-observer.
Features
- Observe deeply nested objects and arrays
- Automatic change batching via microtasks
- Path tracking for precise change locations
- Clean teardown with proxy revocation
- Zero dependencies, ~2KB minified
Usage
npm install @brinzl/observerBasic
import { Observer } from '@brinzl/observer';
// Create an observable object.
const obs = Observer.create({ name: 'Alice', age: 30 });
// Subscribe to changes.
const unsubscribe = Observer.observe(obs, (changes) => {
changes.forEach(change => {
console.log(change.type); // 'set' | 'delete'
console.log(change.path); // ['name']
console.log(change.oldValue); // 'Alice'
console.log(change.newValue); // 'Bob'
});
});
// Mutations trigger the callback.
obs.name = 'Bob';
obs.age = 31;
// Unsubscribe when done.
unsubscribe();Nested Objects
const obs = Observer.create({
user: {
profile: { name: 'Alice' }
}
});
Observer.observe(obs, (changes) => {
console.log(changes);
});
// Nested objects are automatically observed.
obs.user.profile.name = 'Bob';
// Change: { type: 'set', path: ['user', 'profile', 'name'], oldValue: 'Alice', newValue: 'Bob' }Arrays
const obs = Observer.create({ items: [1, 2, 3] });
Observer.observe(obs, (changes) => {
console.log(changes);
});
obs.items.push(4);
obs.items[0] = 10;Cleanup
const obs = Observer.create({ data: 'test' });
// Completely destroy the observable.
Observer.destroy(obs);
// Further access throws an error.
obs.data; // TypeError: Cannot perform 'get' on a proxy that has been revokedAccessing Internals
const { IS_OBSERVABLE, RAW, ROOT, PATH } = Observer.symbols;
const obs = Observer.create({ nested: { value: 1 } });
obs[IS_OBSERVABLE]; // true
obs[RAW]; // { nested: { value: 1 } } (original object)
obs.nested[PATH]; // ['nested']
obs.nested[ROOT] === obs; // trueAPI
| Method | Description |
|--------|-------------|
| Observer.create(target) | Creates an observable from a plain object or array |
| Observer.observe(observable, callback) | Subscribes to changes, returns an unsubscribe function |
| Observer.destroy(observable) | Revokes the proxy and clears all subscribers |
| Observer.symbols | Internal symbols for accessing raw target, path, and root |
Change Record
{
type: 'set' | 'delete',
path: string[], // e.g. ['user', 'profile', 'name']
oldValue: any,
newValue: any
}License
MIT
