neo.mjs 10.0.0-alpha.3 → 10.0.0-alpha.5
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/.github/CODING_GUIDELINES.md +1 -1
- package/README.md +52 -11
- package/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/blog/List.mjs +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/apps/portal/view/learn/ContentComponent.mjs +2 -1
- package/apps/portal/view/learn/MainContainerStateProvider.mjs +3 -6
- package/apps/realworld/view/HomeComponent.mjs +1 -1
- package/apps/realworld/view/user/ProfileComponent.mjs +1 -1
- package/apps/sharedcovid/view/MainContainerController.mjs +1 -1
- package/apps/shareddialog/view/MainContainerController.mjs +2 -2
- package/buildScripts/buildThemes.mjs +1 -1
- package/examples/grid/animatedRowSorting/Viewport.mjs +4 -4
- package/examples/grid/bigData/ControlsContainer.mjs +3 -3
- package/examples/grid/bigData/GridContainer.mjs +8 -8
- package/examples/grid/cellEditing/MainContainer.mjs +5 -5
- package/examples/grid/container/MainContainer.mjs +4 -4
- package/examples/grid/nestedRecordFields/Viewport.mjs +5 -5
- package/learn/README.md +83 -0
- package/learn/guides/ApplicationBootstrap.md +352 -0
- package/learn/guides/DeclarativeComponentTreesVsImperativeVdom.md +500 -0
- package/learn/guides/WorkingWithVDom.md +748 -0
- package/learn/tree.json +53 -0
- package/package.json +2 -2
- package/resources/scss/src/grid/{View.scss → Body.scss} +2 -2
- package/resources/scss/src/grid/VerticalScrollbar.scss +1 -1
- package/resources/scss/src/grid/plugin/AnimateRows.scss +1 -1
- package/resources/scss/src/grid/plugin/CellEditing.scss +1 -1
- package/resources/scss/theme-dark/grid/{View.scss → Body.scss} +1 -1
- package/resources/scss/theme-light/grid/{View.scss → Body.scss} +1 -1
- package/resources/scss/theme-neo-light/grid/{View.scss → Body.scss} +1 -1
- package/src/DefaultConfig.mjs +27 -14
- package/src/Main.mjs +1 -1
- package/src/Neo.mjs +16 -0
- package/src/button/Base.mjs +2 -2
- package/src/calendar/view/MainContainerStateProvider.mjs +1 -1
- package/src/grid/{View.mjs → Body.mjs} +17 -17
- package/src/grid/Container.mjs +58 -58
- package/src/grid/ScrollManager.mjs +56 -56
- package/src/grid/VerticalScrollbar.mjs +2 -2
- package/src/grid/_export.mjs +2 -2
- package/src/grid/column/AnimatedChange.mjs +5 -5
- package/src/grid/column/Base.mjs +1 -1
- package/src/grid/column/Component.mjs +6 -6
- package/src/grid/header/Button.mjs +1 -1
- package/src/grid/header/Toolbar.mjs +9 -9
- package/src/grid/plugin/AnimateRows.mjs +1 -2
- package/src/layout/Cube.mjs +2 -2
- package/src/main/DeltaUpdates.mjs +11 -10
- package/src/main/addon/Navigator.mjs +1 -1
- package/src/main/addon/WindowPosition.mjs +1 -1
- package/src/main/render/StringBasedRenderer.mjs +1 -1
- package/src/tab/header/Toolbar.mjs +1 -1
- package/src/table/header/Button.mjs +1 -1
- package/src/toolbar/Base.mjs +1 -1
- package/src/util/Style.mjs +2 -6
- package/src/util/VDom.mjs +1 -1
- package/src/util/VNode.mjs +1 -1
- package/src/vdom/Helper.mjs +96 -49
- package/src/vdom/VNode.mjs +38 -2
- package/src/worker/App.mjs +8 -19
- package/src/worker/Base.mjs +87 -5
- package/src/worker/Manager.mjs +90 -36
- package/resources/data/deck/learnneo/tree.json +0 -50
- package/resources/data/deck/whyneo.md +0 -80
- /package/{resources/data/deck/learnneo/pages → learn}/Glossary.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/UsingTheseTopics.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/ConfigSystem.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/Effort.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/Features.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/FormsEngine.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/FourEnvironments.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/Introduction.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/MultiWindow.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/OffTheMainThread.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/Quick.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/benefits/Speed.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/ComponentModels.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Config.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/DescribingTheUI.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Events.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Extending.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/References.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Setup.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/gettingstarted/Workspaces.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/ComponentsAndContainers.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/CustomComponents.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/Forms.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/InstanceLifecycle.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/Layouts.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/MainThreadAddonExample.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/MainThreadAddonIntro.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/Mixins.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/MultiWindow.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/PortalApp.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/StateProviders.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/Tables.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/events/CustomEvents.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/guides/events/DomEvents.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/javascript/ClassFeatures.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/javascript/Classes.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/javascript/NewNode.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/javascript/Overrides.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/javascript/Super.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/tutorials/Earthquakes.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/tutorials/RSP.md +0 -0
- /package/{resources/data/deck/learnneo/pages → learn}/tutorials/TodoList.md +0 -0
- /package/resources/data/{deck/learnneo/data/theBeatles.json → theBeatles.json} +0 -0
@@ -0,0 +1,500 @@
|
|
1
|
+
## Overview
|
2
|
+
|
3
|
+
Neo.mjs employs a unique two-tier architecture that separates **declarative component configuration** from **imperative
|
4
|
+
virtual DOM (VDom) operations**. This design provides both developer productivity and framework performance optimization
|
5
|
+
while maintaining clear separation of concerns across different abstraction layers.
|
6
|
+
|
7
|
+
**Target Audience**: This guide is essential for developers coming from React, Vue, or Angular who need to understand
|
8
|
+
Neo.mjs's fundamentally different approach to UI composition.
|
9
|
+
|
10
|
+
## Architecture at a Glance
|
11
|
+
|
12
|
+
Neo.mjs operates on two distinct abstraction layers:
|
13
|
+
|
14
|
+
- **Component Tree Layer** (Application Development): Declarative, mutable, reactive component configurations
|
15
|
+
- **VDom Tree Layer** (Framework Internals): Imperative virtual DOM operations for performance optimization
|
16
|
+
|
17
|
+
```
|
18
|
+
Your Application Code → Component Tree (declarative, mutable, reactive)
|
19
|
+
↓
|
20
|
+
VDom Tree (imperative, optimized)
|
21
|
+
↓
|
22
|
+
Real DOM
|
23
|
+
```
|
24
|
+
|
25
|
+
## Mental Model Shift for Framework Migrants
|
26
|
+
|
27
|
+
### What You're Used To (React/Vue/Angular)
|
28
|
+
|
29
|
+
In React, Vue, and Angular, you compose UIs by writing templates/JSX that mix HTML elements with custom components:
|
30
|
+
|
31
|
+
```jsx
|
32
|
+
// React/Vue/Angular pattern - mixing HTML with components
|
33
|
+
function App() {
|
34
|
+
return (
|
35
|
+
<div className="viewport">
|
36
|
+
<HeaderToolbar />
|
37
|
+
<div className="main-content">
|
38
|
+
<CustomButton text="Click me" />
|
39
|
+
<DataGrid data={users} />
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
);
|
43
|
+
}
|
44
|
+
```
|
45
|
+
|
46
|
+
Your mental model:</br>
|
47
|
+
*"I write DOM structure and insert components as custom HTML tags."*
|
48
|
+
|
49
|
+
### The Neo.mjs Approach
|
50
|
+
|
51
|
+
In Neo.mjs, you work with **declarative component configurations** that create a component tree abstraction:
|
52
|
+
|
53
|
+
```javascript
|
54
|
+
// Neo.mjs pattern - component relationship configuration
|
55
|
+
class Viewport extends Container {
|
56
|
+
static config = {
|
57
|
+
cls : ['viewport'],
|
58
|
+
items: [{
|
59
|
+
module: HeaderToolbar
|
60
|
+
}, {
|
61
|
+
module: Container,
|
62
|
+
cls : ['main-content'],
|
63
|
+
items : [{
|
64
|
+
module: CustomButton,
|
65
|
+
text : 'Click me'
|
66
|
+
}, {
|
67
|
+
module: DataGrid,
|
68
|
+
data : users
|
69
|
+
}]
|
70
|
+
}]
|
71
|
+
}
|
72
|
+
}
|
73
|
+
```
|
74
|
+
|
75
|
+
Your new mental model:</br>
|
76
|
+
*"I configure a component tree abstraction that sits above the VDom layer. Components define their internal DOM via `vdom`."*
|
77
|
+
|
78
|
+
### Key Architectural Differences
|
79
|
+
|
80
|
+
| Aspect | Other Frameworks | Neo.mjs |
|
81
|
+
|---|---|---|
|
82
|
+
| **Layers** | Single virtual DOM layer | Two-tier: Component tree + VDom |
|
83
|
+
| **Composition** | Mix HTML + components directly in templates/JSX | Pure component hierarchies via `items` configs |
|
84
|
+
| **Property Definition** | Component props & DOM attributes often intermingled | Sharp separation, no accidental overriding of raw DOM attributes |
|
85
|
+
| **Updates** | Manual state management | Automatic reactive updates |
|
86
|
+
| **Mutability** | Recreate tree on changes | Runtime mutable component tree |
|
87
|
+
|
88
|
+
## Component Tree Layer (Application Development)
|
89
|
+
|
90
|
+
### Declarative Configuration
|
91
|
+
|
92
|
+
Components are defined through static configuration objects that describe relationships and behavior:
|
93
|
+
|
94
|
+
```javascript
|
95
|
+
// Declarative component hierarchy
|
96
|
+
class Viewport extends BaseViewport {
|
97
|
+
static config = {
|
98
|
+
layout: {ntype: 'vbox', align: 'stretch'},
|
99
|
+
items: [{
|
100
|
+
module: HeaderToolbar,
|
101
|
+
flex : 'none'
|
102
|
+
}, {
|
103
|
+
module : Container,
|
104
|
+
cls : ['portal-main-content'],
|
105
|
+
layout : 'card',
|
106
|
+
reference: 'main-content',
|
107
|
+
items: [
|
108
|
+
{module: () => import('./home/MainContainer.mjs')},
|
109
|
+
{module: () => import('./learn/MainContainer.mjs')},
|
110
|
+
{module: () => import('./blog/Container.mjs')}
|
111
|
+
]
|
112
|
+
}]
|
113
|
+
}
|
114
|
+
}
|
115
|
+
```
|
116
|
+
|
117
|
+
### Runtime Mutability
|
118
|
+
|
119
|
+
The component tree is **dynamic and mutable at runtime**:
|
120
|
+
|
121
|
+
```javascript
|
122
|
+
// Runtime mutations on the component tree
|
123
|
+
container.add({module: NewComponent}); // Add component
|
124
|
+
container.removeAt(0); // Remove component
|
125
|
+
container.insert(1, {module: AnotherComponent}); // Insert component
|
126
|
+
|
127
|
+
// Move components between containers
|
128
|
+
// => This re-uses the same component JS instance & works accross different browser windows
|
129
|
+
let sourceView = sourceContainer.removeAt(0, false);
|
130
|
+
targetContainer.add(sourceView);
|
131
|
+
```
|
132
|
+
|
133
|
+
### Reactive Updates
|
134
|
+
|
135
|
+
**Every component tree configuration change automatically triggers UI updates**:
|
136
|
+
|
137
|
+
```javascript
|
138
|
+
// These changes automatically update the UI
|
139
|
+
button.text = 'New Text'; // Property change → UI update
|
140
|
+
button.iconCls = 'fa fa-home'; // Config change → UI update
|
141
|
+
container.layout.activeIndex = 1; // Layout change → UI update
|
142
|
+
|
143
|
+
// State changes automatically trigger UI updates
|
144
|
+
viewport.stateProvider.setData({size: 'large'});
|
145
|
+
|
146
|
+
// Shorthand Syntax
|
147
|
+
viewport.setState({size: 'large'}); // State change → UI update
|
148
|
+
|
149
|
+
|
150
|
+
```
|
151
|
+
|
152
|
+
### State Provider Integration
|
153
|
+
|
154
|
+
```javascript
|
155
|
+
// Portal.view.ViewportStateProvider
|
156
|
+
class ViewportStateProvider extends StateProvider {
|
157
|
+
static config = {
|
158
|
+
data: {
|
159
|
+
size: null // Reactive state property
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
// State changes automatically trigger UI updates
|
165
|
+
viewportStateProvider.setData({size: 'large'});
|
166
|
+
```
|
167
|
+
|
168
|
+
## VDom Layer (Framework Internals)
|
169
|
+
|
170
|
+
### Internal VDom Structure
|
171
|
+
|
172
|
+
Framework components define their internal DOM structure through `vdom` config:
|
173
|
+
|
174
|
+
```javascript
|
175
|
+
// Neo.button.Base
|
176
|
+
class Button extends Component {
|
177
|
+
static config = {
|
178
|
+
vdom:
|
179
|
+
{tag: 'button', type: 'button', cn: [
|
180
|
+
{tag: 'span', cls: ['neo-button-glyph']},
|
181
|
+
{tag: 'span', cls: ['neo-button-text']},
|
182
|
+
{cls: ['neo-button-badge']},
|
183
|
+
{cls: ['neo-button-ripple-wrapper'], cn: [
|
184
|
+
{cls: ['neo-button-ripple']}
|
185
|
+
]}
|
186
|
+
]}
|
187
|
+
}
|
188
|
+
}
|
189
|
+
```
|
190
|
+
|
191
|
+
### Imperative VDom Operations
|
192
|
+
|
193
|
+
Framework code performs imperative operations on VDom node properties:
|
194
|
+
|
195
|
+
```javascript
|
196
|
+
// Neo.button.Base - internal framework code
|
197
|
+
afterSetIconCls(value, oldValue) {
|
198
|
+
let {iconNode} = this;
|
199
|
+
|
200
|
+
// Imperative class list manipulation
|
201
|
+
NeoArray.remove(iconNode.cls, oldValue);
|
202
|
+
NeoArray.add(iconNode.cls, value);
|
203
|
+
iconNode.removeDom = !value;
|
204
|
+
|
205
|
+
this.update(); // Trigger DOM reconciliation
|
206
|
+
}
|
207
|
+
|
208
|
+
afterSetText(value, oldValue) {
|
209
|
+
let {textNode} = this;
|
210
|
+
|
211
|
+
// Direct imperative manipulation
|
212
|
+
textNode.removeDom = !value || value === '';
|
213
|
+
if (value) {
|
214
|
+
textNode.text = value;
|
215
|
+
}
|
216
|
+
|
217
|
+
this.update();
|
218
|
+
}
|
219
|
+
```
|
220
|
+
|
221
|
+
**Note**: While `this.update()` is not required for creating the initial VDom tree, it is **crucial** for runtime updates.
|
222
|
+
|
223
|
+
### Performance Optimizations
|
224
|
+
|
225
|
+
```javascript
|
226
|
+
// Neo.button.Base - optimized animations
|
227
|
+
async showRipple(data) {
|
228
|
+
let rippleEl = this.rippleWrapper.cn[0];
|
229
|
+
|
230
|
+
// Direct style manipulation for performance
|
231
|
+
rippleEl.style = Object.assign(rippleEl.style || {}, {
|
232
|
+
animation: 'none',
|
233
|
+
height : `${diameter}px`,
|
234
|
+
left : `${data.clientX - buttonRect.left - radius}px`,
|
235
|
+
top : `${data.clientY - buttonRect.top - radius}px`,
|
236
|
+
width : `${diameter}px`
|
237
|
+
});
|
238
|
+
|
239
|
+
// Asynchronous DOM updates
|
240
|
+
delete this.rippleWrapper.removeDom;
|
241
|
+
this.update();
|
242
|
+
|
243
|
+
await this.timeout(1);
|
244
|
+
|
245
|
+
rippleEl.style.animation = `ripple ${duration}ms linear`;
|
246
|
+
this.update();
|
247
|
+
}
|
248
|
+
```
|
249
|
+
|
250
|
+
## Developer Experience Benefits
|
251
|
+
|
252
|
+
### What You Write vs. What the Framework Handles
|
253
|
+
|
254
|
+
Understanding the value proposition of Neo.mjs's two-tier architecture:
|
255
|
+
|
256
|
+
```javascript
|
257
|
+
// What developers write - declarative configurations
|
258
|
+
{
|
259
|
+
module : Button,
|
260
|
+
text : 'Click Me',
|
261
|
+
iconCls : 'fa fa-home',
|
262
|
+
handler : 'onButtonClick',
|
263
|
+
badgeText: '5'
|
264
|
+
}
|
265
|
+
|
266
|
+
// What the framework automatically handles:
|
267
|
+
// ✓ VDom node creation and management
|
268
|
+
// ✓ Event binding and cleanup
|
269
|
+
// ✓ DOM updates and reconciliation
|
270
|
+
// ✓ Performance optimizations via multi-threading
|
271
|
+
// ✓ Cross-worker communication
|
272
|
+
// ✓ Batched updates and efficient rendering
|
273
|
+
```
|
274
|
+
|
275
|
+
This separation allows developers to focus on **what** they want to build rather than **how** the DOM should be manipulated.
|
276
|
+
|
277
|
+
## Real-World Application Examples
|
278
|
+
|
279
|
+
### Navigation System Architecture
|
280
|
+
|
281
|
+
Routing happens inside view controllers, instead of being tag-based.
|
282
|
+
Developers are in full control to define what route-changes should do.
|
283
|
+
|
284
|
+
```javascript
|
285
|
+
// Portal.view.ViewportController
|
286
|
+
// Declarative route configuration
|
287
|
+
static config = {
|
288
|
+
routes: {
|
289
|
+
'/home' : 'onHomeRoute',
|
290
|
+
'/learn': 'onLearnRoute',
|
291
|
+
'/blog' : 'onBlogRoute'
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
// Imperative navigation handling
|
296
|
+
onHomeRoute(params, value, oldValue) {
|
297
|
+
this.setMainContentIndex(0); // Triggers layout changes
|
298
|
+
}
|
299
|
+
|
300
|
+
// Component tree manipulation
|
301
|
+
async setMainContentIndex(index) {
|
302
|
+
let container = this.getReference('main-content');
|
303
|
+
|
304
|
+
// Reactive layout changes
|
305
|
+
if (this.mainContentLayout === 'cube') {
|
306
|
+
container.layout = {
|
307
|
+
ntype : 'cube',
|
308
|
+
activeIndex: index,
|
309
|
+
fitContainer: true
|
310
|
+
};
|
311
|
+
}
|
312
|
+
|
313
|
+
await this.timeout(200);
|
314
|
+
container.layout.activeIndex = index; // Automatic UI update
|
315
|
+
}
|
316
|
+
```
|
317
|
+
|
318
|
+
### Responsive Design Handling
|
319
|
+
|
320
|
+
If needed, this can be done via JavaScript too (instead of purely focussing on CSS).
|
321
|
+
|
322
|
+
```javascript
|
323
|
+
// Portal.view.Viewport
|
324
|
+
static sizes = ['large', 'medium', 'small', 'x-small', null];
|
325
|
+
|
326
|
+
// Reactive size changes
|
327
|
+
afterSetSize(value, oldValue) {
|
328
|
+
let cls = this.cls;
|
329
|
+
|
330
|
+
// Component tree updates
|
331
|
+
NeoArray.remove(cls, 'portal-size-' + oldValue);
|
332
|
+
NeoArray.add(cls, 'portal-size-' + value);
|
333
|
+
this.cls = cls; // Automatic UI update
|
334
|
+
|
335
|
+
// State synchronization
|
336
|
+
this.stateProvider.setData({size: value});
|
337
|
+
this.controller.size = value;
|
338
|
+
}
|
339
|
+
```
|
340
|
+
|
341
|
+
### Dynamic Component Management
|
342
|
+
|
343
|
+
```javascript
|
344
|
+
// Portal.view.ViewportController
|
345
|
+
async onAppConnect(data) {
|
346
|
+
let app = Neo.apps[data.appName];
|
347
|
+
|
348
|
+
// Component tree mutations
|
349
|
+
let sourceView = sourceContainer.removeAt(0, false);
|
350
|
+
mainView.add(sourceView);
|
351
|
+
|
352
|
+
// Reactive config updates
|
353
|
+
tabContainer.activeIndex = 0;
|
354
|
+
tabContainer.getTabAtIndex(1).disabled = true;
|
355
|
+
}
|
356
|
+
```
|
357
|
+
|
358
|
+
## When to Use Each Layer
|
359
|
+
|
360
|
+
### Use Component Tree Layer When (99% of development):
|
361
|
+
|
362
|
+
- Building application interfaces
|
363
|
+
- Defining component hierarchies through composition
|
364
|
+
- Managing application state and business logic
|
365
|
+
- Creating reusable UI patterns
|
366
|
+
- Implementing user interactions and workflows
|
367
|
+
|
368
|
+
### Use VDom Layer When (1% of development):
|
369
|
+
|
370
|
+
- Creating custom framework components
|
371
|
+
- Defining component internal DOM structure (`vdom`)
|
372
|
+
- Implementing component lifecycle methods (`afterSet*`, `beforeSet*`, `beforeGet*`)
|
373
|
+
- Optimizing rendering performance
|
374
|
+
- Building complex animations or effects
|
375
|
+
|
376
|
+
## Performance Benefits
|
377
|
+
|
378
|
+
### Component Tree Advantages:
|
379
|
+
|
380
|
+
- **Predictable Performance**: Framework handles optimizations automatically
|
381
|
+
- **Automatic Batching**: Updates are batched and optimized
|
382
|
+
- **Memory Efficiency**: Shared component instances and configs
|
383
|
+
- **Worker Threading**: Non-blocking UI operations
|
384
|
+
|
385
|
+
### VDom Layer Advantages:
|
386
|
+
|
387
|
+
- **Fine-Grained Control**: Direct VDom node manipulation when needed
|
388
|
+
- **Custom Optimizations**: Tailored performance strategies
|
389
|
+
- **Minimal Overhead**: Direct property access and updates
|
390
|
+
- **Animation Control**: Precise timing and effects
|
391
|
+
|
392
|
+
## Best Practices
|
393
|
+
|
394
|
+
### For Application Development:
|
395
|
+
|
396
|
+
1. **Favor Component Tree**: Use `items` configurations over VDom manipulation
|
397
|
+
2. **Leverage Reactivity**: Trust automatic UI updates from config changes
|
398
|
+
3. **Use State Providers**: Manage application state reactively
|
399
|
+
4. **Component References**: Use `reference` for component communication
|
400
|
+
|
401
|
+
### For Framework Development:
|
402
|
+
|
403
|
+
1. **Encapsulate Complexity**: Hide imperative operations behind declarative APIs
|
404
|
+
2. **Optimize VDom Updates**: Batch changes and use `update()` efficiently
|
405
|
+
3. **Memory Management**: Clean up event listeners and references
|
406
|
+
4. **Worker Communication**: Minimize cross-worker message passing
|
407
|
+
|
408
|
+
## Migration from Other Frameworks
|
409
|
+
|
410
|
+
### Key Mental Shifts:
|
411
|
+
|
412
|
+
- **From**: Direct DOM/Virtual DOM manipulation
|
413
|
+
- **To**: Component tree configuration and reactive updates
|
414
|
+
</br></br>
|
415
|
+
- **From**: Manual state management and re-rendering
|
416
|
+
- **To**: Automatic reactivity and UI synchronization
|
417
|
+
</br></br>
|
418
|
+
- **From**: Mixing HTML structure with components
|
419
|
+
- **To**: Pure component hierarchies via `items`
|
420
|
+
|
421
|
+
### Integration Patterns:
|
422
|
+
|
423
|
+
```javascript
|
424
|
+
// Wrapping existing components or custom elements
|
425
|
+
{
|
426
|
+
module: LegacyWrapper,
|
427
|
+
items: [{
|
428
|
+
ntype: 'component', // Default value, only added for clarity
|
429
|
+
tag : 'legacy-widget', // Custom element - SECURE
|
430
|
+
// html : '<legacy-widget></legacy-widget>', // AVOID - XSS risk
|
431
|
+
domListeners: {
|
432
|
+
'legacy-event': 'onLegacyEvent'
|
433
|
+
}
|
434
|
+
}]
|
435
|
+
}
|
436
|
+
```
|
437
|
+
|
438
|
+
> **Security Note**: Using `tag` instead of `html` is crucial for preventing Cross-Site Scripting (XSS) vulnerabilities by avoiding raw HTML injection for element creation.
|
439
|
+
|
440
|
+
### Framework-Specific Migration Notes:
|
441
|
+
|
442
|
+
- **From React**: Component configs replace JSX, `items` replaces children composition, reactive updates replace manual state management
|
443
|
+
- **From Vue**: Component configs replace templates, reactive properties work similarly but with automatic UI updates,
|
444
|
+
no need for explicit watchers or computed properties for simple cases.
|
445
|
+
- **From Angular**: More explicit separation between component hierarchy (items) and internal template structure (vdom),
|
446
|
+
no need for complex template syntax or change detection strategies.
|
447
|
+
|
448
|
+
## Advanced Topics
|
449
|
+
|
450
|
+
### Custom Component Development
|
451
|
+
|
452
|
+
```javascript
|
453
|
+
import Component from './src/component/Base.mjs';
|
454
|
+
import VdomUtil from './src/util/Vdom.mjs';
|
455
|
+
|
456
|
+
class CustomComponent extends Component {
|
457
|
+
static config = {
|
458
|
+
// Component tree configuration
|
459
|
+
customProperty_: null,
|
460
|
+
|
461
|
+
// VDom structure definition
|
462
|
+
vdom: {
|
463
|
+
tag: 'div', // Default value, only added for clarity
|
464
|
+
cn: [
|
465
|
+
{tag: 'header', flag: 'headerNode'},
|
466
|
+
{tag: 'main', flag: 'contentNode'}
|
467
|
+
]
|
468
|
+
}
|
469
|
+
}
|
470
|
+
|
471
|
+
// VDom layer manipulation using VdomUtil
|
472
|
+
afterSetCustomProperty(value, oldValue) {
|
473
|
+
let headerNode = VdomUtil.getByFlag(this, 'headerNode');
|
474
|
+
headerNode.text = value;
|
475
|
+
this.update(); // Trigger DOM reconciliation
|
476
|
+
}
|
477
|
+
}
|
478
|
+
```
|
479
|
+
|
480
|
+
Neo.mjs provides utilities such as `VdomUtil` for direct interaction with VDom nodes within a component's lifecycle methods.
|
481
|
+
|
482
|
+
### Performance Monitoring
|
483
|
+
|
484
|
+
```javascript
|
485
|
+
Neo.config.logDeltaUpdates = true; // Enable update timing logs
|
486
|
+
```
|
487
|
+
|
488
|
+
## Conclusion
|
489
|
+
|
490
|
+
Neo.mjs's two-tier architecture successfully balances developer productivity with framework performance through:
|
491
|
+
|
492
|
+
- **Clear Abstraction Layers**: Component tree for apps, VDom for framework optimization
|
493
|
+
- **Multi-Threading Architecture**: Optimal resource utilization across worker threads
|
494
|
+
- **Reactive Component Tree**: Automatic UI synchronization with configuration changes
|
495
|
+
- **Runtime Mutability**: Dynamic component tree modifications without recreation
|
496
|
+
- **Performance Optimization**: Framework-level imperative optimizations when needed
|
497
|
+
|
498
|
+
This architecture enables developers to build complex, performant web applications while focusing on business logic rather
|
499
|
+
than DOM manipulation details. Understanding the distinction between these layers is crucial for effectively leveraging
|
500
|
+
Neo.mjs's capabilities and building maintainable, scalable applications.
|