turbo-web 4.2.10 → 4.3.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/README.md +91 -31
- package/bin/turbo-charge.js +36 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,34 +67,45 @@ If you change code inside **examples/** make sure to do a hard reset for changes
|
|
|
67
67
|
---
|
|
68
68
|
|
|
69
69
|
### [CORE] Feature 1: Declarative UI
|
|
70
|
-
Using declarative methods to define final UI instead of manually handling DOM elements. "What the UI looks like vs step-by-step imperative programming"
|
|
70
|
+
**Abstraction** principle: Using declarative methods to define final UI instead of manually handling DOM elements. "What the UI looks like vs step-by-step imperative programming"
|
|
71
|
+
|
|
72
|
+
```component.js```
|
|
71
73
|
|
|
72
74
|
### [CORE] Feature 2: Virtual DOM
|
|
73
|
-
Inversed Control principle - the user doesn't need to manipulate the DOM directly, the framework creates a lightweight
|
|
75
|
+
**Inversed Control** principle - the user doesn't need to manipulate the DOM directly, the framework creates a lightweight
|
|
74
76
|
JavaScript object tree that represents the UI.
|
|
75
77
|
|
|
78
|
+
```patch-dom.js```
|
|
79
|
+
|
|
76
80
|
### [CORE] Feature 3: Reconciliation Algorithm
|
|
77
|
-
Inversed Control principle - optimizing DOM manipulations by the process of diffing and patching only the required nodes (improves DOM re-rendering & re-painting).
|
|
81
|
+
**Inversed Control** principle - optimizing DOM manipulations by the process of diffing and patching only the required nodes (improves DOM re-rendering & re-painting).
|
|
78
82
|
|
|
83
|
+
```patch-dom.js```
|
|
79
84
|
### [CORE] Feature 4: Components
|
|
80
|
-
Encapsulation & Reusability principles: making UI building blocks with better combined hierarchical tree structure.
|
|
85
|
+
**Encapsulation & Reusability** principles: making UI building blocks with better combined hierarchical tree structure.
|
|
81
86
|
|
|
87
|
+
```component.js // building scheme: h(tagOrComponent, { attributes, class, style, on: events }, [children])```
|
|
82
88
|
### Feature 5: Client-Side Router (#hash-based)
|
|
83
89
|
Allows for the **SPA design** (Single Page Application) with route guards (fn: checkNavigation) and a common "catch-all route" (cases: route not found). Based on a hash part of the URL, implemented with regex pattern matching by simulating URL path, parameters and query as part of the hash fragment itself.
|
|
84
|
-
|
|
90
|
+
|
|
91
|
+
```router.js, route-matchers.js```
|
|
85
92
|
|
|
86
93
|
### Feature 6: Centralized Store
|
|
87
|
-
Separation of Concerns & Single Source of Truth principles: Allows for a global state handling and eliminates the need for prop drilling.
|
|
88
|
-
|
|
94
|
+
**Separation of Concerns** & **Single Source of Truth** principles: Allows for a global state handling and eliminates the need for prop drilling.
|
|
95
|
+
```store.js```
|
|
89
96
|
|
|
90
97
|
### Feature 7: Persistent Storage
|
|
91
|
-
Single Source of Truth principle: App's state is persistent between sessions.
|
|
92
|
-
|
|
98
|
+
**Single Source of Truth** principle: App's state is persistent between sessions.
|
|
99
|
+
|
|
100
|
+
```store.js```
|
|
101
|
+
|
|
102
|
+
### Feature 8: built-in HTTP module.
|
|
103
|
+
**Abstraction** principle: simplifies development by abstracting imperative logic in HTTP request handling.
|
|
93
104
|
|
|
94
|
-
|
|
105
|
+
```http.js```
|
|
95
106
|
|
|
96
107
|
### Feature X: Node-based Initialization through CLI
|
|
97
|
-
Scaffolding setup script that creates base structure with required templates for the App
|
|
108
|
+
Scaffolding setup script that creates base structure with required templates for the App
|
|
98
109
|
|
|
99
110
|
Script: ```npx turbo-web ${PROJECT_NAME}``` creates "Project" & src directory + main.js, router.js, store.js + package.json
|
|
100
111
|
|
|
@@ -103,31 +114,80 @@ Script: ```npx turbo-web ${PROJECT_NAME}``` creates "Project" & src directory +
|
|
|
103
114
|
|
|
104
115
|
---
|
|
105
116
|
|
|
106
|
-
###
|
|
117
|
+
### [1] Router
|
|
118
|
+
Define Routes with Guards - Use the beforeEnter guard to protect routes (e.g., authentication).
|
|
119
|
+
>The router supports asynchronous guards that can either return a boolean or redirect to a different path string
|
|
107
120
|
|
|
121
|
+
```
|
|
122
|
+
beforeEnter: async (from, to, params, query) => {
|
|
123
|
+
const isAuthenticated = localStorage.getItem('auth_token');
|
|
124
|
+
if (!isAuthenticated) return '/login'; // Redirects to login
|
|
125
|
+
return true; // allows navigation
|
|
126
|
+
}
|
|
127
|
+
```
|
|
108
128
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```await router.init()```
|
|
112
|
-
```#isInitialized```
|
|
129
|
+
### [2] RouterLink
|
|
130
|
+
Always use the built-in RouterLink component instead of standard anchor tags ```(<a href="...">)``` for internal navigation.
|
|
113
131
|
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
```
|
|
133
|
+
export const Navbar = defineComponent({
|
|
134
|
+
render() {
|
|
135
|
+
return h('nav', {}, [
|
|
136
|
+
h(RouterLink, { to: '/' }, ['Home']),
|
|
137
|
+
h(RouterLink, { to: '/dashboard' }, ['Dashboard'])
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
116
142
|
|
|
117
|
-
###
|
|
118
|
-
|
|
143
|
+
### [3] Components
|
|
144
|
+
Never mutate this.state directly. Always use this.updateState() to trigger the virtual DOM patching process.
|
|
145
|
+
> State initialization should be done via the state() function
|
|
119
146
|
```
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
147
|
+
increment() {
|
|
148
|
+
// Correct: Triggers VDOM diffing and patching
|
|
149
|
+
this.updateState({ count: this.state.count + 1 });
|
|
150
|
+
|
|
151
|
+
// Incorrect: Will not update the UI
|
|
152
|
+
// this.state.count++;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### [4] Centralized Store
|
|
157
|
+
Mutations must be strictly synchronous as they immediately write to localStorage and trigger state-change events. Use actions for asynchronous operations like HTTP requests, and have the action commit the mutation.
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
mutations: {
|
|
161
|
+
setLoading(state, status) {
|
|
162
|
+
state.isLoading = status; // Synchronous
|
|
124
163
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
164
|
+
setUsers(state, users) {
|
|
165
|
+
state.users = users; // Synchronous
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
actions: {
|
|
169
|
+
async fetchUsers({ commit, state }) {
|
|
170
|
+
commit('setLoading', true);
|
|
171
|
+
// Async operation
|
|
172
|
+
const response = await fetch('https://api.example.com/users');
|
|
173
|
+
const data = await response.json();
|
|
174
|
+
commit('setUsers', data);
|
|
175
|
+
commit('setLoading', false);
|
|
132
176
|
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### [5] Subscribe to Store Changes in Components
|
|
180
|
+
To make a component react to global state changes, subscribe to the store in onMounted and trigger a local re-render.
|
|
181
|
+
> You must unsubscribe in ```onUnmounted```!!!.
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
onMounted() {
|
|
185
|
+
this.unsubscribe = this.appContext.store.subscribe(() => {
|
|
186
|
+
this.updateState({}); // Force re-render
|
|
187
|
+
});
|
|
188
|
+
this.appContext.store.dispatch('fetchUsers');
|
|
189
|
+
},
|
|
190
|
+
onUnmounted() {
|
|
191
|
+
if (this.unsubscribe) this.unsubscribe();
|
|
192
|
+
},
|
|
133
193
|
```
|
package/bin/turbo-charge.js
CHANGED
|
@@ -41,17 +41,47 @@ fs.writeFileSync(
|
|
|
41
41
|
|
|
42
42
|
// defines the boilerplate file contents
|
|
43
43
|
const storeContent = `import { Store } from 'turbo-web';
|
|
44
|
-
export const appStore = new Store({ state: { count: 0 } });`;
|
|
45
44
|
|
|
46
|
-
const
|
|
47
|
-
|
|
45
|
+
export const appStore = new Store({
|
|
46
|
+
state: {
|
|
47
|
+
count: 0
|
|
48
|
+
},
|
|
49
|
+
mutations: {
|
|
50
|
+
increment(state) {
|
|
51
|
+
state.count++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});`;
|
|
55
|
+
|
|
56
|
+
const routerContent = `import { HashRouter, defineComponent, h } from 'turbo-web';
|
|
57
|
+
|
|
58
|
+
const Home = defineComponent({
|
|
59
|
+
render() {
|
|
60
|
+
return h('h2', {}, ['Welcome to Home Page']);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export const router = new HashRouter([
|
|
65
|
+
{ path: '/', component: Home }
|
|
66
|
+
]);`;
|
|
48
67
|
|
|
49
|
-
const mainContent = `import { createApp, h } from 'turbo-web';
|
|
68
|
+
const mainContent = `import { createApp, defineComponent, h, RouterOutlet } from 'turbo-web';
|
|
50
69
|
import { router } from './router.js';
|
|
51
70
|
import { appStore } from './store.js';
|
|
52
71
|
|
|
53
|
-
const App =
|
|
54
|
-
|
|
72
|
+
const App = defineComponent({
|
|
73
|
+
render() {
|
|
74
|
+
return h('div', { style: { padding: '20px', fontFamily: 'sans-serif' } }, [
|
|
75
|
+
h('h1', {}, ['Turbo Power! 🚀']),
|
|
76
|
+
h('hr'),
|
|
77
|
+
// RouterOutlet renders the components defined in router.js
|
|
78
|
+
h(RouterOutlet)
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const app = createApp(App, {}, { router, store: appStore });
|
|
84
|
+
app.mount(document.body);`;
|
|
55
85
|
|
|
56
86
|
// write the files to the user's new project directory
|
|
57
87
|
fs.writeFileSync(path.join(srcPath, 'store.js'), storeContent);
|