理解模板编译器

“编译器”就是翻译器。它把便于人类编写的格式(模板)转换为便于机器处理的格式(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 函数也会在这里派上用场!
让我们分几个阶段逐步实现它。
