Installations
npm install cellx
Developer Guide
Typescript
No
Module System
CommonJS
Node Version
22.5.1
NPM Version
10.8.2
Releases
Unable to fetch releases
Contributors
Unable to fetch Contributors
Languages
TypeScript (69.66%)
HTML (23.05%)
JavaScript (7.29%)
Love this project? Help keep it running — sponsor us today! 🚀
Developer
Riim
Download Statistics
Total Downloads
269,188
Last Day
1
Last Week
1
Last Month
357
Last Year
8,918
GitHub Statistics
463 Stars
357 Commits
15 Forks
16 Watching
1 Branches
4 Contributors
Bundle Size
10.50 kB
Minified
3.37 kB
Minified + Gzipped
Package Meta Information
Latest Version
2.0.1
Package Id
cellx@2.0.1
Unpacked Size
88.04 kB
Size
16.06 kB
File Count
21
NPM Version
10.8.2
Node Version
22.5.1
Publised On
08 Aug 2024
Total Downloads
Cumulative downloads
Total Downloads
269,188
Last day
0%
1
Compared to previous day
Last week
-99.2%
1
Compared to previous week
Last month
-61.2%
357
Compared to previous month
Last year
-39.6%
8,918
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Сверхбыстрая реализация реактивности для javascript.
Установка
npm i -S cellx
Пример использования
1let firstName = cellx('Матроскин'); 2let lastName = cellx('Кот'); 3 4let fullName = cellx(() => firstName.value + ' ' + lastName.value) 5 6fullName.subscribe(() => { 7 console.log('fullName:', fullName.value); 8}); 9 10console.log(fullName.value); 11// => 'Матроскин Кот' 12 13firstName.value = 'Шарик'; 14lastName.value = 'Пёс'; 15// => 'fullName: Шарик Пёс'
Несмотря на то, что изменились две зависимости ячейки fullName
, обработчик её изменения сработал только один раз.
Важной особенностью cellx-а является то, что он старается максимально избавиться как от лишних вызовов обработчиков
изменений, так и от лишних вызовов расчётных формул зависимых ячеек. В сочетании с ещё некоторыми оптимизациями это
приводит к идеальной скорости расчёта сложнейших сеток зависимостей.
Больше об этом можно узнать в статье Big State Managers Benchmark.
Также вам может быть интересна статья Разбираемся в сортах реактивности.
Тест производительности
В одном из тестов, который используется для замера производительности, генерируется сетка из множества "слоёв", каждый из которых состоит из 4-x ячеек. Ячейки вычисляются из ячеек предыдущего слоя (кроме самого первого, он содержит исходные значения) по формуле A2=B1, B2=A1-C1, C2=B1+D1, D2=C1. Далее запоминается начальное время, меняются значения всех ячеек первого слоя и замеряется время, через которое все ячейки последнего слоя обновятся. Результаты теста (в милисекундах) с разным числом слоёв:
Library ↓ \ Number of computed layers → | 10 | 20 | 30 | 50 | 100 | 1000 | 5000 |
---|---|---|---|---|---|---|---|
cellx | <~1 | <~1 | <~1 | <~1 | <~1 | 4 | 20 |
VanillaJS (naive) | <~1 | 15 | 1750 | >300000 | >300000 | >300000 | >300000 |
Knockout | 10 | 750, increases in subsequent runs | 67250, increases in subsequent runs | >300000 | >300000 | >300000 | >300000 |
$jin.atom | 2 | 3 | 3 | 4 | 6 | 40 | 230 |
$mol_atom | <~1 | <~1 | <~1 | 1 | 2 | 20 | RangeError: Maximum call stack size exceeded |
Kefir.js | 25 | 2500 | >300000 | >300000 | >300000 | >300000 | >300000 |
MobX | <~1 | <~1 | <~1 | 2 | 3 | 40 | RangeError: Maximum call stack size exceeded |
Исходники теста можно найти в папке perf. Плотность связей в реальных приложениях обычно ниже чем в данном тесте, то есть если в тесте определённая задержка появляется на 100 вычисляемых ячейках (25 слоёв), то в реальном приложении подобная задержка будет либо на большем числе ячеек, либо в формулах ячеек будут какие-то сложные расчёты (например, вычисление одного массива из другого).
Использование
Функциональный стиль:
1let num = cellx(1); 2let plusOne = cellx(() => num.value + 1); 3 4console.log(plusOne.value); 5// => 2
ООП стиль:
1import { cellx, define } from 'cellx'; 2 3class User { 4 name: string; 5 nameInitial: string; 6 7 constructor(name: string) { 8 define(this, { 9 name, 10 nameInitial: cellx(() => this.name.charAt(0).toUpperCase()) 11 }); 12 } 13} 14 15let user = new User('Матроскин'); 16 17console.log(user.nameInitial); 18// => 'M'
ООП стиль с декораторами:
1import { Computed, Observable } from 'cellx-decorators'; 2 3class User { 4 @Observable name: string; 5 6 @Computed get nameInitial() { 7 return this.name.charAt(0).toUpperCase(); 8 } 9 10 constructor(name: string) { 11 this.name = name; 12 } 13} 14 15let user = new User('Матроскин'); 16 17console.log(user.nameInitial); 18// => 'M'
Опции
put
Может использоваться для обработки значения при записи и перенаправления записи:
1function User() { 2 this.firstName = cellx(''); 3 this.lastName = cellx(''); 4 5 this.fullName = cellx( 6 () => (this.firstName.value + ' ' + this.lastName.value).trim(), 7 { 8 put: (name) => { 9 name = name.split(' '); 10 11 this.firstName.value = name[0]; 12 this.lastName.value = name[1]; 13 } 14 } 15 ); 16} 17 18let user = new User(); 19 20user.fullName.value = 'Матроскин Кот'; 21 22console.log(user.firstName.value); 23// => 'Матроскин' 24console.log(user.lastName.value); 25// => 'Кот'
validate
Валидирует значение при записи и вычислении.
Валидация при записи в ячейку:
1let num = cellx(5, { 2 validate: (value) => { 3 if (typeof value != 'number') { 4 throw TypeError('Oops!'); 5 } 6 } 7}); 8 9try { 10 num('Йа строчка'); 11} catch (err) { 12 console.log(err.message); 13 // => 'Oops!' 14} 15 16console.log(num.value); 17// => 5
Валидация при вычислении ячейки:
1let someValue = cellx(5); 2 3let num = cellx(() => someValue.value, { 4 validate: (value) => { 5 if (typeof value != 'number') { 6 throw TypeError('Oops!'); 7 } 8 } 9}); 10 11num.subscribe((err) => { 12 console.log(err.message); 13}); 14 15someValue.value = 'Я строчка'; 16// => 'Oops!' 17 18console.log(someValue.value); 19// => 'Я строчка'
Методы
onChange
Добавляет обработчик изменения:
1let num = cellx(5); 2 3num.onChange((evt) => { 4 console.log(evt); 5}); 6 7num.value = 10; 8// => { prevValue: 5, value: 10 }
offChange
Снимает ранее добавленный обработчик изменения.
onError
Добавляет обработчик ошибки:
1let someValue = cellx(1); 2 3let num = cellx(() => someValue.value, { 4 validate: (v) => { 5 if (v > 1) { 6 throw RangeError('Oops!'); 7 } 8 } 9}); 10 11num.onError(evt => { 12 console.log(evt.error.message); 13}); 14 15someValue.value = 2; 16// => 'Oops!'
offError
Снимает ранее добавленный обработчик ошибки.
subscribe
Подписывает на события change
и error
. В обработчик первым аргументом приходит объект ошибки, вторым — событие.
1fullName.subscribe((err, evt) => { 2 if (err) { 3 // 4 } else { 5 // 6 } 7});
unsubscribe
Отписывает от событий change
и error
.
Динамическая актуализация зависимостей
Формула вычисляемой ячейки может быть написана так, что набор зависимостей может со временем меняться. Например:
1let user = { 2 firstName: cellx(''), 3 lastName: cellx(''), 4 5 name: cellx(() => user.firstName.value || user.lastName.value) 6};
Здесь пока firstName
является пустой строкой, ячейка name
подписана и на firstName
и на lastName
,
так как изменение любого из них приведёт к изменению её значения. Если же задать firstName
-у какую-то не пустую
строку, то, при перевычислении значения name
, до чтения lastName
в формуле просто не дойдёт,
то есть значение ячейки name
с этого момента уже никак не зависит от lastName
.
В таких случаях ячейки автоматически отписываются от незначимых для них зависимостей и не перевычисляются
при их изменении. В дальнейшем, если firstName
снова станет пустой строкой, ячейка name
вновь подпишется
на lastName
.
Синхронизация значения с синхронным хранилищем
1let foo = cellx(() => localStorage.foo || 'foo', { 2 put: ({ push }, value) => { 3 localStorage.foo = value; 4 5 push(value); 6 } 7}); 8 9let foobar = cellx(() => foo.value + 'bar'); 10 11console.log(foobar.value); // => 'foobar' 12console.log(localStorage.foo); // => undefined 13foo('FOO'); 14console.log(foobar.value); // => 'FOObar' 15console.log(localStorage.foo); // => 'FOO'
Синхронизация значения с асинхронным хранилищем
1let request = (() => { 2 let value = 1; 3 4 return { 5 get: url => new Promise((resolve, reject) => { 6 setTimeout(() => { 7 resolve({ 8 ok: true, 9 value 10 }); 11 }, 1000); 12 }), 13 14 put: (url, params) => new Promise((resolve, reject) => { 15 setTimeout(() => { 16 value = params.value; 17 18 resolve({ ok: true }); 19 }, 1000); 20 }) 21 }; 22})(); 23 24let foo = cellx(({ push, fail }, next = 0) => { 25 request.get('http://...').then((res) => { 26 if (res.ok) { 27 push(res.value); 28 } else { 29 fail(res.error); 30 } 31 }); 32 33 return next; 34}, { 35 put: (value, cell, next) => { 36 request.put('http://...', { value: value }).then((res) => { 37 if (res.ok) { 38 cell.push(value); 39 } else { 40 cell.fail(res.error); 41 } 42 }); 43 } 44}); 45 46foo.subscribe(() => { 47 console.log('New foo value: ' + foo.value); 48 49 foo.value = 5; 50}); 51 52console.log(foo.value); 53// => 0 54 55// => 'New foo value: 1' 56// => 'New foo value: 5'
![Empty State](/_next/static/media/empty.e5fae2e5.png)
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
0 existing vulnerabilities detected
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 0/30 approved changesets -- score normalized to 0
Reason
no SAST tool detected
Details
- Warn: no pull requests merged into dev branch
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
license file not detected
Details
- Warn: project does not have a license file
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Score
2.6
/10
Last Scanned on 2025-02-03
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to cellx
cellx-decorators
cellx-decorators ================
cellx-indexed-collections
Индексируемые коллекции для [cellx](https://github.com/Riim/cellx).
cellx-collections
If you record to the cell an instance of class which inherits of `cellx.EventEmitter`, then the cell will subscribe to its `change` event and will claim it as own:
cellx-lite
<p align="right"> <a href="https://github.com/Riim/cellx/blob/master/README.ru.md">Этот документ на русском</a> </p>