有学有练才叫学习:学而不思则罔,思而不学则殆:学而不习,纸上谈兵,习而不进,画地为牢!

vue3 emits(Vue3中defineEmits、defineProps 是怎么做到不用引入就能直接用的)

vue 炮渣日记 3周前 (11-19) 22次浏览 已收录 0个评论 扫描二维码

最近正在将一个使用单文件组件的 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, {
    inlineTemplate: 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 的属性。

喜欢 (0)
炮渣日记
关于作者:
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址