在Vue中使用JSX的正确姿势
在Vue中使用JSX的正确姿势ant-design-vue开源了一段时间后,收到了一些反馈,尤其是Form组件上线后,很多用户对JSX的使用感到迷惑和不习惯,为此专门介绍下Vue JSX的使用姿势及注意事项。Form组件的自动收集校验功能需要在JSX下使用,当然如果不需要自动收集校验,你依然可以使用templateVue 推荐在绝大多数情况下使用template来创建你的 H...
在Vue中使用JSX的正确姿势
ant-design-vue开源了一段时间后,收到了一些反馈,尤其是Form组件上线后,很多用户对JSX的使用感到迷惑和不习惯,为此专门介绍下Vue JSX的使用姿势及注意事项。
Form组件的自动收集校验功能需要在JSX下使用,当然如果不需要自动收集校验,你依然可以使用template
Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,就需要使用 render 函数,它比 template 更接近编译器。但是复杂的render函数书写异常痛苦,好在官方提供了一个 Babel 插件,可以将更接近于模板语法的JSX转译成JavaScript。
使用过React的同学对JSX肯定不陌生,但是Vue的JSX写法和React的还是有一些区别。
React中父子之间传递的所有数据都是属性,即所有数据均挂载在`props`下(style, className, children, value, onChange等等)。
Vue则不然,仅仅属性就有三种:组件属性props
,普通html属性attrs
,Dom属性domProps
。
接下来我们通过一个示例来详细解释他们的区别:本文代码可在codesandbox查看运行
const ButtonCounter = {
name: "button-counter",
props: ["count"],
methods: {
onClick() {
this.$emit("change", this.count + 1);
}
},
render() {
return (
<button onClick={this.onClick}>You clicked me {this.count} times.</button>
);
}
};
export default {
name: "button-counter-container",
data() {
return {
count: 0
};
},
methods: {
onChange(val) {
this.count = val;
}
},
render() {
const { count, onChange } = this;
return (
<div>
<ButtonCounter
style={{ marginTop: "10px" }}
count={count}
type="button"
onChange={onChange}
/>
<ButtonCounter
style={{ marginTop: "10px" }}
count={count}
type="button"
domPropsInnerHTML={`hello ${this.count}.`}
onChange={onChange}
/>
</div>
);
}
};
组件属性props
:指组件声明的属性,即上述示例中声明的props: ['count']
。
普通html属性attrs
: 指组件未声明的属性,即上述示例中的type="button"
,该属性默认会直接挂载到组件根节点的上,如果不需要挂载到根节点,可声明 inheritAttrs: false
。
Dom属性domProps
:指的Dom属性,如上述示例中的innerHTML
,它会覆盖组件内部的children
, 这类属性我们一般很少使用到。
同样事件属性也分了两种:on
nativeOn
那么问题来了,组件是如何区分各类属性的呢?
答:正则则则...... ?,babel-plugin-transform-vue-jsx插件会通过正则匹配的方式在编译阶段将书写在组件上属性进行“分类”。
onXXX的均被认为是事件,nativeOnXXX是原生事件,domPropsXXX是Dom属性,
class,staticClass,style,key,ref,refInFor,slot,scopedSlots
这些被认为是顶级属性,至于我们属性声明的props,以及html属性attrs,不需要加前缀,插件会将其统一分类到attrs属性下,然后在运行阶段根据是否在props声明来决定属性归属(即属于props
还是attrs
)。
在编译阶段通过正则来区分,毕竟不是很严谨,那么根据以上分类规则会有哪些问题呢?
第一、属性分类是编译阶段进行的分类,那么对于动态属性如何划分分类?
在React中所有属性都是顶级属性,直接使用{...props}
就可以了,但是在Vue中,你需要明确该属性所属的分类,如一个动态属性value和事件change,你可以使用如下方式(延展属性)传递:
const dynamicProps = {
props: {},
on: {},
}
if(haValue) dynamicProps.props.value = value
if(hasChange) dynamicProps.on.change = onChange
<Dynamic {...dynamicProps} />
当然你可以混合着使用:
<Dynamic {...dynamicProps} style="color: red"/>
先别高兴太早,如果你没有深入使用过Vue JSX,不建议你使用混合方式,因为Vue会对其进行属性合并,至于合并的规则官方也并没有详细的文档,文档中有一处示例,我在这再举一个例子:
const dynamicProps2 = { on: { change: onChange2 } };
<Dynamic
{...{ on: { change: onChange1 } }}
{...dynamicProps2}
onChange={onChange3}
/>
上例中的onChange1、onChange2、onChange3都会触发,而你想要的可能仅仅是onChange3。其它属性的合并规则我就不一一列举了,总之,我不建议你使用混合方式,除非你及你的团队其他小伙伴对其规则了解的足够透彻。
注:理想情况你不应该需要动态属性,在业务开发中也比较少的使用动态属性,但如果你尝试开发一些通用性比较强的组件,就很难逃过动态属性的使用。
第二、如果声明的属性就是onXXX怎么处理?
首先我不建议你这么做,但如果真的需要,你必须明确该属性的分类,如下所示:
<div>
<Dynamic value="获取到value,然而并不能获取到onXXX" onXXX="?" />
<Dynamic {...{ props: { onXXX: "获取到onXXX,但不建议使用" } }} />
</div>
第三、函数式组件的props如何处理?
关于函数式组件的相关概念可查看官方文档,文档中有如下一段内容:
注意:在 2.3.0 之前的版本中,如果一个函数式组件想要接受 props,则props
选项是必须的。在 2.3.0 或以上的版本中,你可以省略props
选项,所有组件上的属性都会被自动解析为 props。
props
和attrs
之间存在着很微妙的关系,在普通组件中,只要明确声明的属性会被划分到props
分类中,剩下的均在attrs中。而对于函数式组件,只要省略了props
选项,传参时不管是否明确分类,最终context.props获取到的都是全部属性,如果你需要获取明确的分类情况,可以在context.data
下查看。
总之,在函数式组件中,推荐省略props
选项。
第四、指令是否还可用?
很不幸的告诉你,大多数指令并不能在JSX中使用,对于原生指令,只有v-show
是支持的。
大部分指令在JSX中可以使用表达式来替代,如:条件运算符(?:)替代v-if
、array.map
替代v-for
对于自定义的指令,可以使用如下方式使用:
const directives = [
{ name: 'my-dir', value: 123, modifiers: { abc: true } }
]
return <div {...{ directives }}/>
更多Vue JSX的知识,可以查看官方文档。
总结:
说了那么多,其实只要记住一点,尽量使用明确分类的方式传递属性,而不是要babel插件帮你分类及合并属性。
更多推荐
所有评论(0)