大家好,我是馋嘴的猫。最近在用 Mitosis 配合组件库的开发,但在编译为 Vue 的产物时,遇到了一个奇怪的编译问题。借助这个问题,重新认识了 Mitosis 和 Vue 的关键字限制。
请随我的正文,一起来探讨下吧~
问题
在编译 Mitosis 的模板文件时,如果文件里面含有props.style
,则在打包 Vue 平台产物时会失败,并提示Error: avoid using JavaScript keyword as property name: “style”。
但同时,将此份模板文件编译为其他平台,如 React 和 Solid ,则正常无误。
问题复现 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 在遇到”props.style”编译出错的全流程
出错流程解析
1. Vue 的 generator 通过 getProps 方法收集所有的 props 属性,代码链接
const props = Array.from(getProps(component));
2. 在 getProps 方法里,匹配所有满足props.xxx
的赋值,并且对 xxx 的值展开校验。
3. 如果属性值命中了 prohibitedKeywordRE 的正则表达式(如props.style),则 Mitosis 会丢出错误 Error: avoid using JavaScript keyword as property name: "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 等属性,也能自动接收父组件的对应属性,并添加到根元素上,文档如下所示:
所以,在开发者使用 React jsx ,并使用props.style
来实现接受父组件传进来的 style 时,Vue 中却是不需要这么实现的。
并且,Vue 还实现了 class 和 style 的自动合并,所以也可以说,Vue 从另外一方面,更不鼓励从 props 手动取 class 和 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 作者:馋嘴的猫