lego-dom 1.3.4 → 1.5.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/CHANGELOG.md +72 -1
- package/main.js +48 -17
- package/main.min.js +2 -2
- package/package.json +1 -1
- package/parse-lego.js +2 -2
- package/vite-plugin.js +0 -14
- package/.github/workflows/deploy-docs.yml +0 -56
- package/.legodom +0 -87
- package/docs/.vitepress/config.js +0 -161
- package/docs/api/config.md +0 -95
- package/docs/api/define.md +0 -58
- package/docs/api/directives.md +0 -50
- package/docs/api/globals.md +0 -29
- package/docs/api/index.md +0 -30
- package/docs/api/lifecycle.md +0 -40
- package/docs/api/route.md +0 -37
- package/docs/api/vite-plugin.md +0 -58
- package/docs/contributing/01-welcome.md +0 -38
- package/docs/contributing/02-registry.md +0 -133
- package/docs/contributing/03-batcher.md +0 -110
- package/docs/contributing/04-reactivity.md +0 -87
- package/docs/contributing/05-caching.md +0 -59
- package/docs/contributing/06-init.md +0 -136
- package/docs/contributing/07-observer.md +0 -72
- package/docs/contributing/08-snap.md +0 -140
- package/docs/contributing/09-diffing.md +0 -69
- package/docs/contributing/10-studs.md +0 -78
- package/docs/contributing/11-scanner.md +0 -117
- package/docs/contributing/12-render.md +0 -138
- package/docs/contributing/13-directives.md +0 -243
- package/docs/contributing/14-events.md +0 -57
- package/docs/contributing/15-router.md +0 -57
- package/docs/contributing/16-state.md +0 -47
- package/docs/contributing/17-legodom.md +0 -48
- package/docs/contributing/index.md +0 -24
- package/docs/examples/form.md +0 -42
- package/docs/examples/index.md +0 -104
- package/docs/examples/routing.md +0 -409
- package/docs/examples/sfc-showcase.md +0 -34
- package/docs/examples/todo-app.md +0 -383
- package/docs/guide/cdn-usage.md +0 -354
- package/docs/guide/components.md +0 -418
- package/docs/guide/directives.md +0 -539
- package/docs/guide/directory-structure.md +0 -248
- package/docs/guide/faq.md +0 -210
- package/docs/guide/getting-started.md +0 -262
- package/docs/guide/index.md +0 -88
- package/docs/guide/lifecycle.md +0 -525
- package/docs/guide/quick-start.md +0 -49
- package/docs/guide/reactivity.md +0 -415
- package/docs/guide/routing.md +0 -334
- package/docs/guide/server-side.md +0 -134
- package/docs/guide/sfc.md +0 -464
- package/docs/guide/templating.md +0 -388
- package/docs/index.md +0 -160
- package/docs/public/logo.svg +0 -17
- package/docs/router/basic-routing.md +0 -103
- package/docs/router/cold-entry.md +0 -91
- package/docs/router/history.md +0 -69
- package/docs/router/index.md +0 -73
- package/docs/router/resolver.md +0 -74
- package/docs/router/surgical-swaps.md +0 -134
- package/docs/tutorial/01-project-setup.md +0 -152
- package/docs/tutorial/02-your-first-component.md +0 -226
- package/docs/tutorial/03-adding-routes.md +0 -279
- package/docs/tutorial/04-multi-page-app.md +0 -329
- package/docs/tutorial/05-state-and-globals.md +0 -285
- package/docs/tutorial/index.md +0 -40
- package/examples/vite-app/README.md +0 -71
- package/examples/vite-app/index.html +0 -42
- package/examples/vite-app/package.json +0 -18
- package/examples/vite-app/src/app.css +0 -3
- package/examples/vite-app/src/app.js +0 -29
- package/examples/vite-app/src/components/app-navbar.lego +0 -34
- package/examples/vite-app/src/components/customers/customer-details.lego +0 -24
- package/examples/vite-app/src/components/customers/customer-orders.lego +0 -21
- package/examples/vite-app/src/components/customers/order-list.lego +0 -55
- package/examples/vite-app/src/components/greeting-card.lego +0 -41
- package/examples/vite-app/src/components/sample-component.lego +0 -75
- package/examples/vite-app/src/components/shells/customers-shell.lego +0 -21
- package/examples/vite-app/src/components/side-menu.lego +0 -46
- package/examples/vite-app/src/components/todo-list.lego +0 -239
- package/examples/vite-app/src/components/widgets/user-card.lego +0 -27
- package/examples/vite-app/vite.config.js +0 -22
- package/tests/error.test.js +0 -74
- package/tests/main.test.js +0 -103
- package/tests/memory.test.js +0 -68
- package/tests/monitoring.test.js +0 -74
- package/tests/naming.test.js +0 -74
- package/tests/parse-lego.test.js +0 -65
- package/tests/security.test.js +0 -67
- package/tests/server.test.js +0 -114
- package/tests/syntax.test.js +0 -67
package/docs/guide/routing.md
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
# Surgical Routing
|
|
2
|
-
|
|
3
|
-
**Stop rebuilding your entire page just to change one div.**
|
|
4
|
-
|
|
5
|
-
LegoDOM's router is different. It doesn't have a single "Root Outlet". Instead, **any element** can be a router target. This allows you to build **Persistent Layouts** (like Sidebars, Music Players, or Chat Windows) that never reload or lose state while the user navigates.
|
|
6
|
-
|
|
7
|
-
::: tip 🚀 Just Want to Navigate Between Pages?
|
|
8
|
-
Here's the quick answer:
|
|
9
|
-
|
|
10
|
-
**1. Define routes in `app.js`:**
|
|
11
|
-
```javascript
|
|
12
|
-
Lego.route('/', 'home-page');
|
|
13
|
-
Lego.route('/login', 'login-page');
|
|
14
|
-
Lego.route('/dashboard', 'dashboard-page');
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
**2. Navigate with links:**
|
|
18
|
-
```html
|
|
19
|
-
<a href="/login" b-link>Go to Login</a>
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
**3. Or navigate with JavaScript:**
|
|
23
|
-
```javascript
|
|
24
|
-
this.$go('/login').get();
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
That's it! For the full tutorial, see [Adding Routes](/tutorial/03-adding-routes).
|
|
28
|
-
:::
|
|
29
|
-
|
|
30
|
-
## Quick Reference
|
|
31
|
-
|
|
32
|
-
| I want to... | Code |
|
|
33
|
-
|--------------|------|
|
|
34
|
-
| Define a route | `Lego.route('/path', 'component-name')` |
|
|
35
|
-
| Link to a page | `<a href="/path" b-link>Click</a>` |
|
|
36
|
-
| Navigate via JS | `this.$go('/path').get()` |
|
|
37
|
-
| Get URL params | `this.$route.params.id` |
|
|
38
|
-
| Update only one div | `<a href="/x" b-target="#myDiv">` |
|
|
39
|
-
| Navigate without URL change | `this.$go('/x').get(false)` |
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## The Architecture: "The Persistent Shell"
|
|
44
|
-
|
|
45
|
-
The best way to use LegoDOM is to define a static "Shell" that holds your persistent tools, and standard outlets for your content.
|
|
46
|
-
|
|
47
|
-
```html
|
|
48
|
-
<body>
|
|
49
|
-
<!-- 1. The Shell (Sidebar): Never reloads. Keeps scroll pos & draft state. -->
|
|
50
|
-
<aside id="sidebar">
|
|
51
|
-
<file-tree></file-tree>
|
|
52
|
-
</aside>
|
|
53
|
-
|
|
54
|
-
<!-- 2. The Stage (Main Content): This changes when URL changes. -->
|
|
55
|
-
<lego-router id="stage"></lego-router>
|
|
56
|
-
|
|
57
|
-
<!-- 3. The Context (Right Panel): Tools based on selection. -->
|
|
58
|
-
<aside id="tools"></aside>
|
|
59
|
-
</body>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Then, you simply tell links *where* to render their content:
|
|
63
|
-
|
|
64
|
-
```html
|
|
65
|
-
<!-- Updates component in #stage (Default URL navigation) -->
|
|
66
|
-
<a href="/dashboard" b-target="#stage">Dashboard</a>
|
|
67
|
-
|
|
68
|
-
<!-- Updates component in #tools (Keeps URL sync, but only touches right panel) -->
|
|
69
|
-
<a href="/tools/settings" b-target="#tools">Settings</a>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
This feels like a native app. The Sidebar doesn't flicker. The scroll position isn't lost.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## 1. Declarative Routing
|
|
77
|
-
|
|
78
|
-
The most common way to route is using standard `<a>` tags enriched with Lego attributes.
|
|
79
|
-
|
|
80
|
-
### `b-target`
|
|
81
|
-
Specifies the CSS selector of the element to replace.
|
|
82
|
-
|
|
83
|
-
```html
|
|
84
|
-
<!-- Swaps content into <div id="main-content"> -->
|
|
85
|
-
<a href="/profile" b-target="#main-content">Go to Profile</a>
|
|
86
|
-
|
|
87
|
-
<!-- Example of using route params in a template -->
|
|
88
|
-
<main>
|
|
89
|
-
<blog-posts b-show="$route.params.section === 'posts'"></blog-posts>
|
|
90
|
-
<blog-authors b-show="$route.params.section === 'authors'"></blog-authors>
|
|
91
|
-
</main>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### `b-link`
|
|
95
|
-
Controls browser history behavior.
|
|
96
|
-
- `b-link` (or just `b-target`): Defaults to `true` (updates URL, pushes history).
|
|
97
|
-
- `b-link="false"`: Does **not** update the URL. Great for tabs, modals, or side-panels.
|
|
98
|
-
|
|
99
|
-
```html
|
|
100
|
-
<!-- Updates URL to /settings, swaps #main -->
|
|
101
|
-
<a href="/settings" b-target="#main">Settings</a>
|
|
102
|
-
|
|
103
|
-
<!-- Keeps URL same, just swaps the sidebar context -->
|
|
104
|
-
<a href="/sidebar/tools" b-target="#sidebar" b-link="false">Open Tools</a>
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Deep Linking & Defaults
|
|
108
|
-
If a user refreshes the page, surgical targets (like `#sidebar`) usually won't have content because the `b-target` click never happened.
|
|
109
|
-
|
|
110
|
-
**The Golden Rule:** Always have a `<lego-router>` as your default "Main" outlet.
|
|
111
|
-
When the page loads, Lego looks for `<lego-router>` to render the URL's matching component.
|
|
112
|
-
|
|
113
|
-
```html
|
|
114
|
-
<body>
|
|
115
|
-
<nav>...</nav>
|
|
116
|
-
|
|
117
|
-
<!-- Default Outlet: Renders /home, /about, etc. -->
|
|
118
|
-
<lego-router id="main-app"></lego-router>
|
|
119
|
-
|
|
120
|
-
<!-- Surgical Outlet: Only updated when specifically targeted -->
|
|
121
|
-
<aside id="sidebar"></aside>
|
|
122
|
-
</body>
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## 2. The `$go` API
|
|
126
|
-
|
|
127
|
-
For full programmatic control, use the globally available `$go` helper. It allows for surgical updates from your JavaScript logic.
|
|
128
|
-
|
|
129
|
-
### Syntax
|
|
130
|
-
`Lego.globals.$go(path, ...targets)`
|
|
131
|
-
|
|
132
|
-
- **path**: The URL to navigate to (e.g., `/user/1`).
|
|
133
|
-
- **targets**: A list of selectors (e.g., `#main`, `#sidebar`). Passing nothing defaults to `lego-router`.
|
|
134
|
-
|
|
135
|
-
### Methods
|
|
136
|
-
The `$go` function returns an object with HTTP verb methods, primarily only `.get()` is relevant for routing, but others exist for consistency.
|
|
137
|
-
|
|
138
|
-
```javascript
|
|
139
|
-
// 1. Standard Navigation (pushes to history)
|
|
140
|
-
Lego.globals.$go('/profile').get();
|
|
141
|
-
|
|
142
|
-
// 2. Surgical Navigation (updates #sidebar, pushes to history)
|
|
143
|
-
Lego.globals.$go('/widgets/clock', '#sidebar').get();
|
|
144
|
-
|
|
145
|
-
// 3. Silent Update (updates #modal, NO history change)
|
|
146
|
-
// Pass `false` as the first argument to .get()
|
|
147
|
-
Lego.globals.$go('/modals/login', '#modal').get(false);
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Interactive Example: "The Shell"
|
|
151
|
-
You can update **multiple** targets at once (future feature) or chain them.
|
|
152
|
-
Commonly, you use `$go` inside your component logic:
|
|
153
|
-
|
|
154
|
-
```html
|
|
155
|
-
<script>
|
|
156
|
-
export default {
|
|
157
|
-
methods: {
|
|
158
|
-
async loadUser() {
|
|
159
|
-
const userId = Lego.globals.$route.params.id; // Access in JS logic
|
|
160
|
-
const user = await fetch(`/api/users/${userId}`).then(r => r.json());
|
|
161
|
-
this.username = user.name;
|
|
162
|
-
},
|
|
163
|
-
openSettings() {
|
|
164
|
-
// Open settings in the sidebar without losing the main page context
|
|
165
|
-
this.global.$go('/settings-panel', '#sidebar').get(false);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
</script>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## 3. Advanced Patterns
|
|
175
|
-
|
|
176
|
-
### The "Sidebar" Pattern
|
|
177
|
-
Keep a persistent Main Content while swapping sidebars.
|
|
178
|
-
|
|
179
|
-
```html
|
|
180
|
-
<nav>
|
|
181
|
-
<!-- Main Nav: Updates URL and main view -->
|
|
182
|
-
<a href="/dashboard" b-target="#main">Dashboard</a>
|
|
183
|
-
<a href="/files" b-target="#main">Files</a>
|
|
184
|
-
</nav>
|
|
185
|
-
|
|
186
|
-
<main id="main">
|
|
187
|
-
<!-- Dashboard or Files render here -->
|
|
188
|
-
</main>
|
|
189
|
-
|
|
190
|
-
<aside id="context-pane">
|
|
191
|
-
<!-- Context specific tools render here -->
|
|
192
|
-
<template b-id="user-profile">
|
|
193
|
-
<h1>User Profile</h1>
|
|
194
|
-
<p>User ID: [[ $route.params.id ]]</p>
|
|
195
|
-
<button @click="loadUser()">Load User</button>
|
|
196
|
-
</template>
|
|
197
|
-
</aside>
|
|
198
|
-
|
|
199
|
-
<!-- Inside Dashboard Component -->
|
|
200
|
-
<button onclick="Lego.globals.$go('/tools/chart-config', '#context-pane').get(false)">
|
|
201
|
-
Configure Chart
|
|
202
|
-
</button>
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### The "Modal" Pattern
|
|
206
|
-
Render a route into a modal dialog container.
|
|
207
|
-
|
|
208
|
-
```html
|
|
209
|
-
<dialog id="modal-container"></dialog>
|
|
210
|
-
|
|
211
|
-
<a href="/login" b-target="#modal-container"
|
|
212
|
-
onclick="document.getElementById('modal-container').showModal()">
|
|
213
|
-
Login
|
|
214
|
-
</a>
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### The "Persistent Layout" Pattern (The Holy Grail)
|
|
218
|
-
This is where LegoDOM outshines traditional routers. You can have static sidebars that **never** reload, while the center content changes dynamically.
|
|
219
|
-
|
|
220
|
-
```html
|
|
221
|
-
<body>
|
|
222
|
-
<!-- LEFT: Never reloads. Keeps scroll position & expanded folders. -->
|
|
223
|
-
<aside id="static-left">
|
|
224
|
-
<file-tree></file-tree>
|
|
225
|
-
</aside>
|
|
226
|
-
|
|
227
|
-
<!-- CENTER: The main router outlet -->
|
|
228
|
-
<lego-router id="main-content"></lego-router>
|
|
229
|
-
|
|
230
|
-
<!-- RIGHT: Context panel for tools/details -->
|
|
231
|
-
<aside id="static-right"></aside>
|
|
232
|
-
</body>
|
|
233
|
-
```
|
|
234
|
-
* **Main Links:** `<a href="/page" b-target="#main-content">`
|
|
235
|
-
* **Tool Links:** `<a href="/tool" b-target="#static-right">`
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
## 4. Deep Routing Strategies
|
|
240
|
-
|
|
241
|
-
When handling deep routes like `/customers/:id/orders/:orderId`, you have two architectural choices.
|
|
242
|
-
|
|
243
|
-
### Option A: The Shell Strategy (Self-Healing)
|
|
244
|
-
Map everything to a single "Shell" component. The Shell determines what to show in its sub-outlets based on the URL params.
|
|
245
|
-
|
|
246
|
-
* **Pros:** Highly surgical. The Shell never re-renders, only its children do.
|
|
247
|
-
* **Cons:** Requires logic in `mounted()` to "heal" the state on page load.
|
|
248
|
-
|
|
249
|
-
```javascript
|
|
250
|
-
// Route Configuration
|
|
251
|
-
Lego.route('/customers/:id', 'customers-shell');
|
|
252
|
-
Lego.route('/customers/:id/orders/:orderId', 'customers-shell');
|
|
253
|
-
|
|
254
|
-
// Component Logic (Self-Healing)
|
|
255
|
-
mounted() {
|
|
256
|
-
if (this.$route.params.orderId) {
|
|
257
|
-
this.$go(window.location.pathname, '#details-pane').get();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### Option B: The Page Strategy (Component Nesting)
|
|
263
|
-
Map deep routes to specific "Page" components. Each page imports and wraps itself in a shared Layout.
|
|
264
|
-
|
|
265
|
-
* **Pros:** Simpler logic. No "healing" code required.
|
|
266
|
-
* **Cons:** The Layout is technically re-created on every route change (though diffing makes it cheap).
|
|
267
|
-
|
|
268
|
-
```javascript
|
|
269
|
-
// Route Configuration
|
|
270
|
-
Lego.route('/customers/:id/orders/:orderId', 'order-details-page');
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
```html
|
|
274
|
-
<!-- order-details-page.lego -->
|
|
275
|
-
<template>
|
|
276
|
-
<customers-layout>
|
|
277
|
-
<order-info id="[[ $route.params.orderId ]]"></order-info>
|
|
278
|
-
</customers-layout>
|
|
279
|
-
</template>
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
## 5. Middleware & Guards
|
|
285
|
-
|
|
286
|
-
Middleware runs **before** the surgical swap happens. It### Accessing Parameters
|
|
287
|
-
Route parameters are available directly via `$route.params` in templates.
|
|
288
|
-
|
|
289
|
-
> **Note:** `$route` is a global helper available in all templates.
|
|
290
|
-
|
|
291
|
-
```javascript
|
|
292
|
-
/*
|
|
293
|
-
* Middleware Signature:
|
|
294
|
-
* (params: Object, globals: Object) => boolean | Promise<boolean>
|
|
295
|
-
* Return `true` to allow navigation, `false` to block.
|
|
296
|
-
*/
|
|
297
|
-
|
|
298
|
-
// Example: Auth Guard
|
|
299
|
-
const requireAuth = (params, globals) => {
|
|
300
|
-
if (!globals.user) {
|
|
301
|
-
// Redirect to login using surgical routing!
|
|
302
|
-
globals.$go('/login', '#main').get();
|
|
303
|
-
return false; // Stop original navigation
|
|
304
|
-
}
|
|
305
|
-
return true;
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
Lego.route('/admin', 'admin-panel', requireAuth);
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
## 5. Smart History
|
|
312
|
-
|
|
313
|
-
Lego's router is "History Aware".
|
|
314
|
-
When you use `b-target`, Lego stores the target selectors in the browser's History State.
|
|
315
|
-
|
|
316
|
-
**What this means:**
|
|
317
|
-
1. You click "Open Sidebar" (Surgical update to `#sidebar`).
|
|
318
|
-
2. You click "Home" (Main update to `#main`).
|
|
319
|
-
3. You click **Back**.
|
|
320
|
-
4. Lego automatically knows to reverse the "Home" navigation.
|
|
321
|
-
5. You click **Back** again.
|
|
322
|
-
6. Lego knows the previous state was a surgical update to `#sidebar` and restores it correctly!
|
|
323
|
-
|
|
324
|
-
## Summary Table
|
|
325
|
-
|
|
326
|
-
| Feature | Code | Description |
|
|
327
|
-
| :--- | :--- | :--- |
|
|
328
|
-
| **Standard Link** | `<a href="/x">` | Standard browser navigation (full reload). |
|
|
329
|
-
| **SPA Link** | `<a href="/x" b-target>` | Default SPA nav. Swaps `<lego-router>`. |
|
|
330
|
-
| **Surgical Link** | `<a href="/x" b-target="#id">` | Swaps content of `#id`. Updates URL. |
|
|
331
|
-
| **Silent Link** | `... b-link="false">` | Swaps content. **No** URL update. |
|
|
332
|
-
| **JS Nav** | `$go('/x').get()` | Programmatic navigation. |
|
|
333
|
-
| **Silent JS** | `$go('/x').get(false)` | Programmatic silent swap. |
|
|
334
|
-
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# Server-Side Architecture
|
|
2
|
-
|
|
3
|
-
LegoDOM is designed to play nicely with backend frameworks like **Heaven**, **Django**, **Rails**, or **Go**.
|
|
4
|
-
|
|
5
|
-
Unlike typical SPAs that require a massive build step, LegoDOM can fetch components **on-demand** from your server. This gives you the routing simplicity of a backend with the interactivity of a frontend framework.
|
|
6
|
-
|
|
7
|
-
## The Auto-Loader Pattern
|
|
8
|
-
|
|
9
|
-
Instead of bundling every component `users-list`, `chat-widget`, `billing-modal` into one big `app.js` file, you can load them lazily.
|
|
10
|
-
|
|
11
|
-
Configure the `loader` hook in your main entry file:
|
|
12
|
-
|
|
13
|
-
```javascript
|
|
14
|
-
Lego.init(document.body, {
|
|
15
|
-
loader: (tagName) => `/components/${tagName}.lego`
|
|
16
|
-
});
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Now, your HTML can just use tags that haven't been defined yet:
|
|
20
|
-
|
|
21
|
-
```html
|
|
22
|
-
<!-- index.html (Server Rendered) -->
|
|
23
|
-
<h1>Dashboard</h1>
|
|
24
|
-
|
|
25
|
-
<!-- LegoDOM sees this, fetches /components/user-feed.lego, and upgrades it -->
|
|
26
|
-
<user-feed></user-feed>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Power Mode: Authentication & State
|
|
30
|
-
|
|
31
|
-
Often you need to pass **Authentication Tokens** or **Global State** to the server to get a personalized component.
|
|
32
|
-
|
|
33
|
-
Return a `Promise` from your loader to take full control of the fetch:
|
|
34
|
-
|
|
35
|
-
```javascript
|
|
36
|
-
Lego.init(document.body, {
|
|
37
|
-
loader: async (tagName) => {
|
|
38
|
-
const token = localStorage.getItem('jwt');
|
|
39
|
-
|
|
40
|
-
const response = await fetch(`/components/${tagName}`, {
|
|
41
|
-
headers: {
|
|
42
|
-
'Authorization': `Bearer ${token}`,
|
|
43
|
-
'X-Theme': Lego.globals.theme
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
if (!response.ok) return null;
|
|
48
|
-
return await response.text(); // Return the SFC content
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Server-Side State Injection
|
|
54
|
-
|
|
55
|
-
Since the server generates the `.lego` file, it can inject data before sending it to the browser.
|
|
56
|
-
|
|
57
|
-
**Example (Backend Pseudocode):**
|
|
58
|
-
|
|
59
|
-
```python
|
|
60
|
-
# GET /components/user-card ("Flask-like" syntax)
|
|
61
|
-
@app.route('/components/user-card')
|
|
62
|
-
def get_user_card():
|
|
63
|
-
user = db.get_current_user()
|
|
64
|
-
|
|
65
|
-
# We bake the data right into the template!
|
|
66
|
-
return f"""
|
|
67
|
-
<template>
|
|
68
|
-
<div class="card">
|
|
69
|
-
<h2>[[ name ]]</h2>
|
|
70
|
-
<p>Balance: $[[ balance ]]</p>
|
|
71
|
-
</div>
|
|
72
|
-
</template>
|
|
73
|
-
|
|
74
|
-
<script>
|
|
75
|
-
export default {
|
|
76
|
-
name: "{user.name}",
|
|
77
|
-
balance: {user.balance}
|
|
78
|
-
}
|
|
79
|
-
</script>
|
|
80
|
-
"""
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
The browser receives a component that **already has the data**. No second API call needed!
|
|
84
|
-
|
|
85
|
-
## Runtime Components
|
|
86
|
-
|
|
87
|
-
If you fetch component code manually (e.g. via WebSockets or a custom pipeline), you can register it using `Lego.defineSFC`:
|
|
88
|
-
|
|
89
|
-
```javascript
|
|
90
|
-
socket.on('component_update', (msg) => {
|
|
91
|
-
// msg.code contains the <template>... string
|
|
92
|
-
Lego.defineSFC(msg.code, msg.name + '.lego');
|
|
93
|
-
});
|
|
94
|
-
```
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Production Considerations
|
|
98
|
-
|
|
99
|
-
### 1. Error Handling
|
|
100
|
-
What if the server returns a 404 or 500? Your `loader` should handle this gracefully so the user isn't stuck with a blank screen.
|
|
101
|
-
|
|
102
|
-
```javascript
|
|
103
|
-
loader: async (tagName) => {
|
|
104
|
-
try {
|
|
105
|
-
const res = await fetch(`/components/${tagName}.lego`);
|
|
106
|
-
if (!res.ok) {
|
|
107
|
-
console.error(`Component ${tagName} failed: ${res.status}`);
|
|
108
|
-
// Fallback to a generic error component
|
|
109
|
-
return `<template><div class="error">Failed to load ${tagName}</div></template>`;
|
|
110
|
-
}
|
|
111
|
-
return await res.text();
|
|
112
|
-
} catch (err) {
|
|
113
|
-
return `<template><div class="error">Network Error</div></template>`;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### 2. Caching Strategy
|
|
119
|
-
LegoDOM caches the *compiled class* of a component once loaded. It does **not** re-fetch the `.lego` file for every instance.
|
|
120
|
-
However, if you want browser-level caching for the HTTP requests, ensure your server sends correct headers:
|
|
121
|
-
|
|
122
|
-
```http
|
|
123
|
-
Cache-Control: public, max-age=3600, immutable
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### 3. Preloading
|
|
127
|
-
If you know a user is about to visit a page (e.g. hovering a link), you can preload the component SFC:
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
130
|
-
const preload = (tagName) => {
|
|
131
|
-
// Just calling the loader puts it in the browser's fetch cache
|
|
132
|
-
Lego.config.loader(tagName);
|
|
133
|
-
};
|
|
134
|
-
```
|