vue jsx写法记录

[toc]

通过本文, 你可以学到一些vuejsx的语法。

vue更加推荐使用模板开发组件,但是在一些基础组件开发中,为了获取js的完全编程能力,不可避免需要使用一些jsx语法,而官方vue jsxvue特有的语法糖这方面的文档特别少, 本文记录一些本人在开发过程中使用jsx的经验和思考。

整体上来看,由于vuecreateElementreactcreateElement有区别,导致jsx的写法也有一些区别.但整体上还是符合react的jsx,react jsx进阶的语法。

如果需要了解vue jsx特殊写法的原理,可以阅读babel-plugin-transform-vue-jsx , 本文不做探讨。

data写法

jsx本质上是createElement的语法糖,最终会被编译器转为createElement函数.当在jsx的标签中使用{ ...obj }时, obj将会编译为createElement的第二个参数.

vuecreateElement跟react的createElement函数第二个参数意义是不一样的.在vue中,第二个参数是 data对象, 而react第二个参数是props。所以本人将这种方式称为data写法

如在vue中需要设置动态属性时:

const props={
  name: 'joyer',
},

<my-button {...{
  props:props,
}}></my-button>

当不知道模板中某个vue语法怎么用jsx实现时,可以先转换为createElementdata对象,然后使用{...data}写在jsx标签上(本文重点).

如官方推荐原生dom属性的jsx写法:

<button domPropsType="submit"><button>

采用data写法为:

<button { ...{
  domProps: {
    type: 'submit',
  }, 
}}><button>

该方式不够优雅,如果官方有更加优雅的语法糖,推荐使用官方推荐。如果某个语法,官方没有案例,该方式就可以作为最终选择。 并且通过这种方式,createElement中所有的特性都可以用于jsx的开发了。

下文中所有的写法, 都可以使用data写法。

v-model的写法

官网中v-model写法不可行。

模板中写法为:

<el-input v-model.trim="inputValue"/>

jsx写法需要为:

<el-input vModel_trim={inputValue}/>
// 或者使用
<el-input 
 value={this.inputValue}
 on-input={val => this.inputValue = val.trim()}/>

v-for

模板中的写法:

<el-tag
 v-for="(item, index) in content"
 :key="index"
 type="success"
 >
    {{item.name}}
</el-tag>

jsx的写法:

{
  this.content.map((item, index) = >{
    return (<el-tag
      key={ index }
      type="success">{ item.name }</el-tag>);
  })
}

事件 & 按键修饰符

官方的写法

<input vOn:click_stop_prevent="newTodoText" />

一些编辑器会提示语法错误(因为reactjsx不允许这样写),推荐使用下面的写法

<input
 nativeOn-keyup={arg => arg.keyCode === 13 && this.fetch()}
 on-click={event => {event.stopPropagation();event.preventDefault();this.click()} }/>

修饰符需要自己在代码中实现。或者可以使用修饰符简写,对照官网的语法, jsx写法为:

<input {...{
    on: {
     '!click': () => this.doThisInCapturingMode,
    '~keyup': () => this.doThisOnce,
    '~!mouseover': () => this.doThisOnceInCapturingMode
    }
}} />

事件处理都采用了箭头函数, 跟react一样, 需要处理this绑定问题,可以使用bind绑定,

`on-click={this.click.bind(this, args) }`

不能直接赋值函数on-click={this.click}

高阶组件中的v-on="$listeners"v-bind="$attrs"

在高阶组件中, 一般都会使用v-on="$listeners"v-bind="$attrs",但在官方文档没有说明jsx如何实现,只有createElement中说明了实现:

return createElement('div', {
    props: {
      ...$attrs,
      otherProp: value,
    },
    on: {
      ...$listeners,
      click() {
      },
    }
},this.$slots.default])

参照data写法, jsx实现为:

const data = {
  props: {
     ...$attrs,
     otherProp: value,
  },
  on: {
     ...$listeners,
     click() {
     },
  }
}

<button { ...data }><button>

