前言
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>