mark-fe-fwk 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mark-fe-fwk.js +389 -42
- package/package.json +1 -1
package/dist/mark-fe-fwk.js
CHANGED
|
@@ -15,9 +15,145 @@ function removeEventListeners(listeners = {}, el) {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const ARRAY_DIFF_OP = {
|
|
19
|
+
ADD: 'add',
|
|
20
|
+
REMOVE: 'remove',
|
|
21
|
+
MOVE: 'move',
|
|
22
|
+
NOOP: 'noop'
|
|
23
|
+
};
|
|
18
24
|
function withoutNulls(arr) {
|
|
19
25
|
return arr.filter((item) => !(item === null || item === undefined))
|
|
20
26
|
}
|
|
27
|
+
function arraysDiff(oldArray, newArray) {
|
|
28
|
+
return {
|
|
29
|
+
added: newArray.filter(
|
|
30
|
+
(newItem) => !oldArray.includes(newItem)
|
|
31
|
+
),
|
|
32
|
+
removed: oldArray.filter(
|
|
33
|
+
(oldItem) => !newArray.includes(oldItem)
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
class ArrayWithOriginalIndices {
|
|
38
|
+
#array = []
|
|
39
|
+
#originalIndices = []
|
|
40
|
+
#equalsFn
|
|
41
|
+
constructor(array, equalsFn) {
|
|
42
|
+
this.#array = [...array];
|
|
43
|
+
this.#originalIndices = array.map((_,i) => i);
|
|
44
|
+
this.#equalsFn = equalsFn;
|
|
45
|
+
}
|
|
46
|
+
get length() {
|
|
47
|
+
return this.#array.length
|
|
48
|
+
}
|
|
49
|
+
isRemoval(index, newArray) {
|
|
50
|
+
if (index >= this.length) {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
const item = this.#array[index];
|
|
54
|
+
const indexInNewArray = newArray.findIndex((newItem) => this.#equalsFn(item, newItem));
|
|
55
|
+
return indexInNewArray === -1
|
|
56
|
+
}
|
|
57
|
+
removeItem(index) {
|
|
58
|
+
const operation = {
|
|
59
|
+
op: ARRAY_DIFF_OP.REMOVE,
|
|
60
|
+
index,
|
|
61
|
+
item: this.#array[index]
|
|
62
|
+
};
|
|
63
|
+
this.#array.splice(index, 1);
|
|
64
|
+
this.#originalIndices.splice(index,1);
|
|
65
|
+
return operation
|
|
66
|
+
}
|
|
67
|
+
isNoop(index, newArray) {
|
|
68
|
+
if(index >= this.length) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
const item = this.#array[index];
|
|
72
|
+
const newItem = newArray[index];
|
|
73
|
+
return this.#equalsFn(item, newItem)
|
|
74
|
+
}
|
|
75
|
+
originalIndexAt(index) {
|
|
76
|
+
return this.#originalIndices[index]
|
|
77
|
+
}
|
|
78
|
+
noopItem(index) {
|
|
79
|
+
return {
|
|
80
|
+
op: ARRAY_DIFF_OP.NOOP,
|
|
81
|
+
originalIndex: this.originalIndexAt(index),
|
|
82
|
+
index,
|
|
83
|
+
item: this.#array[index]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
isAddition(item, fromIdx) {
|
|
87
|
+
return this.findIndexFrom(item, fromIdx) === -1
|
|
88
|
+
}
|
|
89
|
+
findIndexFrom(item, fromIndex) {
|
|
90
|
+
for (let i = fromIndex; i < this.length; i++) {
|
|
91
|
+
if( this.#equalsFn(item, this.#array[i])) {
|
|
92
|
+
return i
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return -1
|
|
96
|
+
}
|
|
97
|
+
addItem(item, index) {
|
|
98
|
+
const operation = {
|
|
99
|
+
op: ARRAY_DIFF_OP.ADD,
|
|
100
|
+
index,
|
|
101
|
+
item
|
|
102
|
+
};
|
|
103
|
+
this.#array.splice(index, 0, item);
|
|
104
|
+
this.#originalIndices.splice(index, 0, -1);
|
|
105
|
+
return operation
|
|
106
|
+
}
|
|
107
|
+
moveItem(item, toIndex) {
|
|
108
|
+
const fromIndex = this.findIndexFrom(item, toIndex);
|
|
109
|
+
const operation = {
|
|
110
|
+
op: ARRAY_DIFF_OP.MOVE,
|
|
111
|
+
originalIndex: this.originalIndexAt(fromIndex),
|
|
112
|
+
from: fromIndex,
|
|
113
|
+
index: toIndex,
|
|
114
|
+
item: this.#array[fromIndex]
|
|
115
|
+
};
|
|
116
|
+
const [_item] = this.#array.splice(fromIndex, 1);
|
|
117
|
+
this.#array.splice(toIndex, 0, _item);
|
|
118
|
+
const [originalIndex] = this.#originalIndices.splice(fromIndex, 1);
|
|
119
|
+
this.#originalIndices.splice(toIndex, 0, originalIndex);
|
|
120
|
+
return operation
|
|
121
|
+
}
|
|
122
|
+
removeItemsAfter(index) {
|
|
123
|
+
const operations = [];
|
|
124
|
+
while (this.length > index) {
|
|
125
|
+
operations.push(this.removeItem(index));
|
|
126
|
+
}
|
|
127
|
+
return operations
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function arraysDiffSequence(
|
|
131
|
+
oldArray,
|
|
132
|
+
newArray,
|
|
133
|
+
equalsFn = (a,b) => a === b
|
|
134
|
+
) {
|
|
135
|
+
const sequence = [];
|
|
136
|
+
const array = new ArrayWithOriginalIndices(oldArray, equalsFn);
|
|
137
|
+
for (let index = 0; index < newArray.length; index++) {
|
|
138
|
+
if(array.isRemoval(index, newArray)) {
|
|
139
|
+
sequence.push(array.removeItem(index));
|
|
140
|
+
index--;
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
if(array.isNoop(index, newArray)) {
|
|
144
|
+
sequence.push(array.noopItem(index));
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
const item = newArray[index];
|
|
148
|
+
if(array.isAddition(item, index)) {
|
|
149
|
+
sequence.push(array.addItem(item, index));
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
sequence.push(array.moveItem(item, index));
|
|
153
|
+
}
|
|
154
|
+
sequence.push(...array.removeItemsAfter(newArray.length));
|
|
155
|
+
return sequence
|
|
156
|
+
}
|
|
21
157
|
|
|
22
158
|
const DOM_TYPES = {
|
|
23
159
|
TEXT: 'text',
|
|
@@ -44,6 +180,20 @@ function hFragment(vNodes) {
|
|
|
44
180
|
children: mapTextNodes(withoutNulls(vNodes)),
|
|
45
181
|
}
|
|
46
182
|
}
|
|
183
|
+
function extractChildren(vdom) {
|
|
184
|
+
if (vdom.children == null) {
|
|
185
|
+
return []
|
|
186
|
+
}
|
|
187
|
+
const children = [];
|
|
188
|
+
for(const child of vdom.children) {
|
|
189
|
+
if(child.type === DOM_TYPES.FRAGMENT) {
|
|
190
|
+
children.push(...extractChildren(child));
|
|
191
|
+
} else {
|
|
192
|
+
children.push(child);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return children
|
|
196
|
+
}
|
|
47
197
|
|
|
48
198
|
function destroyDOM(vdom) {
|
|
49
199
|
const { type } = vdom;
|
|
@@ -84,6 +234,40 @@ function removeFragmentNodes(vdom) {
|
|
|
84
234
|
children.forEach(destroyDOM);
|
|
85
235
|
}
|
|
86
236
|
|
|
237
|
+
class Dispatcher {
|
|
238
|
+
#subs = new Map()
|
|
239
|
+
#afterHandlers = []
|
|
240
|
+
subscribe(commandName, handler) {
|
|
241
|
+
if(!this.#subs.has(commandName)) {
|
|
242
|
+
this.#subs.set(commandName, []);
|
|
243
|
+
}
|
|
244
|
+
const handlers = this.#subs.get(commandName);
|
|
245
|
+
if (handlers.includes(handler)) {
|
|
246
|
+
return () => {}
|
|
247
|
+
}
|
|
248
|
+
handlers.push(handler);
|
|
249
|
+
return () => {
|
|
250
|
+
const idx = handlers.indexOf(handler);
|
|
251
|
+
handlers.splice(idx,1);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
afterEveryCommand(handler) {
|
|
255
|
+
this.#afterHandlers.push(handler);
|
|
256
|
+
return () => {
|
|
257
|
+
const idx = this.#afterHandlers.indexOf(handler);
|
|
258
|
+
this.#afterHandlers.splice(idx, 1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
dispatch(commandName, payload) {
|
|
262
|
+
if (this.#subs.has(commandName)) {
|
|
263
|
+
this.#subs.get(commandName).forEach((handler) => handler(payload));
|
|
264
|
+
} else {
|
|
265
|
+
console.warn(`No handlers for command: ${commandName}`);
|
|
266
|
+
}
|
|
267
|
+
this.#afterHandlers.forEach((handler) => handler());
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
87
271
|
function setAttributes(el, attrs) {
|
|
88
272
|
const { class: className, style, ...otherAttrs } = attrs;
|
|
89
273
|
if (className) {
|
|
@@ -110,6 +294,9 @@ function setClass(el, className) {
|
|
|
110
294
|
function setStyle(el, name, value) {
|
|
111
295
|
el.style[name] = value;
|
|
112
296
|
}
|
|
297
|
+
function removeStyle(el, name) {
|
|
298
|
+
el.style[name] = null;
|
|
299
|
+
}
|
|
113
300
|
function setAttribute(el, name, value) {
|
|
114
301
|
if(value == null) {
|
|
115
302
|
removeAttribute(el, name);
|
|
@@ -124,18 +311,18 @@ function removeAttribute(el, name) {
|
|
|
124
311
|
el.removeAttribute(name);
|
|
125
312
|
}
|
|
126
313
|
|
|
127
|
-
function mountDOM(vdom, parentEl) {
|
|
314
|
+
function mountDOM(vdom, parentEl, index) {
|
|
128
315
|
switch (vdom.type) {
|
|
129
316
|
case DOM_TYPES.TEXT: {
|
|
130
|
-
createTextNode(vdom, parentEl);
|
|
317
|
+
createTextNode(vdom, parentEl, index);
|
|
131
318
|
break
|
|
132
319
|
}
|
|
133
320
|
case DOM_TYPES.ELEMENT: {
|
|
134
|
-
createElementNode(vdom, parentEl);
|
|
321
|
+
createElementNode(vdom, parentEl, index);
|
|
135
322
|
break
|
|
136
323
|
}
|
|
137
324
|
case DOM_TYPES.FRAGMENT: {
|
|
138
|
-
createFragmentNodes(vdom, parentEl);
|
|
325
|
+
createFragmentNodes(vdom, parentEl, index);
|
|
139
326
|
break
|
|
140
327
|
}
|
|
141
328
|
default: {
|
|
@@ -143,68 +330,224 @@ function mountDOM(vdom, parentEl) {
|
|
|
143
330
|
}
|
|
144
331
|
}
|
|
145
332
|
}
|
|
146
|
-
function createTextNode(vdom, parentEl) {
|
|
333
|
+
function createTextNode(vdom, parentEl, index) {
|
|
147
334
|
const value = vdom.value;
|
|
148
335
|
const textNode = document.createTextNode(value);
|
|
149
336
|
vdom.el = textNode;
|
|
150
|
-
|
|
337
|
+
insert(textNode, parentEl, index);
|
|
151
338
|
}
|
|
152
|
-
function createFragmentNodes(vdom, parentEl) {
|
|
339
|
+
function createFragmentNodes(vdom, parentEl, index) {
|
|
153
340
|
const { children } = vdom;
|
|
154
341
|
vdom.el = parentEl;
|
|
155
|
-
children.forEach((child) => mountDOM(child, parentEl));
|
|
342
|
+
children.forEach((child, i) => mountDOM(child, parentEl, index ? index + i : null));
|
|
156
343
|
}
|
|
157
|
-
function createElementNode(vdom, parentEl) {
|
|
344
|
+
function createElementNode(vdom, parentEl, index) {
|
|
158
345
|
const { tag, props, children } = vdom;
|
|
159
346
|
const element = document.createElement(tag);
|
|
160
347
|
addProps(element, props, vdom);
|
|
161
348
|
vdom.el = element;
|
|
162
349
|
children.forEach((child) => mountDOM(child, element));
|
|
163
|
-
|
|
350
|
+
insert(element, parentEl, index);
|
|
164
351
|
}
|
|
165
352
|
function addProps(el, props, vdom) {
|
|
166
353
|
const { on: events, ...attrs } = props;
|
|
167
354
|
vdom.listeners = addEventListeners(events, el);
|
|
168
355
|
setAttributes(el, attrs);
|
|
169
356
|
}
|
|
357
|
+
function insert(el, parentEl, index) {
|
|
358
|
+
if(index == null) {
|
|
359
|
+
parentEl.append(el);
|
|
360
|
+
return
|
|
361
|
+
}
|
|
362
|
+
if(index < 0)
|
|
363
|
+
{
|
|
364
|
+
throw new Error(`Index must be a positive integer, got ${index}`)
|
|
365
|
+
}
|
|
366
|
+
const children = parentEl.childNodes;
|
|
367
|
+
if(index >= children.length) {
|
|
368
|
+
parentEl.append(el);
|
|
369
|
+
} else {
|
|
370
|
+
parentEl.insertBefore(el, children[index]);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
170
373
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
374
|
+
function areNodesEqual(nodeOne, nodeTwo) {
|
|
375
|
+
if(nodeOne.type !== nodeTwo.type) {
|
|
376
|
+
return false
|
|
377
|
+
}
|
|
378
|
+
if (nodeOne.type === DOM_TYPES.ELEMENT) {
|
|
379
|
+
const { tag: tagOne } = nodeOne;
|
|
380
|
+
const { tag: tagTwo } = nodeTwo;
|
|
381
|
+
return tagOne === tagTwo
|
|
382
|
+
}
|
|
383
|
+
return true
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function objectsDiff(oldObj, newObj) {
|
|
387
|
+
const oldKeys = Object.keys(oldObj);
|
|
388
|
+
const newKeys = Object.keys(newObj);
|
|
389
|
+
return {
|
|
390
|
+
added: newKeys.filter((key) => !(key in oldObj)),
|
|
391
|
+
removed: oldKeys.filter((key) => !(key in newObj)),
|
|
392
|
+
updated: newKeys.filter(
|
|
393
|
+
(key) => key in oldObj && oldObj[key] !== newObj[key]
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function isNotEmptyString(str) {
|
|
399
|
+
return str !== ''
|
|
400
|
+
}
|
|
401
|
+
function isNotBlankOrEmptyString(str) {
|
|
402
|
+
return isNotEmptyString(str.trim())
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function patchDOM(oldVdom, newVdom, parentEl) {
|
|
406
|
+
if (!areNodesEqual(oldVdom, newVdom)) {
|
|
407
|
+
const index = findIndexInParent(parentEl, oldVdom.el);
|
|
408
|
+
destroyDOM(oldVdom);
|
|
409
|
+
mountDOM(newVdom, parentEl, index);
|
|
410
|
+
return newVdom
|
|
411
|
+
}
|
|
412
|
+
newVdom.el = oldVdom.el;
|
|
413
|
+
switch (newVdom.type) {
|
|
414
|
+
case DOM_TYPES.TEXT: {
|
|
415
|
+
patchText(oldVdom, newVdom);
|
|
416
|
+
return newVdom
|
|
181
417
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
handlers.splice(idx,1);
|
|
418
|
+
case DOM_TYPES.ELEMENT: {
|
|
419
|
+
patchElement(oldVdom, newVdom);
|
|
420
|
+
break
|
|
186
421
|
}
|
|
187
422
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
423
|
+
patchChildren(oldVdom, newVdom);
|
|
424
|
+
return newVdom
|
|
425
|
+
}
|
|
426
|
+
function findIndexInParent(parentEl, el) {
|
|
427
|
+
const index = Array.from(parentEl.childNodes).indexOf(el);
|
|
428
|
+
if (index < 0 ) {
|
|
429
|
+
return null
|
|
194
430
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
431
|
+
return index
|
|
432
|
+
}
|
|
433
|
+
function patchText(oldVdom, newVdom) {
|
|
434
|
+
const el = oldVdom.el;
|
|
435
|
+
const oldText = oldVdom.value;
|
|
436
|
+
const newText = newVdom.value;
|
|
437
|
+
if (oldText !== newText) {
|
|
438
|
+
el.nodeValue = newText;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function patchElement(oldVdom, newVdom) {
|
|
442
|
+
const el = oldVdom.el;
|
|
443
|
+
const {
|
|
444
|
+
class: oldClass,
|
|
445
|
+
style: oldStyle,
|
|
446
|
+
on: oldEvents,
|
|
447
|
+
...oldAttrs
|
|
448
|
+
} = newVdom.props;
|
|
449
|
+
const {
|
|
450
|
+
class: newClass,
|
|
451
|
+
style: newStyle,
|
|
452
|
+
on: newEvents,
|
|
453
|
+
...newAttrs
|
|
454
|
+
} = newVdom.props;
|
|
455
|
+
patchAttrs(el, oldAttrs, newAttrs);
|
|
456
|
+
patchClasses(el, oldClass, newClass);
|
|
457
|
+
patchStyles(el, oldStyle, newStyle);
|
|
458
|
+
newVdom.listeners = patchEvents(el, oldListeners, oldEvents, newEvents);
|
|
459
|
+
}
|
|
460
|
+
function patchAttrs(el, oldAttrs, newAttrs) {
|
|
461
|
+
const { added, removed, updated } = objectsDiff(oldAttrs, newAttrs);
|
|
462
|
+
for (const attr of removed) {
|
|
463
|
+
removeAttribute(el, attr);
|
|
464
|
+
}
|
|
465
|
+
for (const attr of added.concat(updated)) {
|
|
466
|
+
setAttribute(el, attr, newAttrs[attr]);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function patchClasses(el, oldClass, newClass) {
|
|
470
|
+
const oldClasses = toClassList(oldClass);
|
|
471
|
+
const newClasses = toClassList(newClass);
|
|
472
|
+
const { added, removed } = arraysDiff(oldClasses, newClasses);
|
|
473
|
+
if(removed.length > 0) {
|
|
474
|
+
el.classList.remove(...removed);
|
|
475
|
+
}
|
|
476
|
+
if(added.length > 0) {
|
|
477
|
+
el.classList.add(...added);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function toClassList(classes = '') {
|
|
481
|
+
return Array.isArray(classes)
|
|
482
|
+
? classes.filter(isNotBlankOrEmptyString)
|
|
483
|
+
: classes.split(/(\s+)/).filter(isNotBlankOrEmptyString)
|
|
484
|
+
}
|
|
485
|
+
function patchStyles(el, oldStyle = {}, newStyle = {}) {
|
|
486
|
+
const {added, removed, updated } = objectsDiff(oldStyle, newStyle);
|
|
487
|
+
for( const style of removed)
|
|
488
|
+
{
|
|
489
|
+
removeStyle(el, style);
|
|
490
|
+
}
|
|
491
|
+
for (const style of added.concat(updated)) {
|
|
492
|
+
setStyle(el, style, newStyle[style]);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function patchEvents(
|
|
496
|
+
el,
|
|
497
|
+
oldListeners = {},
|
|
498
|
+
oldEvents = {},
|
|
499
|
+
newEvents = {}
|
|
500
|
+
) {
|
|
501
|
+
const { removed, added, updated } = objectsDiff(oldEvents, newEvents);
|
|
502
|
+
for (const eventName of removed.concat(updated)) {
|
|
503
|
+
el.removeEventListener(eventName, oldListeners[eventName]);
|
|
504
|
+
}
|
|
505
|
+
const addedListeners = {};
|
|
506
|
+
for (const eventName of added.concat(updated)) {
|
|
507
|
+
const listener = addEventListener(eventName, newEvents[eventName], el);
|
|
508
|
+
addedListeners[eventName] = listener;
|
|
509
|
+
}
|
|
510
|
+
return addedListeners
|
|
511
|
+
}
|
|
512
|
+
function patchChildren(oldVdom, newVdom) {
|
|
513
|
+
const oldChildren = extractChildren(oldVdom);
|
|
514
|
+
const newChildren = extractChildren(newVdom);
|
|
515
|
+
const parentEl = oldVdom.el;
|
|
516
|
+
const diffSeq = arraysDiffSequence(
|
|
517
|
+
oldChildren, newChildren, areNodesEqual
|
|
518
|
+
);
|
|
519
|
+
for(const operation of diffSeq) {
|
|
520
|
+
const { originalIndex, index, item } = operation;
|
|
521
|
+
switch (operation.op) {
|
|
522
|
+
case ARRAY_DIFF_OP.ADD: {
|
|
523
|
+
mountDOM(item, parentEl, index);
|
|
524
|
+
break
|
|
525
|
+
}
|
|
526
|
+
case ARRAY_DIFF_OP.REMOVE: {
|
|
527
|
+
destroyDOM(item);
|
|
528
|
+
break
|
|
529
|
+
}
|
|
530
|
+
case ARRAY_DIFF_OP.MOVE: {
|
|
531
|
+
const oldChild = oldChildren[originalIndex];
|
|
532
|
+
const newChild = newChildren[index];
|
|
533
|
+
const el = oldChild.el;
|
|
534
|
+
const elAtTargetIndex = parentEl.childNodes[index];
|
|
535
|
+
parentEl.insertBefore(el, elAtTargetIndex);
|
|
536
|
+
patchDOM(oldChild, newChild, parentEl);
|
|
537
|
+
break
|
|
538
|
+
}
|
|
539
|
+
case ARRAY_DIFF_OP.NOOP: {
|
|
540
|
+
patchDOM(oldChildren[originalIndex], newChildren[index], parentEl);
|
|
541
|
+
break
|
|
542
|
+
}
|
|
200
543
|
}
|
|
201
|
-
this.#afterHandlers.forEach((handler) => handler());
|
|
202
544
|
}
|
|
203
545
|
}
|
|
204
546
|
|
|
205
547
|
function createApp({state, view, reducers = {} }) {
|
|
206
548
|
let parentEl = null;
|
|
207
549
|
let vdom = null;
|
|
550
|
+
let isMounted = false;
|
|
208
551
|
const dispatcher = new Dispatcher();
|
|
209
552
|
const subscriptions = [dispatcher.afterEveryCommand(renderApp)];
|
|
210
553
|
function emit(eventName, payload) {
|
|
@@ -218,21 +561,25 @@ function createApp({state, view, reducers = {} }) {
|
|
|
218
561
|
subscriptions.push(subs);
|
|
219
562
|
}
|
|
220
563
|
function renderApp() {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
vdom = view(state, emit);
|
|
225
|
-
mountDOM(vdom, parentEl);
|
|
564
|
+
const newVdom = view(state, emit);
|
|
565
|
+
vdom = patchDOM(vdom, newVdom, parentEl);
|
|
226
566
|
}
|
|
227
567
|
return {
|
|
228
568
|
mount(_parentEl) {
|
|
569
|
+
if(isMounted)
|
|
570
|
+
{
|
|
571
|
+
throw new Error(`Already mounted!`)
|
|
572
|
+
}
|
|
229
573
|
parentEl = _parentEl;
|
|
230
|
-
|
|
574
|
+
vdom = view(state, emit);
|
|
575
|
+
mountDOM(vdom, parentEl);
|
|
576
|
+
isMounted = true;
|
|
231
577
|
},
|
|
232
578
|
unmount() {
|
|
233
579
|
destroyDOM(vdom);
|
|
234
580
|
vdom = null;
|
|
235
581
|
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
582
|
+
isMounted = false;
|
|
236
583
|
}
|
|
237
584
|
}
|
|
238
585
|
}
|