理解模板編譯器

「編譯器」就是翻譯器。它把方便人類編寫的格式(模板)轉換為方便機器處理的格式(h 函式)!
實際上,到目前為止我們已經擁有了執行所需的一切(?)
到目前為止,我們已經實作了響應式系統,虛擬 DOM 和元件。 雖然這些都非常小且不實用,但可以毫不誇張地說,我們已經理解了執行所需的整體設定元素。 雖然每個元素本身的功能都不足,但感覺我們已經表面上過了一遍。
從本章開始,我們將實作模板功能,使其更接近 Vue.js。但是,這些只是為了改善 DX,不會影響執行時。(嚴格來說,編譯器最佳化可能會有影響,但由於這不是重點,我們假設它沒有影響。)
更具體地說,我們將擴充開發者介面以改善 DX,並「最終將其轉換為我們迄今為止製作的內部實作」。
這次我們想要實作的開發者介面
目前,開發者介面看起來像這樣。
const MyComponent: Component = {
props: { someMessage: { type: String } },
setup(props: any, { emit }: any) {
return () =>
h('div', {}, [
h('p', {}, [`someMessage: ${props.someMessage}`]),
h('button', { onClick: () => emit('click:change-message') }, [
'change message',
]),
])
},
}
const app = createApp({
setup() {
const state = reactive({ message: 'hello' })
const changeMessage = () => {
state.message += '!'
}
return () =>
h('div', { id: 'my-app' }, [
h(
MyComponent,
{
'some-message': state.message,
'onClick:change-message': changeMessage,
},
[],
),
])
},
})目前,View 部分是使用 h 函式建構的。我們希望能夠在 template 選項中編寫模板,使其更接近原始 HTML。
但是,一次實作各種東西是困難的,所以讓我們從有限的功能集開始。
現在,讓我們將其分為以下任務:
- 能夠渲染簡單的標籤,訊息和靜態屬性。
const app = createApp({ template: `<p class="hello">Hello World</p>` })- 能夠渲染更複雜的 HTML。
const app = createApp({
template: `
<div>
<p>hello</p>
<button> click me! </button>
</div>
`,
})- 能夠使用在 setup 函式中定義的內容。
const app = createApp({
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
return { count, increment }
},
template: `
<div>
<p>count: {{ count }}</p>
<button v-on:click="increment"> click me! </button>
</div>
`,
})我們將進一步將每個任務分為更小的部分,但讓我們大致分為這三個步驟。 讓我們從步驟 1 開始。
編譯器的作用
現在,我們的目標開發者介面看起來像這樣。
const app = createApp({ template: `<p class="hello">Hello World</p>` })首先,讓我們談談什麼是編譯器。 在編寫軟體時,您很快就會聽到「編譯器」這個詞。 「編譯」意味著翻譯,在軟體領域,它通常用於表示從高階描述翻譯到低階描述。
您還記得本書開頭的這個詞嗎?
為了方便起見,我們將更接近原始 JS 的稱為「低階開發者介面」。 而且,重要的是要注意「開始實作時,從低階部分開始」。 這樣做的原因是,在許多情況下,高階描述被轉換為低階描述並執行。 換句話說,1 和 2 最終在內部轉換為 3 的形式。 這種轉換的實作稱為「編譯器」。
那麼,為什麼我們需要這個叫做編譯器的東西呢?主要目的之一是「改善開發體驗」。 至少,如果提供了一個有效的低階介面,就可以僅使用這些函式進行開發。 但是,考慮與功能無關的各種部分可能會很麻煩和困難,描述可能難以理解。因此,我們將僅重新開發介面部分,考慮使用者的感受。
在這方面,Vue.js 的目標是「像原始 HTML 一樣編寫,並使用 Vue 提供的功能(指令等)方便地編寫視圖」。 而且,最終目標是 SFC。
最近,隨著 jsx/tsx 的流行,Vue 也提供這些作為開發者介面的選項。但是,這次,讓我們嘗試實作 Vue 的原始模板。
我已經用長篇文章解釋了它,但最終,我這次想要做的是實作將這樣的程式碼翻譯(編譯)的能力:
const app = createApp({ template: `<p class="hello">Hello World</p>` })轉換為這樣:
const app = createApp({
render() {
return h('p', { class: 'hello' }, ['Hello World'])
},
})為了進一步縮小範圍,就是這部分:
;`<p class="hello">Hello World</p>`
// ↓
h('p', { class: 'hello' }, ['Hello World'])
模板最終會轉換為 h 函式呼叫。也就是說,我們先前實作的 h 函式也會在這裡派上用場!
讓我們分幾個階段逐步實作它。
