turbo-web
v4.5.0
Published
---
Readme
TURBO-WEB FRAMEWORK
I. OVERVIEW
Lightweight JavaScript framework based on a book "Build a Frontend Web Framework (from scratch)" by Angel Sola Orbaiceta with additional features sprinkled on top.
II. INSTALLATION
There are 4 ways to utilize this framework: NPM installation, SETUP script (NODE), locally and through CDN.
(1) NPM Installation
To install Turbo-Web framework, run in your terminal npm install turbo-web
(2) NODE initialization script
To install the framework with pre-configured templates using CLI, run in your terminal npx turbo-web PROJECT_NAME
This will create a starting kit: project directory, package.json, index.html + src/ with router.js, main.js and store.js
// script exists under bin/turbo-charge.js
(3) Locally
To import the framework locally - you must include the bundled file located in ./framework/runtime/dist/turbo.js as imports
// This file can be generated using script npm run build
Example:
'import {createApp, defineComponent, h, hFragment,} from '../../../framework/runtime/dist/turbo.js'
(4) Using CDN (Content Delivery Network)
You can import any version of the framework directly into your JS file:
import {createApp, defineComponent, h, hFragment,} from 'https://unpkg.com/turbo-web'
CDN Version control ( version 1x.2x.3x):
- x MAJOR: @1 or @^1.0.0 - fetches latest compatible minor and path release
- x MINOR: @1.2 or @~1.2.0 - fetches latest compatible patch release of a certain MINOR version 0.X.0
- x PATCH: @1.2.3 - fetches a specific build with full version control
import ... from 'https://unpkg.com/turbo-web@2' - will import latest minor + patch version of 2.X.X.
III. GETTING STARTED
[1] IMPORT
- (2.) NPM installation & NODE script: Import from packages:
import { createApp, defineComponent, h, hFragment } from 'turbo-web'
- (4.) Locally & CDN: You must use relative paths (./ or ../):
import { createApp, defineComponent, h, hFragment } from './framework/runtime/index.js'
[2] LAUNCH
You can run server using npm run serve:examples.
If you change code inside examples/ make sure to do a hard reset for changes to apply:
F12 -> right-click on the REFRESH button -> Empty Cache & Hard Reload OR ( Ctrl + Shift + R )
IV. FEATURES
[CORE] Feature 1: Declarative UI
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"
component.js
[CORE] Feature 2: Virtual DOM
Inversed Control principle - the user doesn't need to manipulate the DOM directly, the framework creates a lightweight JavaScript object tree that represents the UI.
patch-dom.js
[CORE] Feature 3: Reconciliation Algorithm
Inversed Control principle - optimizing DOM manipulations by the process of diffing and patching only the required nodes (improves DOM re-rendering & re-painting).
patch-dom.js
[CORE] Feature 4: Components
Encapsulation & Reusability principles: making UI building blocks with better combined hierarchical tree structure.
// Events accept additional modificator .stop .prevent to stop event bubbling and prevent default browser behaviour.
component.js // building scheme: h(tagOrComponent, { attributes, class, style, on: events }, [children])
Feature 5: Client-Side Router (#hash-based)
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.
router.js, route-matchers.js
Feature 6: Centralized Store
Separation of Concerns & Single Source of Truth principles: Allows for a global state handling and eliminates the need for prop drilling.
store.js
Feature 7: Persistent Storage
Single Source of Truth principle: App's state is persistent between sessions.
store.js
Feature 8: built-in HTTP module.
Abstraction principle: simplifies development by abstracting imperative logic in HTTP request handling.
http.js
Feature X: Node-based Initialization through CLI
Scaffolding setup script that creates base structure with required templates for the App
Script: npx turbo-web ${PROJECT_NAME} creates "Project" & src directory + main.js, router.js, store.js + package.json
V. BEST PRACTICES
[1] Router
Define Routes with Guards - Use the beforeEnter guard to protect routes (e.g., authentication).
The router supports asynchronous guards that can either return a boolean or redirect to a different path string
beforeEnter: async (from, to, params, query) => {
const isAuthenticated = localStorage.getItem('auth_token');
if (!isAuthenticated) return '/login'; // Redirects to login
return true; // allows navigation
}[2] RouterLink
Always use the built-in RouterLink component instead of standard anchor tags (<a href="...">) for internal navigation.
export const Navbar = defineComponent({
render() {
return h('nav', {}, [
h(RouterLink, { to: '/' }, ['Home']),
h(RouterLink, { to: '/dashboard' }, ['Dashboard'])
]);
}
});[3] Components
Never mutate this.state directly. Always use this.updateState() to trigger the virtual DOM patching process.
State initialization should be done via the state() function
increment() {
// Correct: Triggers VDOM diffing and patching
this.updateState({ count: this.state.count + 1 });
// Incorrect: Will not update the UI
// this.state.count++;
}[4] Centralized Store
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.
mutations: {
setLoading(state, status) {
state.isLoading = status; // Synchronous
},
setUsers(state, users) {
state.users = users; // Synchronous
}
},
actions: {
async fetchUsers({ commit, state }) {
commit('setLoading', true);
// Async operation
const response = await fetch('https://api.example.com/users');
const data = await response.json();
commit('setUsers', data);
commit('setLoading', false);
}[5] Subscribe to Store Changes in Components
To make a component react to global state changes, subscribe to the store in onMounted and trigger a local re-render.
You must unsubscribe in
onUnmounted!!!.
onMounted() {
this.unsubscribe = this.appContext.store.subscribe(() => {
this.updateState({}); // Force re-render
});
this.appContext.store.dispatch('fetchUsers');
},
onUnmounted() {
if (this.unsubscribe) this.unsubscribe();
},[6] Event bubbling & default browser behaviour override
It is possible to prevent both by passing additional modificators with functions.
on: { 'click.stop': () => this.addLog('Button with .stop Clicked') }
on: { 'click.prevent': () => this.addLog('Link with .prevent Clicked') }