Skip to content

Commit

Permalink
docs: Create initial tutorial pages (QwikDev#678) (QwikDev#694)
Browse files Browse the repository at this point in the history
Co-authored-by: Miško Hevery <[email protected]>
  • Loading branch information
adamdbradley and mhevery authored Jun 29, 2022
1 parent 4fcb0ea commit 6102668
Show file tree
Hide file tree
Showing 126 changed files with 3,101 additions and 45 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ tsdoc-metadata.json
scripts/**/*
*.tsbuildinfo
packages/docs/src/pages/examples/**/*
packages/docs/src/pages/tutorial/**/*
vite.config.ts
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@
"Pinterest",
"Serializability"
],
"enableFiletypes": ["!mdx"]
"enableFiletypes": ["mdx"]
}
5 changes: 5 additions & 0 deletions packages/docs/src/pages/docs/INDEX
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
- [Content projection](components/projection.mdx)
- [Rendering](components/rendering.mdx)

## Cheat Sheet

- [Components](cheat/components.mdx)
- [Serialization](cheat/serialization.mdx)

## Concepts

- [Resumable](concepts/resumable.mdx)
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/pages/docs/advanced/containers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ A typical site is composed of two logical parts:
1. The navigation that tends to stay constant across many pages, and
2. The outlet, which is the part of the page that changes based on which route the user navigated to.

We can model the two parts as two navigation and outlet containers. When the user first navigates to a route, the server responds with HTML, which contains containers for both the navigation and the outlet. Once the user navigates to the second route, there are three ways to solve the navigation:
We can model the two parts as two navigation and outlet containers. When the user first navigates to a route, the server responds with HTML, that contains containers for both the navigation and the outlet. Once the user navigates to the second route, there are three ways to solve the navigation:

1. The simplistic approach is to make a full round trip and download an entirely new page. The main downside is that the application loses all of its states on the client.
1. The classical approach is to treat any further navigation in JavaScript. We replace the current outlet component with the new outlet component and let the new component render. The disadvantage is that we need to download and execute the JavaScript.
Expand Down
17 changes: 6 additions & 11 deletions packages/docs/src/pages/docs/advanced/optimizer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,15 @@ const Counter = component(qrl('./chunk-a.js', 'Counter_onMount'));
```tsx
export const Counter_onMount = () => {
const store = useStore({ count: 0 });
return qrl('./chunk-b.js', 'Counter_onRender', [store]);
};
```

`chunk-b.js`:

```tsx
const Counter_onRender = () => {
const [store] = useLexicalScope();
return (
<button onClickQrl={qrl('./chunk-c.js', 'Counter_onClick', [store])}>{store.count}</button>
<button onClickQrl={qrl('./chunk-b.js', 'Counter_onClick', [store])}>
{store.count}
</button>
);
};
```

`chunk-c.js`:
`chunk-b.js`:

```tsx
const Counter_onClick = () => {
Expand All @@ -55,6 +48,8 @@ const Counter_onClick = () => {
};
```

Notice that every occurence of `$` results in a new lazy loadable symbol.


# `$` and Optimizer Rules

Expand Down
98 changes: 98 additions & 0 deletions packages/docs/src/pages/docs/cheat/components.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
title: Components
---

# Common Component Patterns Cheat Sheet

## Declartion

```tsx
import {component$, useStore} from "@builder.io/qwik";

export const Greeter = component$(() => {
return <span>Hello World!</span>;
});
```

## Props

```tsx
import {component$, useStore} from "@builder.io/qwik";

interface GreeterProps {
salutation?: string;
name?: string;
}
export const Greeter = component$((props: GreeterProps) => {
const salutation = props.salutation || 'Hello';
const name = props.name || 'World';
return <span>{salutation} {name}!</span>;
});
```

### Event Props

Component props must be serializable, and therefore can not directly reffer to functions.

```tsx
import {component$, useStore, Qrl} from "@builder.io/qwik";

export const Parent = component$(() => {
return (
<MyButton doSomething$={() => console.log('Hello')}>
click
</MyButton>
);
});

interface MyButtonProps {
doSomethingQrl: QRL<() => void>
}
export const MyButton = component$((props: MyButtonProps) => {
return <button onClickQrl={props.doSomethingQrl}>click</button>;
});
```

## Events

## Watching for Changes

## Server
### Fetching Data

```tsx
import {component$, useStore, useServerMount$} from "@builder.io/qwik";

export const Greeter = component$(() => {
const store = useStore<{list: null|string[]}>({list: null});
useServerMount$(async () => {
store.list = await doSomethingToFetchDataOnServer();
});

return (
<ul>
{store.list && store.list.map((item) => <li>{item}</li>)}
</ul>
);
});
```

## Client
### Eagerly Executing Code

