Skip to content

Commit

Permalink
Merge pull request #25 from PxyUp/micro
Browse files Browse the repository at this point in the history
feat(memory): fix memory issue and rewrite
  • Loading branch information
PxyUp authored Apr 27, 2019
2 parents d4b6832 + 145f695 commit 60962df
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 117 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The library allows you to create quick and responsive interfaces using only JS /

## Usage
```sh
yarn add revact@0.0.3-beta
yarn add revact@0.0.4-beta
```

```typescript
Expand Down
2 changes: 1 addition & 1 deletion demo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ bootstrap('#router', createExampleRouter);

bootstrap("#composite", createComposite)

bootstrap("#radi", createTriaApp)
bootstrap("#radi", createTriaApp)
2 changes: 1 addition & 1 deletion docs/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "revact",
"version": "0.0.3-beta",
"version": "0.0.4-beta",
"main": "revact.umd.js",
"module": "revact.es5.js",
"es2015": "revact.es2015.js",
Expand Down
10 changes: 8 additions & 2 deletions src/generators/component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { asyncCall, removeAllListenersComponent } from '../misc/misc';

import { ClassConstructor } from '../interfaces/component';
import { Observer } from '../observer/observer';
import { RevactNode } from '../interfaces/node';
import { removeAllListenersComponent } from '../misc/misc';

export function createComponent<T extends ClassConstructor<any>>(
classProvider: T,
Expand All @@ -27,9 +28,14 @@ export class Component {

destroy(...args: any) {
const force = args[0];
Object.keys(this.rValues).forEach(key => {
this.rValues[key].destroy(...args);
});
this.onDestroy();
if (force === true) {
removeAllListenersComponent(this.template);
delete this.rValues;
delete this.template;
}
this.onDestroy();
}
}
167 changes: 93 additions & 74 deletions src/generators/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,6 @@ import { RevactNode } from '../interfaces/node';

const instance = 'instance';

const listSharedFn = {
textValue: setTextContent,
} as { [key: string]: (node: HTMLElement, value: any) => void };

const listNodeFn = {
styles: setNodeStyle,
classList: setClassList,
props: setProps,
attrs: setNodeAttrs,
} as { [key: string]: (node: HTMLElement, value: any) => void };

export function nodeWrapper(...args: any[]): RevactNode {
return {
tag: 'fragment',
Expand All @@ -51,47 +40,67 @@ export function generateNode(node: RevactNode): HTMLElement | DocumentFragment |

node.domNode = rootNode;

const listSharedObservers = {
textValue: null,
} as { [key: string]: null | Observer<any> };

const listNodeObservers = {
styles: null,
classList: null,
props: null,
attrs: null,
} as { [key: string]: null | Observer<any> };
// That can be apply for all type
Object.keys(listSharedObservers).forEach((key: string) => {
if ((node as any)[key]) {
if ((node as any)[key] instanceof Observer) {
const obs = (node as any)[key] as Observer<any>;
listSharedFn[key](rootNode as HTMLElement, obs.value);
obs.addSubscriber(newValue => {
listSharedFn[key](rootNode as HTMLElement, newValue);
});
listSharedObservers[key] = obs;
return;
}
listSharedFn[key](rootNode as HTMLElement, (node as any)[key]);
let textNodeSubscriber: (e: any) => void;
let styleSubscriber: (e: any) => void;
let classListSubscriber: (e: any) => void;
let propsSubscriber: (e: any) => void;
let attrsSubscriber: (e: any) => void;

if (node.textValue) {
if (node.textValue instanceof Observer) {
textNodeSubscriber = newTextValue => {
setTextContent(rootNode as HTMLElement, newTextValue);
};
node.textValue.addSubscriber(textNodeSubscriber);
} else {
setTextContent(rootNode as HTMLElement, node.textValue);
}
});
}

if (node.tag !== 'textNode') {
Object.keys(listNodeObservers).forEach((key: string) => {
if ((node as any)[key]) {
if ((node as any)[key] instanceof Observer) {
const obs = (node as any)[key] as Observer<any>;
listNodeFn[key](rootNode as HTMLElement, obs.value);
obs.addSubscriber(newValue => {
listNodeFn[key](rootNode as HTMLElement, newValue);
});
listNodeObservers[key] = obs;
return;
}
listNodeFn[key](rootNode as HTMLElement, (node as any)[key]);
if (node.classList) {
if (node.classList instanceof Observer) {
classListSubscriber = newClassListValue => {
setClassList(rootNode as HTMLElement, newClassListValue);
};
node.classList.addSubscriber(classListSubscriber);
} else {
setClassList(rootNode as HTMLElement, node.classList);
}
});
}

if (node.props) {
if (node.props instanceof Observer) {
propsSubscriber = newPropsValue => {
setProps(rootNode as HTMLElement, newPropsValue);
};
node.props.addSubscriber(propsSubscriber);
} else {
setProps(rootNode as HTMLElement, node.props);
}
}

if (node.styles) {
if (node.styles instanceof Observer) {
styleSubscriber = newStylesValue => {
setNodeStyle(rootNode as HTMLElement, newStylesValue);
};
node.styles.addSubscriber(styleSubscriber);
} else {
setNodeStyle(rootNode as HTMLElement, node.styles);
}
}

if (node.attrs) {
if (node.attrs instanceof Observer) {
attrsSubscriber = newAttrsValue => {
setNodeAttrs(rootNode as HTMLElement, newAttrsValue);
};
node.attrs.addSubscriber(attrsSubscriber);
} else {
setNodeAttrs(rootNode as HTMLElement, node.attrs);
}
}

if (node.children) {
const tempArr = [] as Array<HTMLElement | DocumentFragment | Comment | Array<any>>;
Expand Down Expand Up @@ -137,22 +146,31 @@ export function generateNode(node: RevactNode): HTMLElement | DocumentFragment |
}

const fakeDestroy = () => {
callDeep(node, 'destroy', true);

if (node.tag !== 'textNode') {
removeNodeListener(rootNode as HTMLElement, node.listeners);
}

Object.keys(listSharedObservers).forEach(key => {
if (listSharedObservers[key]) {
listSharedObservers[key].destroy();
}
});
if (textNodeSubscriber) {
(node.textValue as Observer<any>).removeSubscriber(textNodeSubscriber);
}

Object.keys(listNodeObservers).forEach(key => {
if (listNodeObservers[key]) {
listNodeObservers[key].destroy();
}
});
callDeep(node, 'destroy', true);
if (attrsSubscriber) {
(node.attrs as Observer<any>).removeSubscriber(attrsSubscriber);
}

if (propsSubscriber) {
(node.props as Observer<any>).removeSubscriber(propsSubscriber);
}

if (classListSubscriber) {
(node.classList as Observer<any>).removeSubscriber(classListSubscriber);
}

if (styleSubscriber) {
(node.styles as Observer<any>).removeSubscriber(styleSubscriber);
}
};

if (typeof node.show === 'object') {
Expand All @@ -165,18 +183,22 @@ export function generateNode(node: RevactNode): HTMLElement | DocumentFragment |
parent.replaceChild(rootNode, comment);
}
callDeep(node, 'reInit', false);
// List for observer textValue
Object.keys(listSharedObservers).forEach(key => {
if (listSharedObservers[key]) {
listSharedObservers[key].reInit();
}
});
// List for observer for node
Object.keys(listNodeObservers).forEach(key => {
if (listNodeObservers[key]) {
listNodeObservers[key].reInit();
}
});

if (styleSubscriber) {
(node.styles as Observer<any>).addSubscriber(styleSubscriber);
}
if (classListSubscriber) {
(node.classList as Observer<any>).addSubscriber(classListSubscriber);
}
if (propsSubscriber) {
(node.props as Observer<any>).addSubscriber(propsSubscriber);
}
if (attrsSubscriber) {
(node.attrs as Observer<any>).addSubscriber(attrsSubscriber);
}
if (textNodeSubscriber) {
(node.textValue as Observer<any>).addSubscriber(textNodeSubscriber);
}
if (node.tag !== 'textNode') {
addNodeListener(rootNode as HTMLElement, node.listeners);
}
Expand All @@ -192,9 +214,6 @@ export function generateNode(node: RevactNode): HTMLElement | DocumentFragment |
});

if (node.show.value) {
if (node.instance) {
node.instance.onInit();
}
return rootNode;
} else {
fakeDestroy();
Expand Down
6 changes: 1 addition & 5 deletions src/misc/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ export function rList<F extends any[]>(
}

const mapKeyFnIndex = new Map<any, number>();
let responseArray = iteration.value.map((item: any, index: number) => {
const el = mapFn(item, itemFn, inputs, index, keyFn);
mapKeyFnIndex.set(el.fdKey, index);
return el;
});
let responseArray: any[] = [];

iteration.addSubscriber(value => {
const parent: HTMLElement = (responseArray as any)._parent;
Expand Down
18 changes: 7 additions & 11 deletions src/observer/observer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export class Observer<T> {
private subscribers: Array<(e: T) => void> = [];
private _subs: Set<(e: T) => void> = new Set();
private firstState: T;
private isDestroy = false;
constructor(private _value?: T, private forces: boolean = false) {
Expand All @@ -15,7 +15,7 @@ export class Observer<T> {
return;
}
if (force) {
this.subscribers = [];
this._subs.clear();
}
this.isDestroy = true;
}
Expand All @@ -29,13 +29,13 @@ export class Observer<T> {
if (this.isDestroy) {
return;
}
this.subscribers.push(subscriber);
this._subs.add(subscriber);
subscriber(this._value);
}

removeSubscriber(subscriber: (e: T) => void) {
const index = this.subscribers.indexOf(subscriber);
if (index > -1) {
this.subscribers.splice(index, 1);
if (this._subs.has(subscriber)) {
this._subs.delete(subscriber);
}
}

Expand All @@ -47,10 +47,6 @@ export class Observer<T> {
return;
}
this._value = value;
if (this.subscribers.length) {
this.subscribers.forEach(sub => {
sub(value);
});
}
this._subs.forEach(sub => sub(value));
}
}
46 changes: 29 additions & 17 deletions tests/integration/listRender/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,33 @@ class CountersShared extends Component {
const obs = rValue([])
const obsFn = rValue([])

let firstClickfr = true;
const btnFirst = document.createElement("button")
btnFirst.className = 'first'
btnFirst.addEventListener('click', () => {
if (firstClickfr) {
obs.value = [1, 2, 3, 4, 5]
firstClickfr = false;
return;
}
obs.value = [1]
})

let firstClicksc = true;
const btnSecond = document.createElement("button")
btnSecond.className = 'second'
btnSecond.addEventListener('click', () => {
if (firstClicksc) {
obsFn.value = [1, 2, 3, 4, 5]
firstClicksc = false;
return;
}
obsFn.value = [1]
})
document.body.appendChild(btnFirst)

document.body.appendChild(btnSecond)

export function createObsFor() {
return {
tag: "div",
Expand All @@ -63,24 +90,9 @@ export function createkeyFnObsFor() {
classList: 'list_fn',
children: [
// Here we will on each changes obs, create createDiv with inputs { value: ...}
rList(obs, createCounters, [(e: any) => rValue(e)], (item: any) => item) // we do map from obs to reactive value
rList(obsFn, createCounters, [(e: any) => rValue(e)], (item: any) => item) // we do map from obs to reactive value
]
};
}

document.body.appendChild(generateNode(createkeyFnObsFor()));

setTimeout(() => {
obs.value = [1, 2, 3, 4, 5]
setTimeout(() => {
obs.value = [1]
}, 3000)
}, 3000)


setTimeout(() => {
obsFn.value = [1, 2, 3, 4, 5]
setTimeout(() => {
obsFn.value = [1]
}, 3000)
}, 3000)
document.body.appendChild(generateNode(createkeyFnObsFor()));
Loading

0 comments on commit 60962df

Please sign in to comment.