对于$attrs$listeners可以有更加灵活的用法。如要实现elemet-ui中一个可以快速布局el-form-item高阶组件,将el-form-itemel-col的结合:

render(h) {
 // 拆分出作用于col和form-item上的属性
 const colAttrs = {};
 const itemAtts = {};
 this.$attrs.forEach((attrName) => {
    // 这里使用了lodash
    if (_.startsWith(attrName, `col-`)) {
         colAttrs[attrName.replace(`col-`, '')] = this.$attrs[attrName];
         return;
    }
    itemAtts[attrName] = this.$attrs[attrName];
 });
 return (<el-col {
    ...{
       props: this.colAttrs,
    }
 }> 
    <el-form-item {
        ...{
            props: this.itemAtts,
        }
    }></el-form-item>
 </el-col>);
}

该高阶组件可以传递两种类型的属性, 带col-开头的属性,作用于el-col上面, 其他属性作用于el-form-item组件上。

如果需要标签属性透传,将当前组件的所有attrs传递给内部组件(内部组件也用v-bind="$attrs"),还需设置attrs值。
如高阶组件my-button:

<el-button v-bind="$attrs" type="primary">
 <slot></slot>
</el-button>

高阶组件high-button:

render(h) {
 return (<my-button { ...{
  props: this.attrs,
  attrs: this.$attrs,
 } }><my-button>);
}

通过设置attrshigh-button接收到的所有标签设置传递给my-button中。如果不这样做, 那么my-button中将接收不到任何属性设置,因为只传递props,在high-button组件中对于没有匹配high-buttonprops声明的标签属性都会被丢弃。

slot写法

默认插槽模板写法:

<button>
    <slot></slot>
</button>

jsx的写法:

<button>
    {this.$scopedSlots.default && this.$scopedSlots.default()}
</button>

具名插槽模板写法:

<button>
    <slot name="before"></slot>
    <slot ></slot>
</button>

jsx写法:

let before = '';
if (this.$scopedSlots.before) {
    before = this.$scopedSlots.before(props => props.text);
}
return (<button>
    { before }
    {this.$scopedSlots.default && this.$scopedSlots.default()}
</button>)

作用域插槽模板写法:

<slot :isAdvancedPanelShow="isAdvancedPanelShow"></slot>

jsx写法:

{this.$scopedSlots.default && this.$scopedSlots.default({
    isAdvancedPanelShow: this.isAdvancedPanelShow
})}

所有的插槽(this.$scopedSlots[${slotName}])在调用时, 都尽量判断以下是否为空, 因为可能存在使用组件时, 没有传递插槽内容.

动态组件名字

还记得官网怎么介绍createElement获取js的完全编程能力吗? 举了一个根据不同的等级使用不同标题的案例:

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level, // 标签名称
      this.$slots.default // 子节点数组
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

这个案例通过jsx的写法为:

Vue.component('anchored-heading', {
  render: function (h) {
   const TagName = 'h' + this.level;
    return <TagName>{ this.$slots.default(this.props) }</TagName>
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

要知道,vuecreateElement函数的第一个参数可以是一个 HTML 标签名、组件选项对象,或者resolve了上述任何一种的一个async函数。

动态组件编排

在实际开发中, 碰到一种场景, 有组件CompA, CompB, CompC 。如果是a情况,需要在当前组件按照CompA, CompB, CompC顺序展示, 如果是不是a情况, 则通过CompC, CompB, CompA来展示。jsx写法为:

render(h) {
 // 假设组件A是需要特殊设置一些属性的
 const compA = (<CompA name="joyer">hellow word</CompA>)
 const content = this.status === 'a' ? [compA, CompB, CompC] : [CompC, CompB, compA];
 return (<div>{content}</div>)
}

这里末尾也可以这样写:

return (<div>{...content}</div>)

但在有些编辑器会报错,因为在react不允许这样写:

Spread children are not supported in React.

在babel官方编辑器中尝试

可以在babel官方提供的编辑器jsx尝试代码,不过需要记得在左边最下角添加babel=plugin-transform-vue-jsx插件:

添加vue-jsx插件
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355