Skip to content

Commit

Permalink
docs: mental model, qrl, resumable, reactivity
Browse files Browse the repository at this point in the history
  • Loading branch information
mhevery committed Apr 21, 2021
1 parent 321df04 commit b487022
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 15 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `Q-oot` `/kyo͞ot/` sub-framework
# `Q-oot` `/kyo͞ot/` DOM-Centric, Resumable Web-App Framework

An Open-Source sub-framework designed with a focus on server-side-rendering, lazy-loading, and styling/animation. (sub-framework implies that it is designed to work with the existing framework you love)
An Open-Source framework designed for best possible [time to interactive](https://web.dev/interactive/), by focusing on [resumability](./docs/RESUMABLE.md) of server-side-rendering of HTML, and [fine-grained lazy-loading](./docs/LAZY_LOADING.md) of code.

---

Expand All @@ -10,4 +10,6 @@ An Open-Source sub-framework designed with a focus on server-side-rendering, laz

## Getting Started

Visit [integration](./integration) folder for guided tours of Qoot to learn how it works.
- Visit [integration](./integration) folder for guided tours of Qoot to learn how it works.
- Understand the difference between [resumable and replayable](./docs/RESUMABLE.md) applications.
- Learn about Qoot's high level [mental model](./docs/MENTAL_MODEL.md).
17 changes: 17 additions & 0 deletions docs/BOOTSTRAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Bootstrapping Qoot

Qoot is designed to have the smallest amount of code which needs to be run on the client to bootstrap the application. The size of bootstrap code has direct impact on application [time to interactive](https://web.dev/interactive/).

The bootstrapping code is known as `qootloader` and is tiny (around 500 bytes minified.) and can be bootstrap in sub one millisecond time. The purpose of `qootloader` is to set up global browser event listeners for your application, such as `click` event. If event fires, the `qootloader` looks for corresponding `on:click` attribute which contains a [QRL](./QRL.md). The QRL tells `qootloader` from where the event handler for the `click` event should be downloaded.

## Bootstraping

To bootstrap Qoot application one needs to insert this into the page:

```html
<script src="/qootloader.min.js" async events="click;dblclick;keyup"></script>
```

- `src`: points to the location where the `qootloader.js` can be found.
- `async`: Tells the browser that it is not necessary to wait for the `qootloader` to load and execute before continuing to pars the HTML. Application will work without this, but adding `async` will slightly improve the startup time.
- `events`: An optional list of events which the `qootloader` should listen too. If the list is not provided the `qootloader` will listen to all of the browser events. While this may be convenient, it will result in longer bootstrap times. Listing the events is strongly proffered.
22 changes: 10 additions & 12 deletions CHEATSHEET.md → docs/CHEATSHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ Special attributes in the HTML
2. Consists of a key as seen in a table
3. Have a value that evaluates according to the table.

| Syntax | Meaning |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `:` | Marker which signifies that the element has injector |
| `on:<name>="<QRL>"` | If `event` is detected than `import(url)` and call `default` function |
| `bind:<key>="attr"` | Data with `key` is bound to `attr`. NOTE: Key/value is reversed so that it is easy to query for all of the data bindings by `key` |
| `::name=QRL` | Service definition. `QRL` points to the `Service`. |
| `::=QRL` | Component render `QRL` points to the template. |
| `key:=JSON` | Serialized data for `key` `Service`. |
| `:.=JSON` | Component state. |

## QRL

| Syntax | Meaning |
Expand All @@ -14,15 +24,3 @@ Special attributes in the HTML
| `key:/path` | URL => results in an import of code from the URL <div> `(await import(CONFIG.protocol[key] + '/path.js')).default(injector) `</div> |
| `./path.foo` | URL => results in an import of code from the URL <div> `(await import('./path.js')).foo(injector) `</div> |
| `./path?key=value` | URL => results in an import of code from the URL <div> `(await import('./path.js')).default(injector) `</div> |

## Events

| Syntax | Meaning |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `:` | Marker which signifies that the element has data which needs to be serialized on dehydration |
| `on:<name>="<QRL>"` | If `event` is detected than `import(url)` and call `default` function |
| `bind:<key>="attr"` | Data with `key` is bound to `attr`. NOTE: Key/value is reversed so that it is easy to query for all of the data bindings by `key` |
| `::name=QRL` | Service definition. `QRL` points to the template. |
| `::=QRL` | Component render `QRL` points to the template. |
| `key:=JSON` | Serialized data for `key` |
| `:.=JSON` | Component state. |
Empty file added docs/HOST_ELEMENT.md
Empty file.
60 changes: 60 additions & 0 deletions docs/LAZY_LOADING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Fine-Grained Lazy-Loading

After [resumability](./RESUMABLE.md), fine-grained lazy-loading is next important goal to achieve low [time to interactive](https://web.dev/interactive/) score.

## Frameworks lack lazy-loading primitives

Current generation of frameworks lack lazy loading primitives. Many frameworks have ways of lazy loading routes or small section of templates, but they lack lazy loading at the core level of the framework and with sufficiently fine granularity.

A good way to look at it is that if the browser downloads code, than close to 100% of that code should to be executed. Most frameworks today will download big chunks for code only to execute a small portion of it. Most of the unexecuted code will reside in event handlers and their dependencies.

Frameworks also tend to have synchronous APIs making it hard for the developers to insert lazy-loading asynchronous boundaries.

## Fined-grained lazy-loading as a core primitive

Qoot takes fine-grained lazy-loading to the extreme. Almost all Qoot APIs can accept point of asynchronicity so that lazy-loading can be inserted in case it is needed. That is not to say that you should make everything lazy-loaded as there are tradeoffs, but rather that the framework allows to make just about anything lazy-loaded in case the need arieses.

Lazy loading directly impacts the amount of code which needs to be downloaded, parsed and executed resulting in improved [time to interactive](https://web.dev/interactive/) score.

## Lazy loading event handlers

A biggest place where fine-grained lazy-loading can be applied is in the area of event handlers. Applications typically have many event handlers to support user interaction. Most of these event handlers do not get exercised in tha typical user interactions, but must be downloaded just-in-case. The event handlers are in form of closures which close over the dependencies of the event handler further increasing the download size. Additionally most frameworks require that a templates be rendered for the framework to attach the event handlers to the DOM.

In practice this means that [heap-centric](./RESUMABLE.md) frameworks must execute all of the templates so that they can collect the event handlers and attach them to the DOM, just in case the user will perform interaction. A developer could place dynamic imports in the event handlers, but in practice this is complicated because event bubbling is synchronous making composing event handlers hard. It also means that a heap-centric framework must download and execute all of the templates currently visible to the user. All of these complications slow down [time to interactive](https://web.dev/interactive/) and do not directly help the developer to do the right thing.

Qoot stores event handlers as strings annotation in the DOM. This has several advantages:

- No template needs to be downloaded and rendered in order for the framework to know where the event handlers need to be registered.
- No event handler code needs to be downloaded before it is needed.
- Global event handler registration can be performed saving on event registration during [bootstrap](./BOOTSTRAP.md).
- All event handlers automatically have a lazy loaded boundaries. This guides the developer to do the performant thing by default.

## Lazy loading components

Current generation of frameworks can't render individual components in isolation, the result is that rendering requires all of the component's template to be present and executed on each rendering. (And along with templates the corresponding event handlers.)

To explore this more in depth imagine following render tree:

```html
<parent>
<middle>
<child></child>
</middle>
</parent>
```

Let's say that framework has determined that only `<middle>` component needs to be re-rendered. Current generation of frameworks don't have a way to re-render `<middle>` but not to recurse to `<child>`. Lack of this capability means that re-hydration in the current frameworks starts with a component and forces all child components. This causes unnecessary downloading, executing and reconciliation. We can say that the current approach causes child-component-coupling.

But reverse is also true. Let's say that `<middle>` has a button with a `click` handler. Clicking the button will cause the application to change state (which may be store in redux pattern.) Current set of frameworks don't directly track state and so there is no way of knowing which other components need to be invalidated due to execution of the event handler. The only thing the framework can do is to re-render the whole application from the root just-in-case there is a component which depends on the state. We can say that the current approach causes parent-component-coupling.

> Yes, there are some frameworks which track data-flow through subscriptions (reactive frameworks), and such frameworks would know which exact component needs to be updated. Problem is that setting up these subscriptions necessitates the creation of closures which in turn need references to the components. Creation of these subscriptions forces all of the components to be materialized negating any benefits. (see: [Qoot reactivity](./REACTIVITY.md))
Child and parent component-coupling means that in practice all of the currently visible application must be downloaded and present for the application to be user interactive. As applications get bigger and more complicated this requirements means tha the application startup gets slower over time.

Qoot focuses on breaking these dependencies so that neither child nor parent component-coupling are an issue resulting in a rendering model which does not require that templates or event handlers are downloaded until they are needed.

Breaking child-component-coupling requires that the Qoot's rendering system understands where component boundaries are, and can conditionally enter child components based on dirty flag of the component. This means that the rendering pipeline of Qoot is asynchronous, and understands how to conditionally download component templates on as needed basis. (In contrast to current generation frameworks which have synchronous rendering pipelines, which complicates async template loading, and the rendering always enters child components.)

## Out of order component re-hydration

A key goal for Qoot is to allow components to re-hydrate and re-render only when it is necessary. The implication of this is that at times components can re-hydrate out of parent/child order. A non-obvious implication of this is that the framework rendering pipeline needs to understand how to render component from the root, skip non-hydrated components and continue with deep children.
Loading

0 comments on commit b487022

Please sign in to comment.