lego-dom 0.0.8 → 1.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/CHANGELOG.md +44 -0
- package/README.md +49 -432
- package/cdn.html +124 -0
- package/docs/.vitepress/config.js +43 -5
- package/docs/api/directives.md +3 -3
- package/docs/api/globals.md +1 -1
- package/docs/api/index.md +3 -3
- package/docs/api/vite-plugin.md +1 -1
- package/docs/contributing/01-welcome.md +36 -0
- package/docs/contributing/02-registry.md +99 -0
- package/docs/contributing/03-batcher.md +110 -0
- package/docs/contributing/04-reactivity.md +87 -0
- package/docs/contributing/05-caching.md +59 -0
- package/docs/contributing/06-init.md +125 -0
- package/docs/contributing/07-observer.md +69 -0
- package/docs/contributing/08-snap.md +126 -0
- package/docs/contributing/09-diffing.md +69 -0
- package/docs/contributing/10-studs.md +76 -0
- package/docs/contributing/11-scanner.md +104 -0
- package/docs/contributing/12-render.md +116 -0
- package/docs/contributing/13-directives.md +225 -0
- package/docs/contributing/14-events.md +57 -0
- package/docs/contributing/15-router.md +9 -0
- package/docs/contributing/16-state.md +48 -0
- package/docs/contributing/17-legodom.md +55 -0
- package/docs/contributing/index.md +5 -0
- package/docs/examples/form.md +2 -2
- package/docs/examples/index.md +4 -4
- package/docs/examples/routing.md +8 -8
- package/docs/examples/sfc-showcase.md +4 -4
- package/docs/examples/todo-app.md +3 -3
- package/docs/guide/cdn-usage.md +16 -8
- package/docs/guide/components.md +34 -16
- package/docs/guide/contributing.md +2 -2
- package/docs/guide/directives.md +23 -23
- package/docs/guide/getting-started.md +41 -16
- package/docs/guide/index.md +12 -12
- package/docs/guide/lifecycle.md +1 -1
- package/docs/guide/quick-start.md +8 -5
- package/docs/guide/reactivity.md +30 -9
- package/docs/guide/routing.md +189 -289
- package/docs/guide/sfc.md +40 -40
- package/docs/guide/templating.md +4 -4
- package/docs/index.md +48 -14
- package/docs/public/logo.svg +17 -38
- package/docs/router/basic-routing.md +103 -0
- package/docs/router/cold-entry.md +91 -0
- package/docs/router/history.md +69 -0
- package/docs/router/index.md +73 -0
- package/docs/router/resolver.md +74 -0
- package/docs/router/surgical-swaps.md +134 -0
- package/examples/vite-app/README.md +2 -2
- package/examples/vite-app/index.html +9 -13
- package/examples/vite-app/package.json +4 -2
- package/examples/vite-app/src/app.css +3 -0
- package/examples/vite-app/src/app.js +29 -0
- package/examples/vite-app/src/components/app-navbar.lego +34 -0
- package/examples/vite-app/src/components/customers/customer-details.lego +24 -0
- package/examples/vite-app/src/components/customers/customer-orders.lego +21 -0
- package/examples/vite-app/src/components/customers/order-list.lego +55 -0
- package/examples/vite-app/src/components/greeting-card.lego +26 -26
- package/examples/vite-app/src/components/sample-component.lego +58 -58
- package/examples/vite-app/src/components/shells/customers-shell.lego +21 -0
- package/examples/vite-app/src/components/todo-list.lego +239 -0
- package/examples/vite-app/src/components/widgets/user-card.lego +27 -0
- package/examples/vite-app/vite.config.js +7 -2
- package/lego.js +2 -0
- package/main.js +280 -83
- package/package.json +8 -3
- package/parse-lego.js +17 -8
- package/parse-lego.test.js +1 -1
- package/{main.test.js → tests/main.test.js} +34 -17
- package/tests/parse-lego.test.js +65 -0
- package/vite-plugin.js +62 -24
- package/docs/.vitepress/dist/404.html +0 -22
- package/docs/.vitepress/dist/api/define.html +0 -35
- package/docs/.vitepress/dist/api/directives.html +0 -32
- package/docs/.vitepress/dist/api/globals.html +0 -27
- package/docs/.vitepress/dist/api/index.html +0 -25
- package/docs/.vitepress/dist/api/lifecycle.html +0 -38
- package/docs/.vitepress/dist/api/route.html +0 -34
- package/docs/.vitepress/dist/api/vite-plugin.html +0 -37
- package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.js +0 -11
- package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.js +0 -8
- package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.js +0 -3
- package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.js +0 -1
- package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.js +0 -14
- package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.js +0 -10
- package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.js +0 -13
- package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.lean.js +0 -1
- package/docs/.vitepress/dist/assets/app.BG5s3B0P.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/@localSearchIndexroot.DQmuWC2Z.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/VPLocalSearchBox.BO-PSxt1.js +0 -9
- package/docs/.vitepress/dist/assets/chunks/framework.B7OFBR9X.js +0 -19
- package/docs/.vitepress/dist/assets/chunks/theme.DA-iSa9B.js +0 -2
- package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.js +0 -34
- package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.js +0 -30
- package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.js +0 -338
- package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.js +0 -13
- package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.js +0 -297
- package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.js +0 -182
- package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.js +0 -174
- package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.js +0 -1
- package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.js +0 -140
- package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.js +0 -107
- package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.js +0 -2
- package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.js +0 -304
- package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.js +0 -33
- package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.js +0 -135
- package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.js +0 -193
- package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.js +0 -187
- package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.js +0 -119
- package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.lean.js +0 -1
- package/docs/.vitepress/dist/assets/index.md.xV1taCED.js +0 -23
- package/docs/.vitepress/dist/assets/index.md.xV1taCED.lean.js +0 -1
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/style.eycE2Jhw.css +0 -1
- package/docs/.vitepress/dist/examples/form.html +0 -58
- package/docs/.vitepress/dist/examples/index.html +0 -368
- package/docs/.vitepress/dist/examples/routing.html +0 -362
- package/docs/.vitepress/dist/examples/sfc-showcase.html +0 -37
- package/docs/.vitepress/dist/examples/todo-app.html +0 -321
- package/docs/.vitepress/dist/guide/cdn-usage.html +0 -206
- package/docs/.vitepress/dist/guide/components.html +0 -198
- package/docs/.vitepress/dist/guide/contributing.html +0 -25
- package/docs/.vitepress/dist/guide/directives.html +0 -164
- package/docs/.vitepress/dist/guide/getting-started.html +0 -131
- package/docs/.vitepress/dist/guide/index.html +0 -26
- package/docs/.vitepress/dist/guide/lifecycle.html +0 -328
- package/docs/.vitepress/dist/guide/quick-start.html +0 -57
- package/docs/.vitepress/dist/guide/reactivity.html +0 -159
- package/docs/.vitepress/dist/guide/routing.html +0 -217
- package/docs/.vitepress/dist/guide/sfc.html +0 -211
- package/docs/.vitepress/dist/guide/templating.html +0 -143
- package/docs/.vitepress/dist/hashmap.json +0 -1
- package/docs/.vitepress/dist/index.html +0 -47
- package/docs/.vitepress/dist/logo.svg +0 -38
- package/docs/.vitepress/dist/vp-icons.css +0 -1
- package/examples/vite-app/src/main.js +0 -11
- package/examples.js +0 -99
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { defineConfig } from 'vitepress';
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
|
-
title: '
|
|
5
|
-
description: 'A
|
|
6
|
-
base: '/
|
|
4
|
+
title: 'Lego',
|
|
5
|
+
description: 'A feature-rich web components + SFC frontend framework',
|
|
6
|
+
base: '/legodom/',
|
|
7
7
|
|
|
8
8
|
themeConfig: {
|
|
9
9
|
logo: '/logo.svg',
|
|
10
10
|
|
|
11
11
|
nav: [
|
|
12
12
|
{ text: 'Guide', link: '/guide/' },
|
|
13
|
+
{ text: 'Contributing', link: '/contributing/' },
|
|
13
14
|
{ text: 'API', link: '/api/' },
|
|
14
15
|
{ text: 'Examples', link: '/examples/' },
|
|
16
|
+
{ text: 'Router', link: '/router/' },
|
|
15
17
|
{
|
|
16
|
-
text: '
|
|
18
|
+
text: 'v1.0.0',
|
|
17
19
|
items: [
|
|
18
20
|
{ text: 'Changelog', link: 'https://github.com/rayattack/LegoJS/releases' }
|
|
19
21
|
|
|
@@ -22,11 +24,35 @@ export default defineConfig({
|
|
|
22
24
|
],
|
|
23
25
|
|
|
24
26
|
sidebar: {
|
|
27
|
+
'/contributing/': [
|
|
28
|
+
{
|
|
29
|
+
text: 'Contributing',
|
|
30
|
+
items: [
|
|
31
|
+
{ text: 'Topic 1 - Welcome', link: '/contributing/01-welcome' },
|
|
32
|
+
{ text: 'Topic 2 - The Registry', link: '/contributing/02-registry' },
|
|
33
|
+
{ text: 'Topic 3 - Batching', link: '/contributing/03-batcher' },
|
|
34
|
+
{ text: 'Topic 4 - Reactivity', link: '/contributing/04-reactivity' },
|
|
35
|
+
{ text: 'Topic 5 - Caching', link: '/contributing/05-caching' },
|
|
36
|
+
{ text: 'Topic 6 - LegoDOM Init', link: '/contributing/06-init' },
|
|
37
|
+
{ text: 'Topic 7 - Observer', link: '/contributing/07-observer' },
|
|
38
|
+
{ text: 'Topic 8 - Snap', link: '/contributing/08-snap' },
|
|
39
|
+
{ text: 'Topic 9 - Diffing', link: '/contributing/09-diffing' },
|
|
40
|
+
{ text: 'Topic 10 - State', 'link': '/contributing/10-studs' },
|
|
41
|
+
{ text: 'Topic 11 - Scanner', 'link': '/contributing/11-scanner' },
|
|
42
|
+
{ text: 'Topic 12 - Render', 'link': '/contributing/12-render' },
|
|
43
|
+
{ text: 'Topic 13 - Directives', 'link': '/contributing/13-directives' },
|
|
44
|
+
{ text: 'Topic 14 - Events', 'link': '/contributing/14-events' },
|
|
45
|
+
{ text: 'Topic 15 - Router', 'link': '/contributing/15-router' },
|
|
46
|
+
{ text: 'Topic 16 - State', 'link': '/contributing/16-state' },
|
|
47
|
+
{ text: 'Topic 17 - LegoDOM', 'link': '/contributing/17-legodom' },
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
25
51
|
'/guide/': [
|
|
26
52
|
{
|
|
27
53
|
text: 'Introduction',
|
|
28
54
|
items: [
|
|
29
|
-
{ text: 'What is
|
|
55
|
+
{ text: 'What is Lego?', link: '/guide/' },
|
|
30
56
|
{ text: 'Getting Started', link: '/guide/getting-started' },
|
|
31
57
|
{ text: 'Quick Start', link: '/guide/quick-start' },
|
|
32
58
|
{ text: 'Contributing', link: '/guide/contributing' }
|
|
@@ -65,6 +91,18 @@ export default defineConfig({
|
|
|
65
91
|
]
|
|
66
92
|
}
|
|
67
93
|
],
|
|
94
|
+
'/router/': [
|
|
95
|
+
{
|
|
96
|
+
text: 'LegoDOM Router',
|
|
97
|
+
items: [
|
|
98
|
+
{ text: 'About', link: '/router/' },
|
|
99
|
+
{ text: 'Basic Routing', link: '/router/basic-routing' },
|
|
100
|
+
{ text: 'Surgical Swaps', link: '/router/surgical-swaps' },
|
|
101
|
+
{ text: 'Cold Entry', link: '/router/cold-entry' },
|
|
102
|
+
{ text: 'History', link: '/router/history' }
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
],
|
|
68
106
|
'/examples/': [
|
|
69
107
|
{
|
|
70
108
|
text: 'Examples',
|
package/docs/api/directives.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Special attributes that control DOM behavior.
|
|
4
4
|
|
|
5
|
-
## b-
|
|
5
|
+
## b-show
|
|
6
6
|
|
|
7
7
|
Conditionally render an element.
|
|
8
8
|
|
|
9
9
|
```html
|
|
10
|
-
<div b-
|
|
11
|
-
<div b-
|
|
10
|
+
<div b-show="loading">Loading...</div>
|
|
11
|
+
<div b-show="!loading">Content loaded!</div>
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## b-for
|
package/docs/api/globals.md
CHANGED
package/docs/api/index.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# API Reference
|
|
2
2
|
|
|
3
|
-
Welcome to the
|
|
3
|
+
Welcome to the Lego API documentation.
|
|
4
4
|
|
|
5
5
|
## Core
|
|
6
6
|
|
|
@@ -11,11 +11,11 @@ Welcome to the LegoJS API documentation.
|
|
|
11
11
|
|
|
12
12
|
## Templates & Binding
|
|
13
13
|
|
|
14
|
-
- [Directives](/api/directives) - `b-
|
|
14
|
+
- [Directives](/api/directives) - `b-show`, `b-for`, `b-sync`
|
|
15
15
|
|
|
16
16
|
## Browser Support
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Lego requires:
|
|
19
19
|
- Web Components (Custom Elements v1)
|
|
20
20
|
- Shadow DOM v1
|
|
21
21
|
- ES6 Proxy
|
package/docs/api/vite-plugin.md
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Topic 1: The Module Pattern & Private Scope
|
|
2
|
+
|
|
3
|
+
LegoDOM is wrapped in an **IIFE** (Immediately Invoked Function Expression) assigned to `const Lego`. This creates a closure, meaning any variable declared at the top (like `registry` or `proxyCache`) is "private"—it cannot be accessed or tampered with from the browser console unless explicitly exposed.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
const Lego = (() => {
|
|
7
|
+
// ... all the logic ...
|
|
8
|
+
return {
|
|
9
|
+
init: () => { ... },
|
|
10
|
+
define: (tagName, templateHTML, logic = {}) => { ... },
|
|
11
|
+
// ...
|
|
12
|
+
};
|
|
13
|
+
})();
|
|
14
|
+
|
|
15
|
+
if (typeof window !== 'undefined') {
|
|
16
|
+
document.addEventListener('DOMContentLoaded', Lego.init);
|
|
17
|
+
window.Lego = Lego;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Why Not Modular ES6?
|
|
22
|
+
|
|
23
|
+
I started with an **IIFE** and underestimated how big it could grow. It might be a better idea to use ES6 modules, but I'm too lazy to refactor it at the moment.
|
|
24
|
+
|
|
25
|
+
### The use of `WeakMap`
|
|
26
|
+
|
|
27
|
+
You’ll notice the use of `WeakMap` for `proxyCache`, `privateData`, and `forPools`.
|
|
28
|
+
|
|
29
|
+
- **Why not a regular Map?** A `WeakMap` allows the keys (which are DOM elements in this code) to be **garbage collected** if the element is removed from the DOM.
|
|
30
|
+
|
|
31
|
+
- **Memory Leak Prevention:** If we used a regular `Map`, the library would hold a reference to every component ever created, even if you deleted them, eventually crashing the browser tab.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Internal Registry
|
|
35
|
+
|
|
36
|
+
`const registry = {}` acts as the library's "brain." It stores the `<template>` elements that define what a component looks like. When you write `<my-button>`, Lego looks into this object to find the blueprint.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# The Good Ole Registry (Lego Basket)
|
|
2
|
+
|
|
3
|
+
Let's not keep our toys everywhere, let's be good citizens and put them in a basket.
|
|
4
|
+
|
|
5
|
+
PSS!!! Come here lemme tell you a secret - are you a frontend developer? "The DOM is not your enemy"
|
|
6
|
+
we talk about Light DOM and Shadow DOM in a minute ;-).
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Topic 2: The Registry & Internal Storage
|
|
10
|
+
|
|
11
|
+
LegoDOM uses three specialized collections to store the "DNA" of your application. This separation allows the framework to distinguish between what a component **looks like** versus how it **behaves**.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const registry = {};
|
|
15
|
+
const sfcLogic = new Map();
|
|
16
|
+
const sharedStates = new Map();
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 1. The HTML Blueprint Collection (`registry`)
|
|
20
|
+
|
|
21
|
+
`const registry = {}` is a plain object that stores references to `<template>` elements.
|
|
22
|
+
|
|
23
|
+
- When the library initializes, it scans the DOM for any `<template b-id="my-component">` and saves it here.
|
|
24
|
+
|
|
25
|
+
- The `b-id` becomes the key, and the DOM node itself is the value.
|
|
26
|
+
|
|
27
|
+
- **Purpose:** This is the "Light DOM" source used to populate the "Shadow DOM" later during the "snapping" process.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### 2. The SFC Logic Collection (`sfcLogic`)
|
|
31
|
+
|
|
32
|
+
`const sfcLogic = new Map();` stores the JavaScript objects passed into `Lego.define()`.
|
|
33
|
+
|
|
34
|
+
- While `registry` holds the HTML/CSS, `sfcLogic` holds the functions like `mounted()`, `updated()`, or custom methods.
|
|
35
|
+
|
|
36
|
+
- **Why a Map?** Unlike a plain object, a `Map` is more performant for frequent lookups by string keys (tag names) - I think!!! or maybe I read wrong (call me out).
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### 3. The Singleton States Collection (`sharedStates`)
|
|
40
|
+
|
|
41
|
+
`const sharedStates = new Map();` is one of the most powerful "hidden" features of LegoDOM.
|
|
42
|
+
|
|
43
|
+
- Every time you define a component, Lego creates a **reactive version** of its logic and stores it here.
|
|
44
|
+
|
|
45
|
+
- This allows other components to access a specific component's state globally via the `$registry('tag-name')` helper. **NOTE** the component's (template/blueprint) state, not the instance state.
|
|
46
|
+
|
|
47
|
+
- **Example:** If you have 3 `user-profile` components, any other component on the page can peek at their (shared) state data by asking the `sharedStates` map.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
#### Dissecting Registration
|
|
51
|
+
|
|
52
|
+
The `registry` doesn't just wait for you to type; it's fed by three distinct streams across 2 paradigms.
|
|
53
|
+
|
|
54
|
+
**Paradigm 1: Explicitly**
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
const sfcLogic = new Map(); // Specifically for SFC script logic
|
|
58
|
+
// ...
|
|
59
|
+
define: (tagName, templateHTML, logic = {}) => {
|
|
60
|
+
const t = document.createElement('template');
|
|
61
|
+
// ... stores template in registry
|
|
62
|
+
sfcLogic.set(tagName, logic); // Stores the JS part separately
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Paradigm 2: Implicitly**
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
// vite-plugin.js
|
|
70
|
+
async buildStart() {
|
|
71
|
+
const root = config?.root || process.cwd();
|
|
72
|
+
legoFiles = await fg(include, { cwd: searchPath, absolute: true }); // Scans for .lego files
|
|
73
|
+
}
|
|
74
|
+
// ...
|
|
75
|
+
async load(id) {
|
|
76
|
+
if (id.endsWith('.lego')) {
|
|
77
|
+
const defineCall = generateDefineCall(parsed); // Converts .lego file to Lego.define()
|
|
78
|
+
return `import { Lego } from 'lego-dom/main.js';\n${defineCall}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**The Three Ways to Register:**
|
|
85
|
+
1. **The HTML Manual Method:** You put `<template b-id="my-comp">` directly in your `*.html` files. During `Lego.init()`, the library scrapes these and populates the registry. This is great for "no-build" prototypes.
|
|
86
|
+
|
|
87
|
+
2. **The `Lego.define()` JS Method:** You call `Lego.define('my-comp', '<h1>Hi</h1>', { ... logic })` in a standard JavaScript file.
|
|
88
|
+
|
|
89
|
+
3. **The SFC Automatic Method (The "Vite Way"):** * The **Vite Plugin** (as seen in `vite-plugin.js`) acts as a build-time robot.
|
|
90
|
+
|
|
91
|
+
- It uses `fast-glob` (`fg`) to scan your entire `src/components` directory for any file ending in `.lego`.
|
|
92
|
+
|
|
93
|
+
- It parses the `<template>` and `<script>` inside that `.lego` file.
|
|
94
|
+
|
|
95
|
+
- It **injects** a `Lego.define()` call into your JavaScript bundle automatically.
|
|
96
|
+
|
|
97
|
+
- **Result:** You just create a file named `UserCard.lego`, and suddenly `<user-card>` is a valid HTML tag in your app.
|
|
98
|
+
|
|
99
|
+
**Key Insight:** Notice in `main.js` how `define` uses `sfcLogic.set(tagName, logic)`. This is where the plugin "parks" your component's JavaScript code so that when the component "snaps" (Topic 23), it can find its specific logic.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Batching, Scheduling, and doing things the right way
|
|
2
|
+
|
|
3
|
+
Say you are building the next big thing, if you update checkboxes, input fields a 100 times,
|
|
4
|
+
you don't want the DOM to re-render 100 times. That would be a performance nightmare.
|
|
5
|
+
LegoDOM uses Batching & Scheduling to do things the right way.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Topic 3: The Global Batcher & Scheduler
|
|
9
|
+
|
|
10
|
+
When you change data in a reactive application, you often change multiple things at once (e.g., updating a user's name, age, and profile picture). Without a **Batcher**, the browser would try to re-render the HTML every single time one of those properties changed. This would cause "jank" (stuttering) and poor performance.
|
|
11
|
+
|
|
12
|
+
### The `createBatcher` Factory
|
|
13
|
+
|
|
14
|
+
The library defines `createBatcher` as a closure that manages the update cycle. It provides three critical layers of protection:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
const createBatcher = () => {
|
|
18
|
+
let queued = false;
|
|
19
|
+
const componentsToUpdate = new Set();
|
|
20
|
+
let isProcessing = false;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
add: (el) => {
|
|
24
|
+
if (!el || isProcessing) return;
|
|
25
|
+
componentsToUpdate.add(el);
|
|
26
|
+
if (queued) return;
|
|
27
|
+
queued = true;
|
|
28
|
+
|
|
29
|
+
requestAnimationFrame(() => {
|
|
30
|
+
isProcessing = true;
|
|
31
|
+
const batch = Array.from(componentsToUpdate);
|
|
32
|
+
componentsToUpdate.clear();
|
|
33
|
+
queued = false;
|
|
34
|
+
|
|
35
|
+
batch.forEach(el => render(el));
|
|
36
|
+
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
batch.forEach(el => {
|
|
39
|
+
const state = el._studs;
|
|
40
|
+
if (state && typeof state.updated === 'function') {
|
|
41
|
+
try {
|
|
42
|
+
state.updated.call(state);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.error(`[Lego] Error in updated hook:`, e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
isProcessing = false;
|
|
49
|
+
}, 0);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const globalBatcher = createBatcher();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
1. **Deduplication with `Set`**:
|
|
59
|
+
|
|
60
|
+
- The batcher maintains a `componentsToUpdate = new Set()`.
|
|
61
|
+
|
|
62
|
+
- Because a `Set` only stores unique values, if you trigger an update on the same component 50 times in a single loop, it is only added to the "todo" list **once**.
|
|
63
|
+
|
|
64
|
+
2. **The `queued` Gatekeeper**:
|
|
65
|
+
|
|
66
|
+
- A boolean flag `queued` prevents multiple update cycles from being scheduled simultaneously.
|
|
67
|
+
|
|
68
|
+
- Once the first change hits the batcher, it "locks the gate," schedules the work, and ignores further requests to start a new cycle until the current one begins.
|
|
69
|
+
|
|
70
|
+
3. **The `isProcessing` Lock**:
|
|
71
|
+
|
|
72
|
+
- This flag ensures that if a component’s state changes _while_ the render is actually happening, it doesn't cause a collision or infinite loop.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### Timing: `requestAnimationFrame` (rAF)
|
|
76
|
+
|
|
77
|
+
Instead of updating immediately, the library uses `requestAnimationFrame`.
|
|
78
|
+
|
|
79
|
+
- **What it does**: It tells the browser, "Wait until you are just about to draw the next frame on the screen, then run this code".
|
|
80
|
+
|
|
81
|
+
- **Efficiency**: It bundles every single change from every component into a single "tick."
|
|
82
|
+
|
|
83
|
+
- **The Benefit**: This syncs your JavaScript logic with the monitor's refresh rate (usually 60 times per second), making animations and updates look buttery smooth.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
### The Execution Phase
|
|
87
|
+
|
|
88
|
+
When the "frame" triggers, the batcher:
|
|
89
|
+
|
|
90
|
+
1. Takes a snapshot of the `Set`.
|
|
91
|
+
|
|
92
|
+
2. Clears the `Set` for the next round.
|
|
93
|
+
|
|
94
|
+
3. Runs `render(el)` for every component in that batch.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
### The `updated` Lifecycle Hook
|
|
98
|
+
|
|
99
|
+
After the render is complete, the batcher uses a `setTimeout(() => ..., 0)`.
|
|
100
|
+
|
|
101
|
+
- **The Trick**: Even with a delay of `0`, this "macro-task" ensures the code inside runs **after** the browser has finished its rendering work.
|
|
102
|
+
|
|
103
|
+
- **The Hook**: It looks for `our` lifecycle hook function a.k.a or notoriously known as `updated`
|
|
104
|
+
in each component's state (`_studs`) and executes it. This is the perfect place for us to run code that needs to measure the new size of an element or scroll a list to the bottom.
|
|
105
|
+
|
|
106
|
+
**Example**
|
|
107
|
+
If you have a `chat-box` component and you update the messages, you might use the `updated()` hook to scroll to the bottom. Because of this batcher, you are guaranteed that the DOM has finished changing before your scroll logic runs.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
> **Visualizing the flow:** State Change -> `batcher.add(el)` -> `Set` collects `el` -> `rAF` triggers -> `render(el)` runs -> `updated()` hook fires.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Reacting to stimuli is how we react to stimuli
|
|
2
|
+
|
|
3
|
+
If living things react to stimuli, then so should our components.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Topic 4: Reactivity (The Proxy)
|
|
7
|
+
|
|
8
|
+
LegoDOM uses the JavaScript **`Proxy`** object to create its reactivity. Think of a Proxy as a "security guard" that sits in front of your data object. Every time you try to read or change a property, the guard intercepts the request.
|
|
9
|
+
|
|
10
|
+
### The `reactive(obj, el, batcher)` Function
|
|
11
|
+
|
|
12
|
+
When a component is "snapped" (created), its data is passed through this function.
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
//... rest of the code
|
|
16
|
+
const reactive = (obj, el, batcher = globalBatcher) => {
|
|
17
|
+
if (obj === null || typeof obj !== 'object' || obj instanceof Node) return obj;
|
|
18
|
+
if (proxyCache.has(obj)) return proxyCache.get(obj);
|
|
19
|
+
|
|
20
|
+
const handler = {
|
|
21
|
+
get: (t, k) => {
|
|
22
|
+
const val = Reflect.get(t, k);
|
|
23
|
+
if (val !== null && typeof val === 'object' && !(val instanceof Node)) {
|
|
24
|
+
return reactive(val, el, batcher);
|
|
25
|
+
}
|
|
26
|
+
return val;
|
|
27
|
+
},
|
|
28
|
+
set: (t, k, v) => {
|
|
29
|
+
const old = t[k];
|
|
30
|
+
const r = Reflect.set(t, k, v);
|
|
31
|
+
if (old !== v) batcher.add(el);
|
|
32
|
+
return r;
|
|
33
|
+
},
|
|
34
|
+
deleteProperty: (t, k) => {
|
|
35
|
+
const r = Reflect.deleteProperty(t, k);
|
|
36
|
+
batcher.add(el);
|
|
37
|
+
return r;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const p = new Proxy(obj, handler);
|
|
42
|
+
proxyCache.set(obj, p);
|
|
43
|
+
return p;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
//... rest of the code
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
1. **The Traps (`get` and `set`)**:
|
|
50
|
+
|
|
51
|
+
- **The `get` trap**: When you access a property (e.g., `state.count`), the Proxy checks if that property is _also_ an object. If it is, it recursively wraps that object in a Proxy too. This ensures that "deep" data like `user.profile.name` is also reactive.
|
|
52
|
+
|
|
53
|
+
- **The `set` trap**: This is the trigger. When you do `state.count = 5`, the Proxy compares the `old` value with the `new` value. If they are different, it immediately calls `batcher.add(el)`.
|
|
54
|
+
|
|
55
|
+
- **The `deleteProperty` trap**: Even if you delete a key (e.g., `delete state.tempData`), the Proxy intercepts this and tells the batcher to re-render the UI.
|
|
56
|
+
|
|
57
|
+
2. **Handling Objects vs. Nodes**:
|
|
58
|
+
|
|
59
|
+
- The code explicitly checks if a value is an `instanceof Node`. If you try to store a raw HTML element in your state, the library **will not** wrap it in a Proxy. This prevents the library from accidentally trying to "observe" the entire DOM tree, which would crash the browser.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### Concrete Example
|
|
63
|
+
|
|
64
|
+
Imagine you have a component defined like this:
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
Lego.define('counter-app', '<h1>{{count}}</h1>', {
|
|
68
|
+
count: 0,
|
|
69
|
+
increment() { this.count++; }
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **Step A**: Lego takes that object `{ count: 0, ... }` and wraps it in a Proxy.
|
|
75
|
+
|
|
76
|
+
- **Step B**: You call `increment()`.
|
|
77
|
+
|
|
78
|
+
- **Step C**: The line `this.count++` triggers the Proxy's `set` trap.
|
|
79
|
+
|
|
80
|
+
- **Step D**: The `set` trap notices `0` is now `1` and calls `globalBatcher.add(thisElement)`.
|
|
81
|
+
|
|
82
|
+
- **Step E**: The Batcher (from Topic 3) schedules a render for the next animation frame.
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### Why this is "Surgical"
|
|
86
|
+
|
|
87
|
+
Because the `reactive` function is passed the specific element (`el`) it belongs to, it knows exactly which component in the DOM needs to re-render. It doesn't have to guess or refresh the whole page; it targets the specific "Lego block" that owns that data.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# There are 2 hard things in computer science
|
|
2
|
+
|
|
3
|
+
- cache invalidation
|
|
4
|
+
- naming things
|
|
5
|
+
- off-by-one errors
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Proxy Caching (`proxyCache`)
|
|
9
|
+
|
|
10
|
+
In [Topic 4](/contributing/04-reactivity), we saw how `reactive()` wraps objects in a Proxy.
|
|
11
|
+
However, a common issue in reactive programming is trying to wrap the same object multiple
|
|
12
|
+
times, or dealing with circular references
|
|
13
|
+
(e.g. Object A points to Object B, and Object B points back to Object A).
|
|
14
|
+
|
|
15
|
+
### The Problem: Infinite Recursion
|
|
16
|
+
|
|
17
|
+
Without a cache, every time you access a nested object, the `get` trap would create a **new** Proxy wrapper.
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
// Without a cache:
|
|
21
|
+
const p1 = state.user;
|
|
22
|
+
const p2 = state.user;
|
|
23
|
+
console.log(p1 === p2); // false! They are different "guards" for the same data.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This wastes memory and breaks object identity. Even worse, if an object points to itself, the code would keep creating Proxies until the browser crashed with a "Maximum call stack size exceeded" error.
|
|
28
|
+
|
|
29
|
+
### The Solution: `proxyCache`
|
|
30
|
+
|
|
31
|
+
The library uses `const proxyCache = new WeakMap()` to keep track of every object it has already turned into a Proxy.
|
|
32
|
+
|
|
33
|
+
1. **Checking the Map**: At the very start of the `reactive()` function, the code checks: `if (proxyCache.has(obj)) return proxyCache.get(obj);`.
|
|
34
|
+
|
|
35
|
+
2. **Storing the Result**: If it's a new object, the code creates the Proxy and then immediately saves it: `proxyCache.set(obj, p);`.
|
|
36
|
+
|
|
37
|
+
3. **The Result**: If you access `state.user` 100 times, you get the exact same Proxy instance every time. It ensures that `p1 === p2` is always `true`.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Why a `WeakMap`?
|
|
41
|
+
|
|
42
|
+
This is a critical "expert-level" choice.
|
|
43
|
+
|
|
44
|
+
- A regular `Map` holds a "strong reference" to its keys. If you deleted a piece of data from your state, but that data was still a key in a regular `Map`, the browser could never delete it from memory.
|
|
45
|
+
|
|
46
|
+
- Because `proxyCache` is a `WeakMap`, as soon as your component is destroyed and the original object is no longer needed, the browser’s Garbage Collector can automatically wipe it from the cache.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Summary of Logic:
|
|
50
|
+
|
|
51
|
+
- **Step 1:** Request to make `obj` reactive.
|
|
52
|
+
|
|
53
|
+
- **Step 2:** Check `proxyCache`. Found it? Return the existing Proxy.
|
|
54
|
+
|
|
55
|
+
- **Step 3:** Not found? Create a new Proxy.
|
|
56
|
+
|
|
57
|
+
- **Step 4:** Store `obj -> Proxy` in `proxyCache`.
|
|
58
|
+
|
|
59
|
+
- **Step 5:** Return the new Proxy.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# In the beginning - `Lego.init()`
|
|
2
|
+
|
|
3
|
+
The `init()` function is the heart of LegoDOM. It orchestrates the entire initialization process, setting up the observer, connecting the global state, and processing existing components.
|
|
4
|
+
|
|
5
|
+
## The LegoDOM Entry Point
|
|
6
|
+
|
|
7
|
+
When your HTML finishes loading
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
if (typeof window !== 'undefined') {
|
|
11
|
+
document.addEventListener('DOMContentLoaded', Lego.init);
|
|
12
|
+
window.Lego = Lego;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
LegoDOM executes `Lego.init()`. This is the orchestration phase where the library sets up its "eyes" (the observer) and connects the global state to the page.
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
return {
|
|
21
|
+
init: () => {
|
|
22
|
+
document.querySelectorAll('template[b-id]').forEach(t => {
|
|
23
|
+
registry[t.getAttribute('b-id')] = t;
|
|
24
|
+
});
|
|
25
|
+
const observer = new MutationObserver(m => m.forEach(r => {
|
|
26
|
+
r.addedNodes.forEach(n => n.nodeType === Node.ELEMENT_NODE && snap(n));
|
|
27
|
+
r.removedNodes.forEach(n => n.nodeType === Node.ELEMENT_NODE && unsnap(n));
|
|
28
|
+
}));
|
|
29
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
30
|
+
|
|
31
|
+
snap(document.body);
|
|
32
|
+
bind(document.body, { _studs: Lego.globals, _data: { bound: false } });
|
|
33
|
+
|
|
34
|
+
if (routes.length > 0) {
|
|
35
|
+
// Smart History: Restore surgical targets on Back button
|
|
36
|
+
window.addEventListener('popstate', (event) => {
|
|
37
|
+
const targets = event.state?.legoTargets || null;
|
|
38
|
+
_matchRoute(targets);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
document.addEventListener('click', e => {
|
|
42
|
+
const link = e.target.closest('a[b-link]');
|
|
43
|
+
if (link) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
const href = link.getAttribute('href');
|
|
46
|
+
const targetAttr = link.getAttribute('b-target');
|
|
47
|
+
const targets = targetAttr ? targetAttr.split(' ') : [];
|
|
48
|
+
|
|
49
|
+
// Execute navigation via $go logic
|
|
50
|
+
Lego.globals.$go(href, ...targets);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
_matchRoute();
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
// ... other functions ...
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 1. Template Registration
|
|
62
|
+
|
|
63
|
+
The first thing `init()` does is look for blueprints you've already defined in your HTML.
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
document.querySelectorAll('template[b-id]').forEach(t => {
|
|
67
|
+
registry[t.getAttribute('b-id')] = t;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
It scans the entire document for any `<template>` tag that has a `b-id` attribute. It then adds these to the `registry` object we discussed in [Topic 2](/contributing/02-registry). This allows you to define components directly in your HTML file without writing a single line of JavaScript.
|
|
73
|
+
|
|
74
|
+
### 2. Setting up the "Eyes" (`MutationObserver`)
|
|
75
|
+
|
|
76
|
+
This is the most critical part of the initialization. The library creates a `MutationObserver`.
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
const observer = new MutationObserver(m => m.forEach(r => {
|
|
80
|
+
r.addedNodes.forEach(n => n.nodeType === Node.ELEMENT_NODE && snap(n));
|
|
81
|
+
r.removedNodes.forEach(n => n.nodeType === Node.ELEMENT_NODE && unsnap(n));
|
|
82
|
+
}));
|
|
83
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- **What it does**: It watches the `document.body` for any changes to the HTML structure.
|
|
87
|
+
|
|
88
|
+
- **The Config**: It observes `{ childList: true, subtree: true }`. This means it sees if an element is added to the body, or if an element is added deep inside another element.
|
|
89
|
+
|
|
90
|
+
- **The Reaction**:
|
|
91
|
+
|
|
92
|
+
- If a new node is **added**, it calls `snap(n)` (which we will cover soon) to turn that raw HTML into a living component.
|
|
93
|
+
|
|
94
|
+
- If a node is **removed**, it calls `unsnap(n)` to clean up memory and fire lifecycle hooks.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
### 3. The "First Snap"
|
|
98
|
+
|
|
99
|
+
After setting up the observer, it calls `snap(document.body)`.
|
|
100
|
+
|
|
101
|
+
- **Why?** The observer only sees _new_ things being added. It doesn't see what was already there when the page loaded.
|
|
102
|
+
|
|
103
|
+
- By calling `snap` on the body, the library manually processes every custom component that was present in the initial HTML.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### 4. Global Data Binding
|
|
107
|
+
|
|
108
|
+
Finally, it binds the `Lego.globals` state to the `document.body`.
|
|
109
|
+
|
|
110
|
+
JavaScript
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
bind(document.body, { _studs: Lego.globals, _data: { bound: false } });
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This is a clever move: it treats the entire website body as if it were a giant component. This allows you to use reactive data in your "Light DOM" (the regular HTML outside of components) by referencing values stored in `Lego.globals`.
|
|
118
|
+
|
|
119
|
+
### 5. Routing Initialization
|
|
120
|
+
|
|
121
|
+
If you have defined any routes using `Lego.route()`, the `init` function sets up global click listeners and history management so that links with the `b-link` attribute don't refresh the page but instead perform a "surgical" swap of content.
|
|
122
|
+
|
|
123
|
+
----------
|
|
124
|
+
|
|
125
|
+
**Summary of `init()`:** It finds templates, starts a "watchdog" for new elements, processes existing elements, and enables global data.
|