neo.mjs 10.0.0-beta.6 → 10.0.1
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/RELEASE_NOTES/v10.0.0-beta.1.md +20 -0
- package/.github/RELEASE_NOTES/v10.0.0-beta.2.md +73 -0
- package/.github/RELEASE_NOTES/v10.0.0-beta.3.md +39 -0
- package/.github/RELEASE_NOTES/v10.0.0.md +52 -0
- package/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/resources/data/blog.json +24 -0
- package/apps/portal/view/ViewportController.mjs +6 -4
- package/apps/portal/view/examples/List.mjs +28 -19
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/examples/functional/button/base/MainContainer.mjs +207 -0
- package/examples/functional/button/base/app.mjs +6 -0
- package/examples/functional/button/base/index.html +11 -0
- package/examples/functional/button/base/neo-config.json +6 -0
- package/learn/blog/v10-deep-dive-functional-components.md +293 -0
- package/learn/blog/v10-deep-dive-reactivity.md +522 -0
- package/learn/blog/v10-deep-dive-state-provider.md +432 -0
- package/learn/blog/v10-deep-dive-vdom-revolution.md +194 -0
- package/learn/blog/v10-post1-love-story.md +383 -0
- package/learn/guides/uibuildingblocks/WorkingWithVDom.md +26 -2
- package/package.json +3 -3
- package/src/DefaultConfig.mjs +2 -2
- package/src/Neo.mjs +47 -45
- package/src/component/Abstract.mjs +412 -0
- package/src/component/Base.mjs +18 -380
- package/src/core/Base.mjs +34 -33
- package/src/core/Effect.mjs +30 -34
- package/src/core/EffectManager.mjs +101 -14
- package/src/core/Observable.mjs +69 -65
- package/src/form/field/Text.mjs +11 -5
- package/src/functional/button/Base.mjs +384 -0
- package/src/functional/component/Base.mjs +51 -145
- package/src/layout/Cube.mjs +8 -4
- package/src/manager/VDomUpdate.mjs +179 -94
- package/src/mixin/VdomLifecycle.mjs +4 -1
- package/src/state/Provider.mjs +41 -27
- package/src/util/VDom.mjs +11 -4
- package/src/util/vdom/TreeBuilder.mjs +38 -62
- package/src/worker/mixin/RemoteMethodAccess.mjs +1 -6
- package/test/siesta/siesta.js +15 -3
- package/test/siesta/tests/VdomCalendar.mjs +7 -7
- package/test/siesta/tests/VdomHelper.mjs +7 -7
- package/test/siesta/tests/classic/Button.mjs +113 -0
- package/test/siesta/tests/core/EffectBatching.mjs +46 -41
- package/test/siesta/tests/functional/Button.mjs +113 -0
- package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +59 -0
- package/test/siesta/tests/vdom/Advanced.mjs +14 -8
- package/test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs +9 -9
- package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +6 -6
- package/test/siesta/tests/vdom/layout/Cube.mjs +11 -7
- package/test/siesta/tests/vdom/table/Container.mjs +9 -5
- package/src/core/EffectBatchManager.mjs +0 -67
@@ -0,0 +1,383 @@
|
|
1
|
+
# A Frontend Love Story: Why the Strategies of Today Won't Build the Apps of Tomorrow
|
2
|
+
|
3
|
+
If you’re a frontend developer in 2025, you are a survivor. You’ve mastered a craft that exists in a state of constant,
|
4
|
+
churning evolution. You embraced the power of declarative UIs, celebrated the elegance of modern component models, and
|
5
|
+
learned to build entire worlds inside a browser tab. It was, for a time, a kind of honeymoon. We were in love with what
|
6
|
+
we could create.
|
7
|
+
|
8
|
+
But honeymoons end. And for many of us, the love story has become strained. The passion is still there, but it’s buried
|
9
|
+
under layers of accumulated complexity, performance anxiety, and the nagging feeling that we’re spending more time fighting
|
10
|
+
our tools than building the ambitious applications we dream of.
|
11
|
+
|
12
|
+
This isn’t just a story about the pain of frontend development. It’s a story about a false choice we’ve been forced into
|
13
|
+
a choice between two flawed paradigms that are holding us back from building the future.
|
14
|
+
|
15
|
+
*(Part 1 of 5 in the v10 blog series. Details at the bottom.)*
|
16
|
+
|
17
|
+
---
|
18
|
+
|
19
|
+
## Part 1: The Client-Side Heartbreak - A War on a Single Front
|
20
|
+
|
21
|
+
Our first love was the interactive, client-side application. We were promised a world of rich, stateful experiences that
|
22
|
+
felt like native desktop software, running entirely in the browser. But as our ambitions grew, so did the heartbreak.
|
23
|
+
The very architecture that gave us this power became our cage.
|
24
|
+
|
25
|
+
### The Tyranny of the Main Thread
|
26
|
+
|
27
|
+
The core philosophy of a truly performant system should be that *"the browser's main thread must be treated like a
|
28
|
+
neurosurgeon: only perform precise, scheduled operations with zero distractions."*
|
29
|
+
|
30
|
+
Mainstream frameworks do the opposite. They treat the main thread like a frantic, overworked general practitioner.
|
31
|
+
We ask this single, precious resource to be a world-class UI renderer, a high-performance JavaScript VM, a sophisticated
|
32
|
+
layout engine, and a millisecond-responsive event handler—all at the same time.
|
33
|
+
|
34
|
+
The result is a constant, low-grade war for milliseconds. Every developer knows the feeling in their gut: the janky
|
35
|
+
scroll as a list virtualizer struggles to keep up; the button that feels "stuck" because a complex component is rendering;
|
36
|
+
the entire UI freezing during a heavy data calculation.
|
37
|
+
|
38
|
+
This isn't just a technical failure; it's a breach of trust with the user. Every stutter and freeze erodes their confidence
|
39
|
+
in our creations.
|
40
|
+
|
41
|
+
### Death by a Thousand Optimizations
|
42
|
+
|
43
|
+
To compensate for this architectural flaw, we've been given a toolbox of manual overrides. In the React world, this is
|
44
|
+
the "memoization tax." In other frameworks, it might be manual change detection strategies or complex observable setups.
|
45
|
+
|
46
|
+
The result is the same: you, the developer, are forced to write extra code to prevent the framework from doing unnecessary work.
|
47
|
+
|
48
|
+
This tax is most obvious when looking at a "performant" component side-by-side with one that is performant by design.
|
49
|
+
|
50
|
+
<center>
|
51
|
+
<table>
|
52
|
+
<tr>
|
53
|
+
<th>A "Performant" React Component</th>
|
54
|
+
<th>The Neo.mjs Equivalent</th>
|
55
|
+
</tr>
|
56
|
+
<tr>
|
57
|
+
<td>
|
58
|
+
|
59
|
+
```javascript
|
60
|
+
import {useState, useMemo} from 'react';
|
61
|
+
|
62
|
+
// A typical "optimized" React component
|
63
|
+
const MyComponent = React.memo(({ user }) => {
|
64
|
+
// This will only log when the component *actually* re-renders
|
65
|
+
console.log('Rendering MyComponent');
|
66
|
+
return <div>{user.name}</div>;
|
67
|
+
});
|
68
|
+
|
69
|
+
const App = () => {
|
70
|
+
const [count, setCount] = useState(0);
|
71
|
+
|
72
|
+
// We must wrap this in useMemo...
|
73
|
+
const user = useMemo(() => ({ name: 'John Doe' }), []);
|
74
|
+
|
75
|
+
// ...to prevent MyComponent from re-rendering
|
76
|
+
// every time the App's state changes.
|
77
|
+
return (
|
78
|
+
<div>
|
79
|
+
<button onClick={() => setCount(c => c + 1)}>
|
80
|
+
App Clicks: {count}
|
81
|
+
</button>
|
82
|
+
<MyComponent user={user} />
|
83
|
+
</div>
|
84
|
+
);
|
85
|
+
};
|
86
|
+
```
|
87
|
+
|
88
|
+
</td>
|
89
|
+
<td>
|
90
|
+
|
91
|
+
```javascript
|
92
|
+
import {defineComponent, useConfig} from 'neo.mjs/src/functional/_export.mjs';
|
93
|
+
|
94
|
+
const MyComponent = defineComponent({
|
95
|
+
// The user config is passed in from the parent
|
96
|
+
createVdom({user}) {
|
97
|
+
// This will only log when the component *actually* re-renders
|
98
|
+
console.log('Rendering MyComponent');
|
99
|
+
return {text: user.name};
|
100
|
+
}
|
101
|
+
});
|
102
|
+
|
103
|
+
export default defineComponent({
|
104
|
+
createVdom() {
|
105
|
+
const [count, setCount] = useConfig(0);
|
106
|
+
// By using useConfig, the user object is stable across re-renders
|
107
|
+
const [user] = useConfig({name: 'John Doe'});
|
108
|
+
|
109
|
+
return {
|
110
|
+
cn: [{
|
111
|
+
tag : 'button',
|
112
|
+
text : `App Clicks: ${count}`,
|
113
|
+
handler: () => setCount(prev => prev + 1)
|
114
|
+
}, {
|
115
|
+
module: MyComponent,
|
116
|
+
// Pass the stable user object to the child
|
117
|
+
user
|
118
|
+
}]
|
119
|
+
}
|
120
|
+
}
|
121
|
+
});
|
122
|
+
```
|
123
|
+
|
124
|
+
</td>
|
125
|
+
</tr>
|
126
|
+
</table>
|
127
|
+
</center>
|
128
|
+
|
129
|
+
At first glance, the Neo.mjs syntax might seem more verbose than JSX. That's a deliberate design choice.
|
130
|
+
Instead of a custom syntax that requires transpilation, Neo.mjs uses plain, structured JavaScript objects to define the UI.
|
131
|
+
This makes the code more explicit, eliminates a build step, and creates a blueprint that is incredibly easy for LMMs/AIs
|
132
|
+
to read and manipulate.
|
133
|
+
|
134
|
+
Because the core is pure JavaScript, it also opens the door for optional, more familiar syntaxes
|
135
|
+
based on [Template Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) in the
|
136
|
+
future, should the community desire it. The real story, however, isn't the syntax, but the outcome:
|
137
|
+
the elimination of the memoization tax.
|
138
|
+
|
139
|
+
This isn't an advanced optimization strategy; it's a tedious, mandatory chore. It's a tax on our time and a cage for our
|
140
|
+
creativity. We spend a significant portion of our development cycle simply preventing the framework from doing unnecessary
|
141
|
+
work, a task that the framework should be doing for us.
|
142
|
+
|
143
|
+
One of the core goals for Neo.mjs v10 was to 'meet developers where they are,' making the framework more approachable
|
144
|
+
with familiar patterns.
|
145
|
+
|
146
|
+
While the syntax for these functional components might feel familiar, it's crucial to understand that this familiarity
|
147
|
+
is just the tip of the iceberg. There is a LOT more to it.
|
148
|
+
|
149
|
+
### The State Management Labyrinth
|
150
|
+
|
151
|
+
Nowhere is this pain more acute than when building a truly complex, stateful UI. Forget a simple login form. Imagine a
|
152
|
+
common enterprise requirement: a massive, multi-page, drag-and-drop form generator for a government agency, with 300+ fields,
|
153
|
+
conditional logic, and real-time validation.
|
154
|
+
|
155
|
+
How do we even begin to build this in a single-threaded world?
|
156
|
+
|
157
|
+
The state management alone is a paralyzing choice.
|
158
|
+
|
159
|
+
Do we use hundreds of local state hooks and create a component tree
|
160
|
+
so riddled with props-drilling that it becomes unmanageable? Or do we build a monolithic state object in a global store,
|
161
|
+
only to find that every keystroke triggers a performance-killing update across the entire application?
|
162
|
+
|
163
|
+
We reach for heroic third-party libraries—each a brilliant solution to a problem that, if we're being honest, shouldn't
|
164
|
+
be this hard in the first place. The simple act of building a form, a fundamental building block of the web, has become
|
165
|
+
an architectural nightmare.
|
166
|
+
|
167
|
+
In Neo.mjs, forms are powerful, self-contained state managers. Each `FormContainer` inherently manages the data for its
|
168
|
+
fields, building a hierarchical data object based on field `name` attributes. This simplifies data collection, validation,
|
169
|
+
and even supports complex nested structures and lazy-loaded sections **without requiring an external state provider**.
|
170
|
+
While forms can seamlessly integrate with the framework's state providers for broader application-level data sharing,
|
171
|
+
their core functionality for managing form data is self-sufficient.
|
172
|
+
|
173
|
+
---
|
174
|
+
|
175
|
+
## Part 2: The Server-Side Mirage - A Different Kind of Heartbreak
|
176
|
+
|
177
|
+
The industry saw this pain and offered a solution: "The client is too slow! Let's move rendering to the server with
|
178
|
+
Server-Side Rendering (SSR)." Frameworks like Next.js and SvelteKit promised to solve our problems by delivering
|
179
|
+
fast-loading, pre-rendered HTML.
|
180
|
+
|
181
|
+
And for a certain class of website, it was a revelation. Blogs, marketing sites, and e-commerce storefronts have never
|
182
|
+
been faster. SSR is a brilliant strategy for delivering *content*.
|
183
|
+
|
184
|
+
But it is a poor strategy for building *applications*.
|
185
|
+
|
186
|
+
The promise of SSR quickly becomes a mirage when confronted with the applications we truly want to build.
|
187
|
+
|
188
|
+
How do you build a multi-window IDE, where a user can drag a component from one screen to another,
|
189
|
+
when the server's job is to send you static HTML? How do you manage the persistent, real-time state of a financial trading
|
190
|
+
dashboard when every update requires a round trip to a serverless function?
|
191
|
+
|
192
|
+
You can't. You end up with a clunky hybrid, where the dream of a fluid, native-like web app dies, caught between a slow
|
193
|
+
client and an inflexible server. The server can't possibly know the intricate, second-by-second state of a complex user
|
194
|
+
interface, and the client is left trying to hydrate static markup into a living, breathing application.
|
195
|
+
|
196
|
+
SSR isn't a solution to the problem of application complexity; it's an elegant retreat from it.
|
197
|
+
|
198
|
+
---
|
199
|
+
|
200
|
+
## Part 3: The Architectural Epiphany - The Third Way
|
201
|
+
|
202
|
+
We've been trapped in a false choice: a client-side app that's a nightmare to scale, or a server-side site that can't
|
203
|
+
handle true interactivity. Neither of these paths leads to the future we were promised.
|
204
|
+
|
205
|
+
The entire client vs. server debate is a distraction. The answer isn't about *where* you render, but *how* your entire
|
206
|
+
application is architected.
|
207
|
+
|
208
|
+
The only real solution is to **escape the main thread entirely.**
|
209
|
+
|
210
|
+
This is the architectural epiphany that powers Neo.mjs. It’s a framework built on a simple, powerful idea: your
|
211
|
+
application should not live on the main thread. It should live in a Web Worker.
|
212
|
+
|
213
|
+
By moving the entire application—the component tree, the state management, and the business logic—into a dedicated App Worker,
|
214
|
+
we liberate the main thread to do what it does best: paint pixels. The result is a level of performance and responsiveness
|
215
|
+
that single-threaded frameworks can only dream of.
|
216
|
+
|
217
|
+
That 300-field form generator that was an architectural nightmare? In Neo.mjs, it's trivial. The form's state and
|
218
|
+
validation logic live in the worker, completely decoupled from the DOM. The main thread only ever receives the minimal
|
219
|
+
set of changes required to update the view. The UI remains fluid and responsive, even with hundreds of fields, because
|
220
|
+
it is architecturally impossible for the application logic to block it.
|
221
|
+
|
222
|
+
This isn't a workaround. It's a new paradigm.
|
223
|
+
|
224
|
+
---
|
225
|
+
|
226
|
+
## Part 4: The AI Revelation - The Framework for the Next Generation
|
227
|
+
|
228
|
+
This architectural shift doesn't just solve the problems of today; it unlocks the possibilities of tomorrow. The next
|
229
|
+
great leap in software development is upon us: the rise of AI as a co-developer and a core part of the user experience.
|
230
|
+
And this new reality exposes the flaws of our current tools in a new and unforgiving light.
|
231
|
+
|
232
|
+
Neo.mjs is uniquely positioned as **The Framework For and By AI.**
|
233
|
+
|
234
|
+
### The Framework FOR AI: Building the Cockpit
|
235
|
+
|
236
|
+
Interacting with powerful AI models requires more than a simple chat window. It requires a cockpit. A multi-window,
|
237
|
+
IDE-like environment where a user can write a prompt in one window, see the AI's reasoning in a second, view the
|
238
|
+
generated code in a third, and see a live preview in a fourth.
|
239
|
+
|
240
|
+
This is impossible with traditional frameworks.
|
241
|
+
|
242
|
+
But it's native to Neo.mjs. Its multi-threaded, multi-window architecture
|
243
|
+
is the perfect foundation for building the complex, data-intensive UIs that the AI era demands.
|
244
|
+
|
245
|
+
### The Framework BY AI: Speaking the Right Language
|
246
|
+
|
247
|
+
AIs are now writing frontend code. But we are asking them to write JSX or other template syntaxes—a mix of HTML and
|
248
|
+
JavaScript that is verbose, error-prone, and fundamentally human-centric.
|
249
|
+
|
250
|
+
AIs don't think in JSX. They think in structured data. They think in JSON.
|
251
|
+
|
252
|
+
Neo.mjs is built on the language of AI. It uses **declarative JSON blueprints** to define UI. An AI generating a JSON
|
253
|
+
blueprint is an order of magnitude simpler, faster, and more reliable than an AI generating JSX. The framework's
|
254
|
+
`DomApiRenderer` then takes this simple blueprint and translates it into hyper-performant, secure DOM operations.
|
255
|
+
The AI doesn't need to know *how* to build the UI; it just needs to describe *what* the UI is.
|
256
|
+
|
257
|
+
Beyond the architectural innovations, a significant effort has been invested in making the Neo.mjs codebase inherently
|
258
|
+
understandable for AI. Through countless dedicated sessions, we've meticulously added intent-driven comments to core
|
259
|
+
files (like `src/core/Config.mjs`), ensuring that when Large Language Models are pointed to relevant source code, they
|
260
|
+
can easily grasp the underlying logic and design. This commitment to AI-readability is a testament to Neo.mjs truly
|
261
|
+
being a 'Framework For and By AI,' designed for the future of collaborative development.
|
262
|
+
|
263
|
+
---
|
264
|
+
|
265
|
+
## Your Invitation to a New Way of Building
|
266
|
+
|
267
|
+
This brings us to today. **Neo.mjs v10 is not an upgrade—it's a new operating system for the web.** It is the culmination
|
268
|
+
of years of architectural pioneering, refined into a cohesive and powerful whole. It is the realization of the "third way."
|
269
|
+
|
270
|
+
**Quick Project Overview**
|
271
|
+
|
272
|
+
Neo.mjs is a pioneering multi-threaded JavaScript framework that redefines web development. Leveraging an Off-Main-Thread
|
273
|
+
(OMT) architecture, it delivers unparalleled performance and scalability for complex applications, enabling desktop-class
|
274
|
+
multi-window experiences with advanced features like shared state and component persistence, and a zero-builds development
|
275
|
+
workflow. Its unified class config system provides a consistent, declarative approach to building UIs, managing data, and
|
276
|
+
orchestrating application logic, ensuring linear scalability even for the most intricate projects.
|
277
|
+
|
278
|
+
---
|
279
|
+
|
280
|
+
We've rebuilt our core, created a new functional component model, and revolutionized our rendering engine. But you don't
|
281
|
+
have to take our word for it. We invite you to explore for yourself.
|
282
|
+
|
283
|
+
### Choose Your Own Adventure:
|
284
|
+
|
285
|
+
* **I'm skeptical. Show me the code.**
|
286
|
+
Dive into our collection of 74 live, interactive examples. See the code, edit it in real-time, and witness the
|
287
|
+
performance for yourself. No setup required. Many more demo apps can get navigated to from here.
|
288
|
+
<br>
|
289
|
+
**[=> Explore the Examples Portal](https://neomjs.com/dist/esm/apps/portal/#/home)**
|
290
|
+
|
291
|
+
* **I'm intrigued. How does it actually work?**
|
292
|
+
This article is the first in a five-part series that goes deep into the architecture of v10. Start with our deep
|
293
|
+
dive on the new reactivity system that makes manual optimizations a thing of the past.
|
294
|
+
<br>
|
295
|
+
**[=> Next Article: Deep Dive into the Two-Tier Reactivity System](./v10-deep-dive-reactivity.md)**
|
296
|
+
|
297
|
+
* **I'm ready to build. What's the "Hello World"?**
|
298
|
+
Our `create-app` script will have you running your first multi-threaded application in under a minute. Experience
|
299
|
+
the difference firsthand.
|
300
|
+
<br>
|
301
|
+
`npx neo-app@latest`
|
302
|
+
|
303
|
+
---
|
304
|
+
|
305
|
+
## Conclusion: Fall in Love with Frontend Again
|
306
|
+
|
307
|
+
For too long, we have accepted the limitations of our tools. We have patched the symptoms, paid the performance tax,
|
308
|
+
and lowered our ambitions. We have forgotten the initial joy of building for the web—the thrill of creating something
|
309
|
+
new, powerful, and beautiful.
|
310
|
+
|
311
|
+
Neo.mjs v10 is an invitation to rediscover that passion. It's a framework built on the belief that you shouldn't have
|
312
|
+
to choose between performance and interactivity, between a powerful developer experience and an ambitious user experience.
|
313
|
+
|
314
|
+
It's time to stop fighting the main thread. It's time to stop paying the performance tax. It's time to start building
|
315
|
+
the applications of the future.
|
316
|
+
|
317
|
+
It's time to fall in love with frontend again.
|
318
|
+
|
319
|
+
---
|
320
|
+
|
321
|
+
## The Neo.mjs v10 Blog Post Series
|
322
|
+
|
323
|
+
1. A Frontend Love Story: Why the Strategies of Today Won't Build the Apps of Tomorrow
|
324
|
+
2. [Deep Dive: Named vs. Anonymous State - A New Era of Component Reactivity](./v10-deep-dive-reactivity.md)
|
325
|
+
3. [Beyond Hooks: A New Breed of Functional Components for a Multi-Threaded World](./v10-deep-dive-functional-components.md)
|
326
|
+
4. [Deep Dive: The VDOM Revolution - JSON Blueprints & Asymmetric Rendering](./v10-deep-dive-vdom-revolution.md)
|
327
|
+
5. [Deep Dive: The State Provider Revolution](./v10-deep-dive-state-provider.md)
|
328
|
+
|
329
|
+
---
|
330
|
+
|
331
|
+
## A Personal Note from the Author
|
332
|
+
|
333
|
+
Neo.mjs is one of Europe's biggest Open Source Projects, using the MIT license.</br>
|
334
|
+
Meaning: The entire code base, as well as all demo apps and examples, are **free to use**.
|
335
|
+
|
336
|
+
To put the magnitude of the project in numbers:
|
337
|
+
|
338
|
+
**Number of files:**
|
339
|
+
* `/src` 303
|
340
|
+
* `/apps` 260
|
341
|
+
* `/examples` 471 (105 executable example apps)
|
342
|
+
* `/test` 48
|
343
|
+
* `/resources` 420
|
344
|
+
|
345
|
+
We are talking about **22,000+** commits inside the ecosystem, and **5000+** closed tickets.
|
346
|
+
**770** passing unit tests.
|
347
|
+
|
348
|
+
While the Blog Post Series goes deep into the heart of the v10 core changes, they are barely touching the surface
|
349
|
+
of the entire project. We are talking about a `Buffered Grid` on AG Grid level, Multi-Window IDEs directly included
|
350
|
+
inside the Neo.mjs Website, a fully functional `Calendar` app, a vast component library, and more.
|
351
|
+
|
352
|
+
While Neo.mjs made it into the Top5 of "The most exciting use of technology in 2021" of the OS Awards Program,
|
353
|
+
it is fair to say that the framework is an underdog. Maybe the most underrated framework in the history of
|
354
|
+
frontend development.
|
355
|
+
|
356
|
+
I encourage you to take a deeper dive into the code base and examples. You will find countless hidden gems, and
|
357
|
+
concepts which would have been worth patents.
|
358
|
+
|
359
|
+
Neo.mjs is an Enabler. Using it can give developers wings, and push companies literally years ahead of the competition.
|
360
|
+
|
361
|
+
My belief in Neo.mjs's transformative potential led me to dedicate myself fully to its development, stepping away from a
|
362
|
+
"Custom Software Engineering Senior Manager" role to bring this vision to life. While the open-source landscape presents
|
363
|
+
challenges, we are actively seeking partners and sponsors who share our vision and want to accelerate the future of web development.
|
364
|
+
|
365
|
+
There are several exciting enhancements on the horizon for v11+:
|
366
|
+
* A Neo.mjs middleware (SEO support, critical rendering paths, backend-connectors, Websocket connections to clients)
|
367
|
+
* A multi-window LLM UI
|
368
|
+
* A new design and theming system
|
369
|
+
* `Template Literls` support, for those who really want it
|
370
|
+
* A new version of the Docs App
|
371
|
+
* A lot more Learning Section Content
|
372
|
+
|
373
|
+
---
|
374
|
+
|
375
|
+
Neo.mjs is at a pivotal moment, poised for its next phase of growth. Your engagement is crucial to shaping its future and
|
376
|
+
accelerating its impact. Use your chance to influence the project roadmap, let us know what you would love to see next,
|
377
|
+
e.g. via opening feature requests. Most importantly, you can help to increase the visibility, to allow the right
|
378
|
+
people to find the project. Even with something as simple as a mention on social media.
|
379
|
+
|
380
|
+
A huge **"Thank you!"**, to everyone who was involved with reviewing v10 items.
|
381
|
+
|
382
|
+
Best regards & happy coding,</br>
|
383
|
+
Tobias
|
@@ -29,7 +29,7 @@ Neo.mjs VDom nodes are plain JavaScript objects that represent DOM elements.
|
|
29
29
|
cn : [], // Child nodes array (exclusive with html/text)
|
30
30
|
vtype : 'vnode', // VNode type: 'vnode', 'text', 'root'
|
31
31
|
static : false, // Exclude from delta updates (optimization)
|
32
|
-
removeDom: false, //
|
32
|
+
removeDom: false, // The recommended way to conditionally render nodes. true removes the element from the DOM while preserving the VDOM structure.
|
33
33
|
data : {custom: 'value'}, // data-* attributes
|
34
34
|
// Standard HTML attributes
|
35
35
|
disabled: true,
|
@@ -378,7 +378,31 @@ class DataList extends Component {
|
|
378
378
|
}
|
379
379
|
```
|
380
380
|
|
381
|
-
### 3.
|
381
|
+
### 3. Best Practice: Conditional Rendering via `removeDom`
|
382
|
+
|
383
|
+
For conditionally showing or hiding elements, the recommended and most robust pattern in Neo.mjs is to use the `removeDom`
|
384
|
+
property. This approach ensures **structural integrity** of your VDom tree, which is critical for performance and stability.
|
385
|
+
|
386
|
+
By keeping a static VDom structure and toggling the `removeDom` flag, you allow the framework's diffing engine to work
|
387
|
+
most efficiently. It can rely on the stable index of each child node, resulting in minimal and correct deltas. This also
|
388
|
+
ensures that internal framework features which traverse the VDOM tree (like `syncVdomIds`) function predictably.
|
389
|
+
|
390
|
+
#### The Anti-Pattern: Modifying the `cn` Array
|
391
|
+
|
392
|
+
Avoid dynamically adding or removing objects from the `cn` array. While it might seem intuitive to build the array based
|
393
|
+
on conditions, it leads to a structurally unstable VDom, unless items have their explicitly defined id.
|
394
|
+
|
395
|
+
This anti-pattern is problematic because:
|
396
|
+
1. It changes the length and indexes of the cn array between renders.
|
397
|
+
2. It confuses the diffing algorithm, which can lead to a "delta storm"—a cascade of inefficient and incorrect
|
398
|
+
`removeNode`, `insertNode`, and `moveNode` operations instead of a single, simple update.
|
399
|
+
3. It forces the developer to manually add stable ids to all sibling nodes to help the diffing algorithm keep its bearings,
|
400
|
+
which is an extra, error-prone step.
|
401
|
+
4. It breaks the contract of a stable VDOM structure, which can interfere with internal framework optimizations.
|
402
|
+
|
403
|
+
Always prefer the `removeDom` flag for predictable, performant, and robust components.
|
404
|
+
|
405
|
+
### 4. Complex VDom Transformations (e.g., 3D Animations)
|
382
406
|
|
383
407
|
For sophisticated UI patterns like 3D visualizations or complex dynamic layouts, you might imperatively calculate and apply VDom properties or even use `Neo.applyDeltas()` for maximum performance.
|
384
408
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name" : "neo.mjs",
|
3
|
-
"version" : "10.0.
|
3
|
+
"version" : "10.0.1",
|
4
4
|
"description" : "Neo.mjs: The multi-threaded UI framework for building ultra-fast, desktop-like web applications with uncompromised responsiveness, inherent security, and a transpilation-free dev mode.",
|
5
5
|
"type" : "module",
|
6
6
|
"repository" : {
|
@@ -78,7 +78,7 @@
|
|
78
78
|
},
|
79
79
|
"homepage" : "https://neomjs.com/",
|
80
80
|
"devDependencies": {
|
81
|
-
"@fortawesome/fontawesome-free": "^
|
81
|
+
"@fortawesome/fontawesome-free": "^7.0.0",
|
82
82
|
"autoprefixer" : "^10.4.21",
|
83
83
|
"chalk" : "^5.4.1",
|
84
84
|
"clean-webpack-plugin" : "^4.0.0",
|
@@ -88,7 +88,7 @@
|
|
88
88
|
"fs-extra" : "^11.3.0",
|
89
89
|
"highlightjs-line-numbers.js" : "^2.9.0",
|
90
90
|
"html-minifier-terser" : "^7.2.0",
|
91
|
-
"inquirer" : "^12.
|
91
|
+
"inquirer" : "^12.8.2",
|
92
92
|
"marked" : "^16.1.1",
|
93
93
|
"monaco-editor" : "0.50.0",
|
94
94
|
"neo-jsdoc" : "1.0.1",
|
package/src/DefaultConfig.mjs
CHANGED
@@ -299,12 +299,12 @@ const DefaultConfig = {
|
|
299
299
|
useVdomWorker: true,
|
300
300
|
/**
|
301
301
|
* buildScripts/injectPackageVersion.mjs will update this value
|
302
|
-
* @default '10.0.
|
302
|
+
* @default '10.0.1'
|
303
303
|
* @memberOf! module:Neo
|
304
304
|
* @name config.version
|
305
305
|
* @type String
|
306
306
|
*/
|
307
|
-
version: '10.0.
|
307
|
+
version: '10.0.1'
|
308
308
|
};
|
309
309
|
|
310
310
|
Object.assign(DefaultConfig, {
|
package/src/Neo.mjs
CHANGED
@@ -372,59 +372,61 @@ If you intended to create custom logic, use the 'beforeGet${Neo.capitalize(key)}
|
|
372
372
|
const config = this.getConfig(key);
|
373
373
|
if (!config) return;
|
374
374
|
|
375
|
-
let me
|
376
|
-
oldValue
|
377
|
-
{
|
378
|
-
isNewBatch
|
375
|
+
let me = this,
|
376
|
+
oldValue = config.get(), // Get the old value from the Config instance
|
377
|
+
{EffectManager} = Neo.core,
|
378
|
+
isNewBatch = !EffectManager?.isPaused();
|
379
379
|
|
380
380
|
// If a config change is not triggered via `core.Base#set()`, honor changes inside hooks.
|
381
|
-
isNewBatch &&
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
381
|
+
isNewBatch && EffectManager?.pause();
|
382
|
+
|
383
|
+
try {
|
384
|
+
// 1. Prevent infinite loops:
|
385
|
+
// Immediately remove the pending value from the configSymbol to prevent a getter from
|
386
|
+
// recursively re-triggering this setter.
|
387
|
+
delete me[configSymbol][key];
|
388
|
+
|
389
|
+
switch (config.clone) {
|
390
|
+
case 'deep':
|
391
|
+
value = Neo.clone(value, true, true);
|
392
|
+
break;
|
393
|
+
case 'shallow':
|
394
|
+
value = Neo.clone(value, false, true);
|
395
|
+
break;
|
396
|
+
}
|
396
397
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
398
|
+
// 2. Create a temporary state for beforeSet hooks:
|
399
|
+
// Set the new value directly on the private backing property. This allows any beforeSet
|
400
|
+
// hook to access the new value of this and other configs within the same `set()` call.
|
401
|
+
me[_key] = value;
|
401
402
|
|
402
|
-
|
403
|
-
|
403
|
+
if (typeof me[beforeSet] === 'function') {
|
404
|
+
value = me[beforeSet](value, oldValue);
|
404
405
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
406
|
+
// If they don't return a value, that means no change
|
407
|
+
if (value === undefined) {
|
408
|
+
// Restore the original value if the update is canceled.
|
409
|
+
me[_key] = oldValue;
|
410
|
+
return
|
411
|
+
}
|
411
412
|
}
|
412
|
-
}
|
413
413
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
414
|
+
// 3. Restore state for change detection:
|
415
|
+
// Revert the private backing property to its original value. This is crucial for the
|
416
|
+
// `config.set()` method to correctly detect if the value has actually changed.
|
417
|
+
me[_key] = oldValue;
|
418
|
+
|
419
|
+
// 4. Finalize the change:
|
420
|
+
// The config.set() method performs the final check and, if the value changed,
|
421
|
+
// triggers afterSet hooks and notifies subscribers.
|
422
|
+
if (config.set(value)) {
|
423
|
+
me[afterSet]?.(value, oldValue);
|
424
|
+
me.afterSetConfig?.(key, value, oldValue)
|
425
|
+
}
|
426
|
+
} finally {
|
427
|
+
// End the batch only if this setter started it.
|
428
|
+
isNewBatch && EffectManager?.resume()
|
425
429
|
}
|
426
|
-
|
427
|
-
isNewBatch && EffectBatchManager?.endBatch()
|
428
430
|
}
|
429
431
|
};
|
430
432
|
|