Preface:
Recently, a Vue2 JavaScript project using the Options API with a single file component is being upgraded to Vue3 typescript and leverages the benefits of the Composition API.
For example, the following option API method:
export default { props: { name: { type: String, required: true. } }, emits: ['someEvent', 'increaseBy'] };
We convert it into a combination API method:
const props = defineProps<{ name: string; }>(); const emit = defineEmits<{ (event: 'someEvent): void; (event: 'increaseBy', value: number): void; }>();
fromOptions APITheemit
andprops
ArriveCombination APIThedefineemit
anddefineProps
The conversion of functions based on type syntax is not simple. I'm also curious about how Vue handles interfaces.
The TypeScript interface is a structure that exists only at design and compile time. They are filtered out before JavaScript runtime, so how do they affect the behavior of components?
I'm wondering if there is a way to see how Vue explains it is passed todefineEmits
anddefineProps
General parameters of . If you notice that the document says you don't need to importdefineEmits
anddefineProps
Function. This is because they are actually macros of JavaScript functions of the same name. Before doing a full TypeScript pass, the Vue webpack plugin uses TypeScript's AST (Abstract Syntax Tree) to derive the JavaScript version of function options.
If it's not because of the macro:
defineProps<{ prop1: string; prop2: number; }>();
It will become:
defineProps();
This will lead to missing parameters errors.
If you look at the source code of Vue's SFC (single-file component) compiler, there is a function called compileScript. I started trying to call this function with the least number of parameters so that there would be no errors and simulate any unnecessary parameters that were not important. Finally, another function called parse was discovered. This gave me most of the parameters I needed, leaving only the components to mockid
。
Here is a small script that receives SFC's.vue
File and output how Vue interprets TypeScript.
import { readFile, writeFile } from "fs"; import parseArgs from "minimist"; import { parse, compileScript } from "@vue/compiler-sfc"; const { file, out } = parseArgs((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, { inlineTemplate: true, templateOptions: { filename }, id: mockId }); if (out) { writeFile(out, "utf8", content); } else { (content); } });
Case address:/edit/node-fzuykn?file=
For example, there are components like the following:
interface Bar { prop1: string; prop2: number; } defineProps<{ bar: Bar; bars: Bar[]; asdf1?: boolean; asdf2: string[]; }>();
Output:
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"))
}
}
As seen above, the SFC compiler uses TypeScript type information and has establishedprops
Object. Primitive types are one-to-one. The interface becomes an object, and?
Optional syntax driverrequired
attributes.
This is the article about defineEmits and defineProps in Vue3 that are used directly without introducing it. For more related Vue3 defineEmit content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!