前言
Vue JSX:让Vue支持JSX来书写代码的一个开发构建依赖。
最近已经到1.0 正式版了,稍微梳理下,就落实到具体业务去尝试。
更多的姿势可以看上面仓库的README,这里只说说我用到的。
差异化
这里仅仅列出我写这篇文章时候脑海能回忆起来的
React
写JSX很自然,毕竟是自家倡导的
- 类名需要做
classname
化 props
的传递可以直接{...props}
- 节点的传递,通过
{props.children}
渲染 - 支持空节点包括同级节点,
<><child/><child2/></>
- 支持花括号直接遍历数组生成节点,
{list.map(item=>(<a {...item.props}/>)}
- 函数式组件支持非常好
Vue
能够支持部分vue独有的特性,比如拿到computed
, 指令及自定义事件;
其他的写法上和react差不多,具体一些特性如下:
- 类名依旧可以直接class,其他对象和数组的支持跟react大同小异
props
的快速传递需要包括到attrs
- 若是要快速传递所有父级
props
,{...{attrs:this.$attrs}}
$attrs
会汇总除了class和style之外的所有props
- 若是要快速传递所有父级
- 节点的传递可以通过
slots
,比如最常见的具名<div>{this.$slots.default}</div>
- 传递变量(
scope-slots
),父用this.$scopedSlots.default
这类来传递一个对象
- 传递变量(
- 同级节点不支持,必须最外层有包裹层
- 不支持花括号内直接遍历(我用的时候会报错),单独抽离出一个函数式组件
- 函数式组件支持模板和js两种写法,简单的用法基本和react一致
代码体现
自定义事件
结合第二个栗子就能串起来
<script>
import png_default_scan_avatar from '@assets/cert/face_cert/scan_avatar.png';
import CertFooter from '../components/CertFooter';
export default {
components: {
CertFooter
},
name: 'face_cert',
methods: {
nextStep(isClick) {
if (isClick) {
console.log('11');
}
// 下一步验证
// this.$router.push({ name: 'cert_step4' });
}
},
render() {
const DefaultScanAvatar = () => {
return (
<div class="default-scan-avatar">
<div class="default-scan-avatar__desrc">请正对手机,确保光线充足</div>
<img class="default-scan-avatar__img" src={png_default_scan_avatar} />
</div>
);
};
return (
<div class="face-cert-page">
<DefaultScanAvatar />
<cert-footer text={'开始刷脸'} disabled={false} on-button-click={e => this.nextStep(e)} />
</div>
);
}
};
</script>
<style lang="scss" scoped>
.face-cert-page {
background-color: #fff;
height: 100%;
.default-scan-avatar {
margin-top: 54px;
margin-bottom: 148px;
&__desrc {
font-size: 36px;
color: #333;
text-align: center;
margin-bottom: 127px;
}
&__img {
display: block;
height: 350px;
width: 350px;
margin: 0 auto;
}
}
.cert-footer {
.next-wrapper {
width: 626px;
margin: 0 auto;
}
}
}
</style>
复制代码
{...props}及slot的体现
<script>
export default {
name: 'CertFooter',
methods: {
btnClick() {
// 点击了按钮
this.$emit('button-click', true);
}
},
render() {
return (
<div class="cert-footer">
<div class="cert-footer__btn" onClick={this.btnClick}>
<ns-button {...{ attrs: this.$attrs }} />
</div>
<safe-tips />
{this.$slots.default}
</div>
);
}
};
</script>
<style lang="scss" scoped>
.cert-footer {
width: 100%;
&__btn {
width: 626px;
margin: 0 auto;
}
}
</style>
复制代码
常规用法
<script>
export default {
props: {
cardinfo: {
type: Object,
default: function() {
return {
title: '银行名字',
type: '卡类型',
cardnumber: ['3432', '*****', '*****', '4232']
};
}
},
defaultCard: {
type: Boolean,
default: false
}
},
render() {
const { cardinfo } = this.$props;
const CardNumber = ({ props }) => {
return props.list.map((item, index) => {
return (
<div class="bankcard__card--number-field" key={index}>
{item}
</div>
);
});
};
return (
<div class="bankcard">
<div class="bankcard__title">
{cardinfo.title}
{this.defaultCard ? (
<div class={['bankcard__btn', 'bankcard__btn--disabled']}>默认</div>
) : (
<div class={['bankcard__btn', 'bankcard__btn--setDefaultCard']} onClick={() => this.$emit('change', true)}>
设为默认
</div>
)}
</div>
<div class="bankcard__card--type">{cardinfo.type}</div>
<div class="bankcard__card--number">
<CardNumber list={cardinfo.cardnumber} />
</div>
</div>
);
}
};
</script>
<style lang="scss" scoped>
.bankcard {
margin: 30px 0;
background-color: #fff;
box-shadow: 1px 1px 7px rgba(79, 123, 234, 0.31);
width: 100%;
border-radius: 5px;
padding: 56px 44px;
.bankcard__title {
font-size: 36px;
color: #333;
@include flex(row, space-between, center);
}
.bankcard__btn {
font-size: 14px;
color: #333;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
&--disabled {
background-color: rgba(211, 208, 208, 0.66);
color: #989393;
}
&--setDefaultCard {
border: 1px solid #989393;
&:active {
color: #4f7aea;
border: 1px solid #4f7aea;
}
}
}
.bankcard__card--type {
padding-top: 11px;
font-size: 25px;
color: #666;
}
.bankcard__card--number {
margin-top: 50px;
@include flex(row, flex-start, center);
cursor: pointer;
font-size: 36px;
.bankcard__card--number-field {
height: 30px;
line-height: 30px;
&:not(:first-child) {
margin-left: 50px;
}
}
}
}
</style>
复制代码
总结
Vue jsx 对自定义组件的v-model
和复杂的指令目前是无解,直接会报错;
所以遇到这种情况,我还是选择template
的写法来实现对应的业务需求;
总体来说还是挺实用的,一些情况下写起来舒服很多。
vue jsx 这个开发依赖解析还有待完善,坐等对vue自身特性更完美的支持。
目前若是基于vue cli3
构建的项目可以直接使用,无需手动去配置相关依赖。
有不对之处请留言,会及时修正,谢谢阅读。