Понимание компилятора шаблонов
На самом деле, у нас уже есть всё необходимое для работы (?)
До сих пор мы реализовали API createApp, систему реактивности и систему Virtual 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'])
Давайте реализуем это шаг за шагом в несколько фаз.