Skip to content

Commit

Permalink
feat: minimal reactivity improvement (#394)
Browse files Browse the repository at this point in the history
* chore: words alignment

* feat: minimal reactivity improvement
  • Loading branch information
ubugeeei authored Dec 9, 2024
1 parent fbe2dcc commit c69b7cb
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 19 deletions.
Binary file modified book/images/reactivity_create.drawio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added book/images/target_map.drawio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions book/online-book/.vitepress/config/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ export const jaConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
link: '/ja/10-minimum-example/025-event-handler-and-attrs',
},
{
text: 'Reactivity System の前程知識',
text: 'リアクティビティシステムの前程知識',
link: '/ja/10-minimum-example/030-prerequisite-knowledge-for-the-reactivity-system',
},
{
text: '小さいリアクティビティシステムを実装してみる',
link: '/ja/10-minimum-example/035-try-implementing-a-minimum-reactivity-system',
},
{
text: '小さい Virtual DOM',
text: '小さい仮想 DOM',
link: '/ja/10-minimum-example/040-minimum-virtual-dom',
},
{
Expand Down Expand Up @@ -123,7 +123,7 @@ export const jaConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'スケジューラ',
link: '/ja/20-basic-virtual-dom/030-scheduler',
link: '/kja/20-basic-virtual-dom/030-scheduler',
},
{
text: '🚧 対応できていない Props のパッチ',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,39 @@ class ReactiveEffect {
}
```

It means registering "a certain effect" for "a certain key" of "a certain target (object)".

It might be hard to understand just by looking at the code, so here is a concrete example and a supplementary diagram.\
Consider a component like the following:

```ts
export default defineComponent({
setup() {
const state1 = reactive({ name: "John", age: 20 })
const state2 = reactive({ count: 0 })

function onCountUpdated() {
console.log("count updated")
}

watch(() => state2.count, onCountUpdated)

return () => h("p", {}, `name: ${state1.name}`)
}
})
```

Although we haven't implemented `watch` in this chapter yet, it is written here for the sake of illustration.\
In this component, the targetMap will eventually be formed as follows.

![target_map](https://raw.githubusercontent.com/chibivue-land/chibivue/main/book/images/target_map.drawio.png)

The key of targetMap is "a certain target". In this example, state1 and state2 correspond to that.\
The keys that these targets have become the keys of targetMap.\
The effects associated with them become the values.

In the part `() => h("p", {}, name: ${state1.name})`, the mapping `state1->name->updateComponentFn` is registered, and in the part `watch(() => state2.count, onCountUpdated)`, the mapping `state2->count->onCountUpdated` is registered.

This basic structure is responsible for the rest, and then we think about how to create (register) targetMap and how to execute the effect.

That's where the concepts of `track` and `trigger` come in.
Expand Down
2 changes: 1 addition & 1 deletion book/online-book/src/ja/00-introduction/010-about.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@

- **Basic Component System 部門**
ここでは Component System 関する基本実装を行います.実は,Basic Virtual DOM 部門で Component System のベースは実装してしまうので,
それ以外の部分の Component System を実装します.例えば props/emit や provide/inject, Reactivity System の拡張,ライフサイクルフックなどです.
それ以外の部分の Component System を実装します.例えば props/emit や provide/inject, リアクティビティシステムの拡張,ライフサイクルフックなどです.

- **Basic Template Compiler 部門**
Basic Virtual DOM で実装した Virtual DOM システムに対応する機能のコンパイラに加え,v-on, v-bind, v-for 等のディレクティブなどの実装を行います.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Reactivity System の前程知識
# リアクティビティシステムの前程知識

## 今回目指す開発者インタフェース

ここからは Vue.js の醍醐味である Reactivity System というものについてやっていきます
ここからは Vue.js の醍醐味であるリアクティビティシステムというものについてやっていきます
これ以前の実装は,見た目が Vue.js に似ていれど,それは見た目だけで機能的には全く Vue.js ではありません.
たんに最初の開発者インタフェースを実装し,いろんな HTML を表示できるようにしてみました.

Expand Down Expand Up @@ -48,7 +48,7 @@ reactive 関数でステートを定義し,それを書き換える increment
- ボタンをクリックすると,ステートが更新される
- ステートの更新を追跡して render 関数を再実行し,画面を再描画する

## Reactivity System とはどのようなもの?
## リアクティビティシステムとはどのようなもの?

さてここで,そもそもリアクティブとは何だったかのおさらいです.
公式ドキュメントを参照してみます.
Expand Down Expand Up @@ -136,7 +136,7 @@ app.mount('#app')

Proxy と呼ばれるオブジェクトが肝になっています.

まず, Reactivity System の実装方法についてではなく,それぞれについての説明をしてみます.
まず,リアクティビティシステムの実装方法についてではなく,それぞれについての説明をしてみます.

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## Proxy を使ったリアクティビティの仕組み

::: info 現在の vuejs/core との設計の違いについて
現在 (2024/12)の Vue.js の Reactivity System では,doubly linked list ベースの Observer Pattern が採用されています.\
現在 (2024/12)の Vue.js のリアクティビティシステムでは,doubly linked list ベースの Observer Pattern が採用されています.\
この実装は [Refactor reactivity system to use version counting and doubly-linked list tracking](https://github.com/vuejs/core/pull/10397) で行われ,パフォーマンスの向上に寄与しました.

しかし,初めてリアクティビティシステムを実装する人にとっては少し難しいものになっており,今回のこのチャプターでは従来 (改善以前) のものをより簡略化したものの実装を行います.\
Expand All @@ -16,7 +16,7 @@
改めて目的を明確にしておくと,今回の目的は「ステートが変更された時に `updateComponent` を実行したい」です.
Proxy を用いた実装の流れについて説明してみます.

まず,Vue.js の Reactivity System には `target`, `Proxy`, `ReactiveEffect`, `Dep`, `track`, `trigger`, `targetMap`, `activeEffect` (現在は `activeSub`) というものが登場します.
まず,Vue.js のリアクティビティシステムには `target`, `Proxy`, `ReactiveEffect`, `Dep`, `track`, `trigger`, `targetMap`, `activeEffect` (現在は `activeSub`) というものが登場します.

まず,targetMap の構造についてです.
targetMap はある target の key と dep のマッピングです.
Expand All @@ -41,6 +41,39 @@ class ReactiveEffect {
}
```

「ある target (オブジェクト)」 の「ある key」 に対して「ある作用」 を登録するということになります.

パッと見のコードだけだと分かりづらいと思うので具体例と図による補足です.\
以下のようなコンポーネントがあったと考えてみます.

```ts
export default defineComponent({
setup() {
const state1 = reactive({ name: "John", age: 20 })
const state2 = reactive({ count: 0 })

function onCountUpdated() {
console.log("count updated")
}

watch(() => state2.count, onCountUpdated)

return () => h("p", {}, `name: ${state1.name}`)
}
})
```

このチャプターではまだ watch は実装していないのでですが,イメージのために書いてあります.\
このコンポーネントでは最終的にかのような targetMap が形成されます.

![target_map](https://raw.githubusercontent.com/chibivue-land/chibivue/main/book/images/target_map.drawio.png)

targetMap の key は「ある target」 です.この例では state1 と state2 がそれにあたります.\
そして,これらの target が持つ key が targetMap の key になります.\
そこに紐づく作用がその value になります.

`() => h("p", {}, name: ${state1.name})` の部分で `state->name->updateComponentFn` というマッピングが登録され,`watch(() => state2.count, onCountUpdated)` の部分で `state2->count->onCountUpdated` というマッピングが登録されるという感じです.

基本的な構造はこれが担っていて,あとはこの TargetMap をどう作っていくか(どう登録していくか)と実際に作用を実行するにはどうするかということを考えます.

そこで登場する概念が `track``trigger` です.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Virtual DOM,何に使われる?

前のチャプターで Reactivity System を導入したことで画面を動的に更新できるようになりました
前のチャプターでリアクティビティシステムを導入したことで画面を動的に更新できるようになりました
改めて現在の render 関数の内容を見てみましょう.

```ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## 既存実装の整理ベースで考える

これまで,createApp API や Reactivity System, Virtual DOM を小さく実装してきました.
今現時点での実装では Reactivity System によって UI を動的に変更することもできますし, Virtual DOM によって効率的なレンダリングを行うことができているのですが,開発者インタフェースとしては全ての内容を createApp API に書く感じになってしまっています.
これまで,createApp API やリアクティビティシステム, 仮想 DOM を小さく実装してきました.
今現時点での実装ではリアクティビティシステムによって UI を動的に変更することもできますし, 仮想 DOM によって効率的なレンダリングを行うことができているのですが,開発者インタフェースとしては全ての内容を createApp API に書く感じになってしまっています.
実際にはもっとファイルを分割したり,再利用のために汎用的なコンポーネントを実装したいです.
まずは既存実装の散らかってしまっている部分を見直してみます.renderer.ts の render 関数をみてください.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 実はここまでで動作に必要なものは揃った ( ? )

これまで, Reactivity System や Virtual DOM ,Component などを実装してきました
これまで,リアクティビティシステムや仮想 DOM,コンポーネントなどを実装してきました
これらは非常に小さなもので,実用的なものではないのですが,実は動作に必要な構成要素の全体像としては一通り理解できたと言っても過言ではないのです.
それぞれの要素自体の機能は足りていないですが,浅〜〜〜〜〜く 1 周した感じです.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## テンプレートにバインドしたい

今の状態だと,直接 DOM 操作をしているので, Reactivity System や Virtual DOM の恩恵を得ることができていません.
今の状態だと,直接 DOM 操作をしているので,リアクティビティシステムや仮想 DOM の恩恵を得ることができていません.
実際にはイベントハンドラであったり,テキストの内容はテンプレート部分に書きたいわけです.それでこそ宣言的 UI の嬉しさと言った感じですよね.
以下のような開発者インタフェースを目指します.

Expand Down
4 changes: 2 additions & 2 deletions book/online-book/src/ja/10-minimum-example/100-break.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ h('div', { id: 'my-app' }, [

ここで初めて Virtual DOM のようなものが登場しました.

## Reactivity System とは何か,どうやって画面を動的に更新していくかということが分かった
## リアクティビティシステムとは何か,どうやって画面を動的に更新していくかということが分かった

Vue の醍醐味である,Reactivity System がどのような実装で成り立っているのか,そもそも Reactivity System とはなんのことなのか,ということについて理解しました
Vue の醍醐味である,リアクティビティシステムがどのような実装で成り立っているのか,そもそもリアクティビティシステムとはなんのことなのか,ということについて理解しました

```ts
const targetMap = new WeakMap<any, KeyToDepMap>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

## reactive にしたくないオブジェクト

さて,ここでは現状の Reactivity System のある問題について解決していきます
さて,ここでは現状のリアクティビティシステムのある問題について解決していきます
まずは以下のコードを動かしてみてください.

```ts
Expand Down
2 changes: 1 addition & 1 deletion book/online-book/src/ja/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ hero:

features:
- title: Reactivity System
details: 基本的な Reactivity System の原理から、effectScope や customRef などの応用的な API の実装まで幅広く行います。
details: 基本的なリアクティビティの原理から、effectScope や customRef などの応用的な API の実装まで幅広く行います。
icon: 🔆
- title: Virtual DOM
details: Virtual DOM の基本的な実装から、パッチレンダリング、スケジューラの実装まで幅広く行います。
Expand Down

0 comments on commit c69b7cb

Please sign in to comment.