turbo-web 4.2.6 → 4.2.7
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 +132 -0
- package/dist/turbo.js +56 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# TURBO-WEB FRAMEWORK
|
|
2
|
+
|
|
3
|
+
## I. OVERVIEW
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Lightweight JavaScript framework based on a book "Build a **Frontend Web Framework** (from scratch)" by **Angel Sola Orbaiceta** with additional features sprinkled on top.
|
|
8
|
+
|
|
9
|
+
[//]: # (Installation instructions.)
|
|
10
|
+
## II. INSTALLATION
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
There are 4 ways to utilize this framework: **NPM installation**, **SETUP script** (NODE), **locally** and through **CDN**.
|
|
14
|
+
### (1) NPM Installation
|
|
15
|
+
To install Turbo-Web framework, run in your terminal ```npm install turbo-web```
|
|
16
|
+
|
|
17
|
+
### (2) NODE initialization script
|
|
18
|
+
To install the framework with pre-configured templates using CLI, run in your terminal ```npx turbo-web PROJECT_NAME```
|
|
19
|
+
This will create a starting kit: project directory, package.json, index.html + src/ with router.js, main.js and store.js
|
|
20
|
+
|
|
21
|
+
// script exists under bin/turbo-charge.js
|
|
22
|
+
|
|
23
|
+
### (3) Locally
|
|
24
|
+
To import the framework locally - you must include the bundled file located in ```./framework/runtime/dist/turbo.js``` as imports
|
|
25
|
+
|
|
26
|
+
// This file can be generated using script ```npm run build```
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
|
|
30
|
+
```'import {createApp, defineComponent, h, hFragment,} from '../../../framework/runtime/dist/turbo.js'```
|
|
31
|
+
|
|
32
|
+
### (4) Using CDN (Content Delivery Network)
|
|
33
|
+
You can import any version of the framework directly into your JS file:
|
|
34
|
+
|
|
35
|
+
```import {createApp, defineComponent, h, hFragment,} from 'https://unpkg.com/turbo-web'```
|
|
36
|
+
|
|
37
|
+
CDN Version control ( version 1x.2x.3x):
|
|
38
|
+
1. x MAJOR: @1 or @^1.0.0 - fetches latest compatible minor and path release
|
|
39
|
+
2. x MINOR: @1.2 or @~1.2.0 - fetches latest compatible patch release of a certain MINOR version 0.X.0
|
|
40
|
+
3. x PATCH: @1.2.3 - fetches a specific build with full version control
|
|
41
|
+
|
|
42
|
+
``````import ... from 'https://unpkg.com/turbo-web@2'`````` - will import latest minor + patch version of 2.X.X.
|
|
43
|
+
|
|
44
|
+
[//]: # (A "Getting Started" guide.)
|
|
45
|
+
## III. GETTING STARTED
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### [1] IMPORT
|
|
50
|
+
1. (2.) NPM/NODE script: Import from packages:
|
|
51
|
+
> import { createApp, defineComponent, h, hFragment } from 'turbo-web'
|
|
52
|
+
|
|
53
|
+
3. (4.) Locally/CDN: You must use relative paths (./ or ../):
|
|
54
|
+
> import { createApp, defineComponent, h, hFragment } from './framework/runtime/index.js'
|
|
55
|
+
|
|
56
|
+
### [2] LAUNCH
|
|
57
|
+
|
|
58
|
+
You can run server using ```npm run serve:examples```.
|
|
59
|
+
|
|
60
|
+
If you change code inside **examples/** make sure to do a hard reset for changes to apply:
|
|
61
|
+
> F12 -> right-click on the REFRESH button -> Empty Cache & Hard Reload OR ( Ctrl + Shift + R )
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
[//]: # (A detailed explanation of each feature with code examples.)
|
|
65
|
+
## IV. FEATURES
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
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"
|
|
71
|
+
|
|
72
|
+
### [CORE] Feature 2: Virtual DOM
|
|
73
|
+
Inversed Control principle - the user doesn't need to manipulate the DOM directly, the framework creates a lightweight
|
|
74
|
+
JavaScript object tree that represents the UI.
|
|
75
|
+
|
|
76
|
+
### [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).
|
|
78
|
+
|
|
79
|
+
### [CORE] Feature 4: Components
|
|
80
|
+
Encapsulation & Reusability principles: making UI building blocks with better combined hierarchical tree structure.
|
|
81
|
+
|
|
82
|
+
### Feature 5: Client-Side Router (#hash-based)
|
|
83
|
+
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
|
+
// router.js, route-matchers.js
|
|
85
|
+
|
|
86
|
+
### 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
|
+
...
|
|
89
|
+
|
|
90
|
+
### Feature 7: Persistent Storage
|
|
91
|
+
Single Source of Truth principle: App's state is persistent between sessions.
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
### Feature X: Node-based Initialization through CLI
|
|
97
|
+
Scaffolding setup script that creates base structure with required templates for the App.
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
[//]: # (Best practices and guidelines for building applications with the framework.)
|
|
101
|
+
## V. BEST PRACTICES
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Routing
|
|
106
|
+
Make sure to ...
|
|
107
|
+
|
|
108
|
+
```const routes = [{ path: '/', component: Home }, {}, {}]```
|
|
109
|
+
```const router = new HashRouter(routes)```
|
|
110
|
+
```await router.init()```
|
|
111
|
+
```#isInitialized```
|
|
112
|
+
|
|
113
|
+
### Components
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
### Centralized Store
|
|
117
|
+
Must be initialized in a separate file (store.js).
|
|
118
|
+
```
|
|
119
|
+
export const store = new Store({
|
|
120
|
+
state: {
|
|
121
|
+
count: 0,
|
|
122
|
+
user: null
|
|
123
|
+
},
|
|
124
|
+
mutations: {
|
|
125
|
+
increment(state, payload = 1) {
|
|
126
|
+
state.count += payload
|
|
127
|
+
},
|
|
128
|
+
setUser(state, user) {
|
|
129
|
+
state.user = user
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
package/dist/turbo.js
CHANGED
|
@@ -1193,4 +1193,59 @@ const RouterOutlet = defineComponent({
|
|
|
1193
1193
|
},
|
|
1194
1194
|
});
|
|
1195
1195
|
|
|
1196
|
-
|
|
1196
|
+
class Store {
|
|
1197
|
+
#state = {}
|
|
1198
|
+
#initialState = {}
|
|
1199
|
+
#mutations = {}
|
|
1200
|
+
#actions = {}
|
|
1201
|
+
#dispatcher = new Dispatcher()
|
|
1202
|
+
#storageKey = 'turbo_state'
|
|
1203
|
+
constructor({ state = {}, mutations = {}, actions = {} }) {
|
|
1204
|
+
this.#initialState = structuredClone(state);
|
|
1205
|
+
const saved = localStorage.getItem(this.#storageKey);
|
|
1206
|
+
this.#state = saved ? JSON.parse(saved) : state;
|
|
1207
|
+
this.#mutations = mutations;
|
|
1208
|
+
this.#actions = actions;
|
|
1209
|
+
window.addEventListener('storage', (event) => {
|
|
1210
|
+
if (event.key === this.#storageKey && event.newValue) {
|
|
1211
|
+
this.#state = JSON.parse(event.newValue);
|
|
1212
|
+
this.#dispatcher.dispatch('state-change', this.state);
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
get state() {
|
|
1217
|
+
return structuredClone(this.#state)
|
|
1218
|
+
}
|
|
1219
|
+
commit(mutationName, payload) {
|
|
1220
|
+
const mutation = this.#mutations[mutationName];
|
|
1221
|
+
if (typeof mutation !== 'function') {
|
|
1222
|
+
console.warn(`[Store] Mutation "${mutationName}" does not exist.`);
|
|
1223
|
+
return
|
|
1224
|
+
}
|
|
1225
|
+
mutation(this.#state, payload);
|
|
1226
|
+
localStorage.setItem(this.#storageKey, JSON.stringify(this.#state));
|
|
1227
|
+
this.#dispatcher.dispatch('state-change', this.state);
|
|
1228
|
+
}
|
|
1229
|
+
async dispatch(actionName, payload) {
|
|
1230
|
+
const action = this.#actions[actionName];
|
|
1231
|
+
if (typeof action !== 'function') {
|
|
1232
|
+
console.warn(`[Store] Action "${actionName}" does not exist.`);
|
|
1233
|
+
return
|
|
1234
|
+
}
|
|
1235
|
+
const context = {
|
|
1236
|
+
commit: this.commit.bind(this),
|
|
1237
|
+
state: this.state
|
|
1238
|
+
};
|
|
1239
|
+
return await action(context, payload)
|
|
1240
|
+
}
|
|
1241
|
+
subscribe(handler) {
|
|
1242
|
+
return this.#dispatcher.subscribe('state-change', handler)
|
|
1243
|
+
}
|
|
1244
|
+
clear() {
|
|
1245
|
+
localStorage.removeItem(this.#storageKey);
|
|
1246
|
+
this.#state = structuredClone(this.#initialState);
|
|
1247
|
+
this.#dispatcher.dispatch('state-change', this.state);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
export { DOM_TYPES, HashRouter, RouterLink, RouterOutlet, Store, createApp, defineComponent, h, hFragment, hSlot, hString, nextTick };
|