? 前言
? v-bind 的 .sync
修飾符在 vue3 已移除
? 但為了更深入的瞭解 vue3
模板編譯 和 AST
,所以我嘗試用 AST
實現 .sync
修飾符
? vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { transformSync } from './transformSync'
export default defineConfig({
plugins: [
vue({
template: { compilerOptions: { nodeTransforms: [transformSync] } }
})
]
})
? transformSync.ts
import { createSimpleExpression, DirectiveNode, SimpleExpressionNode, TemplateChildNode } from '@vue/compiler-core'
import { remove } from '@vue/shared'
const ELEMENT = 1
const DIRECTIVE = 7
// 建立事件表示式
// e.g. @arg="exp"
const createEventExpression = (arg?: SimpleExpressionNode, exp?: SimpleExpressionNode) => ({ type: DIRECTIVE, name: 'on', arg, exp, loc: undefined, modifiers: [] } as unknown as DirectiveNode)
/**
* 將 `.sync` 修飾符轉換為 `@update:xxx`
*
* e.g.
*
* `<AAA :xxx.sync="value" />`
*
* ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
*
* `<AAA :xxx="value" @update:xxx="value = $event" />`
*/
export function transformSync(node: TemplateChildNode) {
if (node.type === ELEMENT) {
const { props } = node
for (let i = 0; i < props.length; i++) {
const dir = props[i]
// 判斷屬性是否有 sync 修飾符
if (dir.type == DIRECTIVE && dir.modifiers.includes('sync')) {
remove(dir.modifiers, 'sync')
const { arg, exp } = dir
// @update:xxxx
const name = createSimpleExpression('update:' + arg?.loc.source, true)
// value = $event
const val = createSimpleExpression(exp?.loc.source + ' = $event')
// 為元素新增 @update:xxx="value = $event"
props.push(createEventExpression(name, val))
}
}
}
}
? 以上就是完整程式碼了
transformSync.ts
檔案去除註釋後大概是 30 行程式碼
? 讓我們來試試效果
<!-- MyButton.vue -->
<template>
<button @click="$emit('update:count', count + 1)">count: {{ count }}</button>
</template>
<script setup lang="ts">
defineProps<{
count: number
}>()
</script>
<!-- App.vue -->
<template>
<my-button :count.sync="value" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import MyButton from './MyButton.vue'
const value = ref(0)
</script>