```tsx
import {component$, useStore, useClientEffet} from "@builder.io/qwik";

export const Greeter = component$(() => {
const store = useStore<{list: null|string[]}>({list: null});
useClientEffet$(async () => {
store.list = await doSomethingToFetchDataOnServer();
});

return (
<ul>
{store.list && store.list.map((item) => <li>{item}</li>)}
</ul>
);
});
```
53 changes: 53 additions & 0 deletions packages/docs/src/pages/docs/cheat/serialization.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: Serialization & Serialization Boundaries
---

# `$` Boundaries

## Rules

- Only serializable data can cross a `$` boundery.

## Serialization Boundery

A serialization boundery occures whenever you cross a lexical scope of a function that is converted into lazy loadable form. It is always denoted by `$(...)` (or `____$(...)`) See example:

```tsx
import {component$} from "@builder.io/qwik";

export const topLevel = Promise.resolve('nonserializable data');

export const Greeter = component$(() => {
// BEGIN component serialization boundery

// Referring to top level symbols that are exported is always allowed.
console.log(topLevel); // OK

const captureSerializable = 'serializable data';
const captureNonSerializable = Promise.resolve('nonserializable data');
return (
<button onClick$={() => {
// BEGIN onClick serialization boundery

// Referring to top level symbols that are exported is always allowed.
// Even if the value is non-serializable.
console.log(topLevel); // OK

// Capturing a non-toplevel variable is allowed only if:
// - declaed as `const`
// - is serializable (runtime error)
console.log(captureSerializable); // OK

// Reffering to captureNonSerializable will pass static analysis but
// will fail at runtime because Qwik does not know how to serilize it.
console.log(captureNonSerializable); // RUNTIME ERROR

// END onClick serialization boundery
}}>
click
</button>
);
// BEGIN component serialization boundery
});

```
4 changes: 2 additions & 2 deletions packages/docs/src/pages/docs/components/anatomy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ const Parent = component$(() => {
In the above example the Optimizer transforms the above to:

```tsx
const Child = component$(qrl('./chunk-a', 'Child_onMount'));
const Parent = component$(qrl('./chunk-b', 'Parent_onMount'));
const Child = componentQrl(qrl('./chunk-a', 'Child_onMount'));
const Parent = componentQrl(qrl('./chunk-b', 'Parent_onMount'));
const Parent_onMount = () => qrl('./chunk-c', 'Parent_onRender');
const Parent_onRender = () => (
<section>
Expand Down
6 changes: 3 additions & 3 deletions packages/docs/src/pages/docs/components/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ The main point here is that while the syntax of the events is consistent between

## Prevent default

Because of the async nature of Qwik, event's handler execution might be delayed because the implementation is not downloaded yet. This introduces a problem when the event's handler needs to prevent the default behavior of the event. Tradicional `event.preventDefault()` will not work, instead use the qwik's `preventdefault:{eventName}` attribute:
Because of the async nature of Qwik, event's handler execution might be delayed because the implementation is not downloaded yet. This introduces a problem when the event's handler needs to prevent the default behavior of the event. Traditional `event.preventDefault()` will not work, instead use the qwik's `preventdefault:{eventName}` attribute:

```tsx
const Counter = component$(() => {
Expand Down Expand Up @@ -269,15 +269,15 @@ Notice that `on:click` attribute contains three pieces of information:
2. `Counter_button_onClick`: The symbol which needs to be retrieved from the lazy-loaded chunk.
3. `[0]`: An array of lexically captured variable references (State of the closure).

In our case `() => store.count++` only captures `store`, and hence it contains only a single reference `0`. `0` is an index into the `q:obj` attribute which contains a reference to the actual serialized object referring to `store`. (The exact mechanisms and syntax is an implementation detail that can change at any time.)
In our case `() => store.count++` only captures `store`, and hence it contains only a single reference `0`. `0` is an index into the `q:obj` attribute that contains a reference to the actual serialized object referring to `store`. (The exact mechanisms and syntax is an implementation detail that can change at any time.)

## Comparison to `import()`

JavaScript supports dynamic `import()`. At first glance, it may seem that the same can be achieved by `import()`, but there are a few differences worth mentioning.

Dynamic `import()`:

- Is relative to the file which contains it. This works great for `file-a.js` trying to load `file-b.js` as `import('./file-b.js')`. However, when the `./file-a.js` gets serialized into HTML then we lose its relative nature. It is the framework that reads the `./file-b.js` from HTML and performs the `import()`. This means that all imports now become relative to the framework, which is incorrect.
- Is relative to the file that contains it. This works great for `file-a.js` trying to load `file-b.js` as `import('./file-b.js')`. However, when the `./file-a.js` gets serialized into HTML then we lose its relative nature. It is the framework that reads the `./file-b.js` from HTML and performs the `import()`. This means that all imports now become relative to the framework, which is incorrect.
- Requires that the developer writes `import('./file-a.js')`, which means the developer is in charge of deciding where the lazy-loaded boundaries are. This limits our ability of the tooling to move code around in an automated way.
- Supports import of top-level functions only which don't capture the state. This is the biggest difference. Qwik allows the imported symbol to be a closure that carries all of its state with it.

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/pages/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ After your new app is created, you will see an output like the following in your
npm start
```
At this point, you will have `qwik-todo` directory, which contains the starter app.
At this point, you will have `qwik-todo` directory, that contains the starter app.
## Running in development
Expand Down
8 changes: 4 additions & 4 deletions packages/docs/src/pages/docs/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ fetch: https://hackmd.io/@mhevery/Sy52N2Ax9

# Overview

Qwik is a new kind of web framework that can deliver instant loading web applications at any size or complexity. Your sites and apps can boot with less than 1kb of JS (_including_ your code, regardless of complexity), and achieve unheard of performance at scale.
Qwik is a new kind of web framework that can deliver instantly load web applications at any size or complexity. Your sites and apps can boot with about 1kb of JS (regardless of application complexity), and achieve consistent performance at scale.

## Qwik is:

- **General-purpose**: Qwik is familiar for React developers and can be used to build any type of web site or application.
- **Resumable**: Qwik is [resumable](./concepts/resumable.mdx) which means Qwik applications require **0 hydration**. This allows Qwik apps to have instant-on interactivity.
- **Resumable**: Qwik is [resumable](./concepts/resumable.mdx) which means Qwik applications require **no hydration**. This allows Qwik apps to have instant-on interactivity.
- **Progressive**: Qwik takes [full responsibility of how to load and download JS](https://www.builder.io/blog/dont-blame-the-developer-for-what-the-frameworks-did). No more manual code splitting.
- **Reactive**: Qwik semantics allow for [fully reactive and efficient rendering](./concepts/reactivity.mdx).
- **Fast**: Qwik has unprecedented performance, offering sub-second full page loads even on mobile devices. Qwik achieves this by delivering pure HTML, and incrementally loading JS only as-needed.
- **Scalable**: Qwik application have [O(1) constant scalability](https://www.builder.io/blog/our-current-frameworks-are-on-we-need-o1). It does not matter if your application has 1 million components, boot time is unaffected.
- **Fast**: Qwik has consistantly fast performance no matter the application complexity, offering sub-second full page loads even on mobile devices. Qwik achieves this by delivering pure HTML, and incrementally loading JS only as-needed.
- **Scalable**: Qwik application have [O(1) constant scalability](https://www.builder.io/blog/our-current-frameworks-are-on-we-need-o1). It does not matter the size of your application, boot time is constantly fast.

<img
alt="Qwik Diagram"
Expand Down
8 changes: 8 additions & 0 deletions packages/docs/src/pages/tutorial/component/basic/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Basic Component
layout: tutorial
---

Components are building blocks of Qwik application. Components are declared using `component$()` and at a minimum need to return a JSX.

Create a component that returns `Hello World!`
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { component$ } from '@builder.io/qwik';

export const App = component$(() => {
return <span>__put_something_here__</span>;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { component$ } from '@builder.io/qwik';

export const App = component$(() => {
return <span>Hello World!</span>;
});
9 changes: 9 additions & 0 deletions packages/docs/src/pages/tutorial/component/binding/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: Binding Expressions
layout: tutorial
---

The purpose of components is to merge data with the template (JSX.) This is done with the help of the `{expression}` syntax and can be placed either as a text node or attribute.

- Bind `data.name` to the `value` attribute of `<input/>`.
- Bind `data.description` to the content of `<textarea/>`.
26 changes: 26 additions & 0 deletions packages/docs/src/pages/tutorial/component/binding/problem/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { component$ } from '@builder.io/qwik';

export const App = component$(() => {
// @ts-ignore
const data = {
name: 'Qwik',
description: DESCRIPTION,
};

return (
<>
<input value="data.name should go here" />
<br />
<textarea rows={10} cols={60}>
data.description should go here
</textarea>
</>
);
});

export const DESCRIPTION = `
Qwik is designed for the fastest possible page load time,
by delivering pure HTML with near 0 JavaScript for your
pages to become interactive, regardless of how complex
your site or app is. It achieves this via resumability
of code.`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { component$ } from '@builder.io/qwik';

export const App = component$(() => {
const data = {
name: 'Qwik',
description: DESCRIPTION,
};

return (
<>
<input value={data.name} />
<br />
<textarea rows={10} cols={60}>
{data.description}
</textarea>
</>
);
});

export const DESCRIPTION = `
Qwik is designed for the fastest possible page load time,
by delivering pure HTML with near 0 JavaScript for your
pages to become interactive, regardless of how complex
your site or app is. It achieves this via resumability
of code.`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Component Composition
layout: tutorial
---

Components can be composed together to build applications.

In this example we have pre-declared an `<App>` and a `<Greeter>` component. Place the `<Greeter>` component inside the `<App>` component so that the user can see its contents.
Loading

0 comments on commit 6102668

Please sign in to comment.