最近正在將一個使用單文件組件的 Options API 的 Vue2 JAVAScript 項目升級為 Vue3 typescript,并利用 Composition API 的優勢。
比如,下面這種 選項API 方式:
export default { props: { name: { type: String, required: true. } }, emits: ['someEvent', 'increaseBy']};
我們將它轉成 組合API 方式:
const props = defineProps<{ name: string; }>(); const emit = defineEmits<{ (event: 'someEvent): void; (event: 'increaseBy', value: number): void; }>();
從 選項API 的 emit 和 props 到 組合API 的 defineemit 和 defineProps 函數的基于類型語法的轉換并不簡單。我也很好奇 Vue 是如何處理接口的。
TypeScript 接口是只在設計和編譯時存在的結構。它們在JavaScript運行時之前被過濾掉,那么它們是如何影響組件的行為的呢?
我想知道是否有辦法看到Vue如何解釋傳遞給 defineEmits 和 defineProps 的通用參數。如果你注意到文檔中說你不需要導入 defineEmits 和 defineProps 函數。這是因為它們實際上是同名的JavaScript函數的宏。在進行完整的 TypeScript 傳遞之前,Vue webpack插件使用TypeScript的 AST(抽象語法樹)來推導JavaScript版本的函數選項。
如果不是因為宏:
defineProps<{ prop1: string; prop2: number; }>();
就會變成:
defineProps();
這樣就會導致參數缺失的錯誤。
如果看一下Vue的 SFC(單文件組件)編譯器源代碼,有一個叫做 compileScript 的函數。我開始嘗試用最少的參數來調用這個函數,這樣就不會出錯,并模擬任何不重要的必要參數。最終發現了另一個叫 parse 的函數。這給了我所需的大部分參數,只剩下要mock的組件 id。
這里有一個小腳本,它接收SFC的 .vue文件并輸出 Vue 如何解釋 TypeScript。
import { readFile, writeFile } from "fs";import parseArgs from "minimist";import { parse, compileScript } from "@vue/compiler-sfc";const { file, out } = parseArgs(process.argv.slice(2), { string: ["file", "out"], alias: { file: "f", out: "o" }});const filename = file;const mockId = "xxxxxxxx";readFile(filename, "utf8", (err, data) => { const { descriptor } = parse(data, { filename }); const { content } = compileScript(descriptor, { inl.NETemplate: true, templateOptions: { filename }, id: mockId }); if (out) { writeFile(out, "utf8", content); } else { process.stdout.write(content); }});
事例地址:
https://stackblitz.com/edit/node-fzuykn?file=index.js
例如,有如以下組件:
interface Bar { prop1: string; prop2: number;}defineProps<{ bar: Bar; bars: Bar[]; asdf1?: boolean; asdf2: string[];}>();
輸出:
interface Bar { prop1: string; prop2: number;}export default /*#__PURE__*/_defineComponent({ __name: 'demo', props: { bar: { type: Object, required: true }, bars: { type: Array, required: true }, asdf1: { type: Boolean, required: false }, asdf2: { type: Array, required: true } }, setup(__props: any) { return (_ctx: any,_cache: any) => { return (_openBlock(), _createElementBlock("div")) }}
正如上面所看到的,SFC編譯器采用TypeScript類型信息,并建立了 props 對象。原始類型是一對一的。接口變成對象,而 ? 可選語法驅動 required 的屬性。
作者:romaopedro 譯者:前端小智 來源:logrocket
https://unicorn-utterances.com/posts/vue-composition-inspector