Mitosis 编译 Vue 产物,模板含有 props.style 竟然会出错?

大家好,我是馋嘴的猫。最近在用 Mitosis 配合组件库的开发,但在编译为 Vue 的产物时,遇到了一个奇怪的编译问题。借助这个问题,重新认识了 Mitosis 和 Vue 的关键字限制。

请随我的正文,一起来探讨下吧~

问题

在编译 Mitosis 的模板文件时,如果文件里面含有props.style,则在打包 Vue 平台产物时会失败,并提示Error: avoid using JavaScript keyword as property name: “style”。

但同时,将此份模板文件编译为其他平台,如 React 和 Solid ,则正常无误。

问题复现 Playground

Mitosis Playground

可在 Mitosis Playground 的右边窗口选择 React、Solid、Vue 选项卡查看编译结果,仅在选择 Vue 时会出错,出错信息可在 Chrome 的 console 里查看。

Mitosis 出错源码

export default function MyComponent(props) {
  return (
    <div style={props.style}/>
  );
}

分析

在编译 Vue 的时候出错提示为:

Error: avoid using JavaScript keyword as property name: "style"

在 Mitosis 源码查找出错提示,发现为 getProps 方法抛出的错误,代码位置

Mitosis 编译 Vue 产物,模板含有 props.style 竟然会出错?

继续追查下去,可以重现 Mitosis 在遇到”props.style”编译出错的全流程

出错流程解析

1.  Vue 的 generator 通过 getProps 方法收集所有的 props 属性,代码链接

const props = Array.from(getProps(component));

2.  在 getProps 方法里,匹配所有满足props.xxx的赋值,并且对 xxx 的值展开校验。

Mitosis 编译 Vue 产物,模板含有 props.style 竟然会出错?

3.  如果属性值命中了 prohibitedKeywordRE 的正则表达式(如props.style),则 Mitosis 会丢出错误 Error: avoid using JavaScript keyword as property name: "style" ,即本篇一开始提示遇到的错误。 代码链接

Mitosis 编译 Vue 产物,模板含有 props.style 竟然会出错?

4.  至此,全流程完结。

Mitosis为何要做这样的限制?

查看源码,发现 Mitosis 官方在一笔提交里加上了对 Vue 的关键字限制,代码链接

这笔提交,主要是两处关键字的限制,与 Vue 是对齐的,可对照 Vue 仓库的源码查看:

1.  utils.ts 里的限制,代码链接

export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')

2.  attrs.ts 里的限制,代码链接

export const isReservedAttr = makeMap('style,class')

3.  结合以上的限制,Vue 在 dev 模式下,会对不符合条件的 props,抛出 warning,代码链接

if (__DEV__) {
  const hyphenatedKey = hyphenate(key);
  if (
    isReservedAttribute(hyphenatedKey) ||
    config.isReservedAttr(hyphenatedKey)
  ) {
    warn(
      `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
      vm
    );
  }
}

Vue 为何要做这样的限制

那回过头来,Mitosis 是为了对齐 Vue 而做的关键字限制。那为什么 Vue 一开始就要做限制呢?

原因

Vue 支持透传特性,可以实现组件不声明 class、style 等属性,也能自动接收父组件的对应属性,并添加到根元素上,文档如下所示:

Mitosis 编译 Vue 产物,模板含有 props.style 竟然会出错?

所以,在开发者使用 React jsx ,并使用props.style来实现接受父组件传进来的 style 时,Vue 中却是不需要这么实现的。

并且,Vue 还实现了 class 和 style 的自动合并,所以也可以说,Vue 从另外一方面,更不鼓励从 props 手动取 class 和 style 的值了。

Mitosis 编译 Vue 产物,模板含有 props.style 竟然会出错?

鉴于以上的原因,Vue 对 props 数组加上了关键字限制,如 class、style 等。

关键字限制会影响 Mitosis 什么平台的编译?

1.  首先,getProps 函数里的校验逻辑,是导致 props 关键字限制的直接原因。

2.  其次,getProps 函数会被以下平台的 Mitosis generator 调用: Angular,Vue,Html,Lit,Stencil,Svelte,因此,这些平台打包时,都会受到关键字限制的影响。

结论

1.  Mitosis 作者对照 Vue 对 props 的关键字限制,在 Mitosis 的实现里添加了一样的关键字限制,导致了props.style 编译错误的发生。

2.  因为关键字限制的校验仅在 getProps 方法会被调用,因此会导致如props.style在 Vue 上会编译出错,在 React 上却编译正常(因 React generator 没有调用 getProps 函数)

3.  这些关键字限制是 Vue 独有的,但 Mitosis 官方却把此限制运用在 Angular,Vue,html,lit,stencil,svelte 这几个平台。可能是为了保证同一份 mitosis 能在所有平台都能编译,所以限制条件是取最苛刻的,没有做很完善的平台区分,但是这对于仅使用Angular、lit等框架的开发者是不太方便的(限制了本来没有必要限制的props)

4.  在书写 Mitosis JSX 时,因为 Mitosis 的校验逻辑限制,导致即使开发者尝试通过 useStore 来实现 getter method,类似以下代码,也会报错:

// still throws out error about "props.style"
const state = useStore({
  get myStyle() {
    return props.style;
  },
});

5.  如果开发者只需要生成 React 和 Solid 产物,暂不会遇到此问题。但如果需要 Angular,Vue,Html,Lit,Stencil,Svelte 这几个平台的产物,则需要想办法绕开关键字限制,比如与父组件约定传入其它非限制 key 值的 props。

原文链接:https://juejin.cn/post/7312635823786819622 作者:馋嘴的猫

(0)
上一篇 2023年12月16日 上午10:26
下一篇 2023年12月16日 上午10:37

相关推荐

发表回复

登录后才能评论