Skip to content

Commit

Permalink
feat: add architecture doc
Browse files Browse the repository at this point in the history
  • Loading branch information
sanyuan0704 committed Sep 18, 2022
1 parent 2bd354f commit d31999d
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
56 changes: 55 additions & 1 deletion docs/guide/islands-arch.md
@@ -1,3 +1,57 @@
# Islands architecture

TODO
The idea of "islands architecture" was first coined by Etsy's frontend architect [Katie Sylor-Miller](https://twitter.com/ksylor) in 2019, and expanded on in this [post](https://jasonformat.com/islands-architecture/) by Preact creator Jason Miller.

This kind of architecture is aim to solve the problem of the hydration need of MPA, which is deeply analyzed in [previous page](./spa-vs-mpa).

## What's it?

As the name suggests, we can image the whole page as a sea of static, and the interactive parts as islands.As the following picture shows:

![./assets/islands-arch.png](https://res.cloudinary.com/wedding-website/image/upload/v1596766231/islands-architecture-1.png)

Then the hydration process will only be executed on the islands, which will make the first page load performance and TTI(time to interactive) better because it only needs partial client script that is correspond to the interactive parts.

## How to implement it?

The implementation of this architecture includes three parts: `server runtime``build time` and `client runtime`.

### Basic Usage

Before introducing the architecture implementation, I think it is necessary to introduce how to use an island component.In island.js, the usage is very simple, just like the following code:

```js
import { Aside } from './Aside.tsx';

export function Layout() {
return <Aside __island />;
}
```

You only need to add a `__island` prop to the component anywhere you use it, and then the component will automatically be identified as a island component.Then, island.js will only inject the client script of this component and its props when it is rendered on the client.

### Internal Implement

**1. Server runtime**.The server runtime is responsible for the server-side rendering of the islands, and it is also the core of the islands architecture.The main task of the server runtime is to collect the islands information in `renderToString` process.

In `Island.js`, it hijack the react/jsx-runtime's jsx function and collect the islands information when `__island` prop is found in the component.

**2. Build time**.The build time is responsible for generating the client script of the islands and injecting it into the html.In build time, Island.js will generate three bundle:

- Server bundle, for rendering to html string in server.
- Client hydration bundle, for hydrating the islands in client.
- Islands bundle, for registering the islands components and props on window object.

Island.js will combine all island components into a virtual module and bundle them.In the virtual module, all of the island components will be hang on `window` object.So in client hydration bundle, we can get the island components from `window` object and hydrate them separately.

**3. Client runtime**.The client runtime is responsible for hydrating the islands in browser to make them interactive.

There are the some relevant code in repository:

[island-jsx-runtime.js](https://github.com/sanyuan0704/island.js/blob/master/src/runtime/island-jsx-runtime.js): The jsx runtime will collect the islands information when `__island` prop is found in the component, served as the server runtime.

[babel-plugin-island](https://github.com/sanyuan0704/island.js/blob/master/src/node/babel-plugin-island.ts): The babel plugin will transform the `__island` prop to `__island=${islandAbsoluteFilePath}` prop, so in build time, bundler will find the island component file path.

[SSGBuilder](https://github.com/sanyuan0704/island.js/blob/master/src/node/build.ts): The complete build time implement.

[client-entry](https://github.com/sanyuan0704/island.js/blob/master/src/runtime/client-entry.tsx#L50): The client runtime will hydrate the islands in browser to make them interactive.
64 changes: 62 additions & 2 deletions docs/guide/spa-vs-mpa.md
@@ -1,3 +1,63 @@
# SPA vs MPA
# MPA vs SPA

TODO
Understanding the tradeoffs between Multi-Page Application (MPA) and Single-Page Application (SPA) architecture is important to understand how island.js works and how to use it in proper way.

## What are MPA and SPA ?

MPA and SPA are two different ways to build web applications.

`A Multi-Page Application(MPA)` is a web application that loads multiple HTML pages from server.Each page is independent of each other and has its own URL. When you click the link to navigate to another page, the browser will send a request to the server and load the new page.For example, the traditional template technology like in JSP、Python Django、PHP Laravel and so on are all MPA framework.

`A Single-Page Application(SPA)` is a web application that loads a single HTML page that loads in the user's browser and renders HTML locally.The page is loaded once and then dynamically updated via JavaScript as the user interacts with the app. For example, Next.js, Nuxt、CRA(create-react-app) are all SPA framework.

Fortunately, Island.js support both MPA and SPA architecture.But how do we choose between them? Let's take a look at the pros and cons of each.

## Comparison

### Performance

In MPA, the server will response the complete HTML page to browser, but SPA need to execute javascript to render the page.So first load performance of page in MPA is better than SPA, which is important for a content-focused website.

However, on the other hand, SPA has a better performance and experience on subsequent page loads. Because SPA only need to dynamically load the part of the page, instead of a whole page.And also, SPA won't reload the page when you navigate to another page, which is more friendly to the user.

### SEO

MPA is a good choice for SEO, because each page is independent of each other, while every page has its complete HTML content, so it is easy to optimize the page for search engines.

SPA is not good for SEO, because the content is dynamically generated by JavaScript, so it is difficult for search engines to crawl the content.

### Route

MPA does not need to consider the route in browser, because each page has its own URL, and the server will accept the request and dispatch it to the corresponding page.

But SPA need to import route module to handle the route in browser(e.g. base on history API), because the page is loaded once and then dynamically updated via JavaScript as the user interacts with the app.

### State Management

Another complex part of SPA is state management. Because in SPA the state of the page is maintained by JavaScript and should be managed between different pages.So there are lots of state management libraries like Redux、Vuex、Mobx、Valtio and so on.In fact, sometimes it can be a little bit difficult to manage the state in SPA.

MPA is not so complex, because the state of the page is maintained by the server, and the state of the page is independent of each other, so it is not necessary to manage the state between different pages in browser.

## Tradeoff

So you can see, MPA make the fast first page load performance, but SPA make the better subsequent page loads performance and experience, with the cost of more complex route and state management.Both of them cannot meet all the requirements of the project, so we need to make a tradeoff, between the performance and the complexity.

However, is there a way to combine the two ways?

The answer is yes.In fact, many framework use MPA for first page and then SPA for subsequent page loads, such as Next.js, Nuxt and so on.

How are they done?

Firstly, the framework will render the page on the server side(be called `SSR` or `SSG`), and then send the complete HTML page to the browser, so the first page load performance is good.It's important that the client script for SPA is injected in the HTML page at the same time.So the result is that when the page is loaded, the client script will be executed to rerender the page and manage route and state via JavaScript, as well as binding the DOM event to make the page interactive.

The so-called rerender and binging DOM event process is also called `hydration`.

So in a word, MPA and SPA are not mutually exclusive, we can combine them with the cost of the hydration runtime execution and the network io caused by client script.In addition, there are also some other tradeoff.

## Motivation

With these cost after combining MPA and SPA, i have to say sometimes combine them is not a good choice, especially for the content-heavy website, because the hydration runtime execution and the network io caused by client script will make the first page load performance and TTI(time to interactive) worse.

So island.js recommended to use MPA for content-focused website, which is a default internal strategy.Then here comes the problem: MPA only generate static HTML page, how to ensure the hydration process if there are some interactive parts in the page?

That's the motivation of island architecture.You will find the answer in the next article: [Islands Architecture](./islands-arch)
Empty file removed docs/guide/what-is-island-js.md
Empty file.
5 changes: 5 additions & 0 deletions src/theme-default/logic/useAsideAnchor.ts
Expand Up @@ -35,6 +35,7 @@ export function useAsideAnchor(
}
// Util function to set dom ref after determining the active link
const activate = (links: NodeListOf<HTMLAnchorElement>, index: number) => {
console.log('current active: ', index);
if (prevActiveLinkRef.current) {
prevActiveLinkRef.current.classList.remove('aside-active');
}
Expand Down Expand Up @@ -71,6 +72,10 @@ export function useAsideAnchor(
const currentAnchorTop =
currentAnchor.parentElement!.offsetTop - NAV_HEIGHT;

if (i === 0 && scrollTop === 0) {
activate(links, 0);
}

if (!nextAnchor) {
activate(links, i);
break;
Expand Down

0 comments on commit d31999d

Please sign in to comment.