学会使用Vue JSX,一车老干妈都是你的

君自前端来,应知前端事。 需求时时变,bug改不完。javascript

连续几篇文章,每篇都有女神,被掘友给吐槽了,今天不提了女神了,反正女神都是别人的(扎心了)。这两天小编看了腾讯与老干妈的事情,晚上馒头夹老干妈吃起来都感受很带劲。今天这篇文章将给你们小编在项目中使用JSX的一些实战经验。其实通常状况下写Vue仍是比较推荐template的写法的,可是有时候咱们真的须要更灵活的去作一些功能,这时候就须要用到JSX了。php

阅读小编近期的热门Vue相关文章,感谢各位掘友和群友支持,每周一,不见不散html

前方高能,这是最新的一波Vue实战技巧,不用则已,一用惊人 获赞900+前端

实战技巧,Vue原来还能够这样写 获赞2300+vue

绝对干货~!学会这些Vue小技巧,能够早点下班和女神约会了 获赞 1100+java

看到赚到!重读vue2.0风格指南,我整理了这些关键规则 获赞 140+c++

介绍一下JSX

JSX 简介

JSX是一种Javascript的语法扩展,JSX = Javascript + XML,即在Javascript里面写XML,由于JSX的这个特性,因此他即具有了Javascript的灵活性,同时又兼具html的语义化和直观性。面试

应用场景

为了让你们更方便的去理解JSX的做用及用法,小编先为你们罗列了几个可能会用到JSX的应用场景。vue-cli

在消息框内添加html

在开发过程当中,常常会用到消息框,使用消息框可能的一种写法是这样的element-ui

Message.alert({
 messge: '肯定要删除?',  type: 'warning' }) 复制代码

可是有时候产品或UI但愿message能够自定义一些样式,这时候你可能就须要让Message.alert支持JSX了(固然也可使用插槽/html等方式解决)

Message.alert({
 // 此处使用了JSX  messge: <div>肯定要删除<span style="color:red">学习子君Vue系列文章</span>的笔记?</div>,  type: 'warning' }) 复制代码

函数式组件

在小编前面的文章实战技巧,Vue原来还能够这样写中介绍了为何要使用函数式组件,及函数式组件与普通组件的区别。虽然在Vue.2.5以后,函数式组件也可使用模板语法,但使用JSX可能会更方便一些(我的理解)

export default {
 // 经过配置functional属性指定组件为函数式组件  functional: true,  /**  * 渲染函数  * @param {*} h  * @param {*} context 函数式组件没有this, props, slots等都在context上面挂着  */  render(h, context) {  const { props } = context  if (props.avatar) {  return <img src={props.avatar}></img>  }  return <img src="default-avatar.png"></img>  } } 复制代码

一个表单的需求

为了方便快速开发管理系统,小编对所使用的UI库中的表单进行了二次封装,封装以后的效果以下(仅供参考):

<template>
 <custom-form v-model="formData" :fields="fields" /> </template> <script> export default {  data() {  return {  formData: {},  fields: Object.freeze([  {  label: '字段1',  props: 'field1',  type: 'input'  },  {  label: '字段2',  props: 'field2',  type: 'number'  }  ])  }  } } </script>  复制代码

这样封装以后,定义表单时,只须要定义简单的JSON便可快速完成表单开发,但有时候会有一些特殊的需求,好比但愿能够给输入框后面加一个按钮或者图标之类的,这时候就须要考虑使用JSX去处理了

{
 label: '字段2',  props: 'field2',  type: 'number',  // 会渲染到表单元素后面  renderSuffix() {  return <button onClick={this.$_handleSelect}>选择</button>  } } 复制代码

其余一些场景

好比咱们一条数据须要根据状态不一样,定义不一样的展示方式,这时候你可能会想到用策略模式,这时候若是将每个策略都写成一个JSX,那么就不须要针对每个策略定义一个单文件组件了。固然若是你说,我就喜欢用JSX,那么全部的场景你均可以用。

为了您阅读后面的内容时得到更好的阅读体验,建议您先点击一下电脑左侧或手机下方的大拇指图标,让页面色彩变得更丰富。

学习JSX,先了解一下createElement

提到JSX,不可避免的就要提到createElement,当你看完本节,你会发现,奇怪的知识又增多了。

Vue编译后的代码看createElement

你是否看过写的Vue代码通过编译以后的样子,好比下面这段代码

<template>
 <div>我是子君,个人公众号是<span class="emphasize">前端有的玩</span></div> </template>  复制代码

小编对这段代码进行编译以后,获得下面这段代码

function () {
 var e = this,  // e._self._c 对应源码里面的createElement  t = e._self._c;  // 返回了一个 createElement('div',[])  return t("div", [  // e._v 对应源码里面的createTextVNode  e._v("我是子君,个人公众号是"),  t("span", { staticClass: "emphasize" }, [e._v("前端有的玩")]),  ]); } 复制代码

经过对上面的代码进行分析,不难发现,Vue模板中的每个元素编译以后都会对应一个createElement,那么这个createElement究竟是什么,嗯,这个你面试的时候也许已经提到过了。

那么什么是createElement

不管是Vue仍是React,都存在createElement,并且做用基本一致。可能你对createElement不是很了解,函数名翻译过来就是增长一个元素,但他的返回值你必定知道。createElement函数返回的值称之为虚拟节点,即VNode,而由VNode扎堆组成的树即是大名鼎鼎,面试必问的虚拟DOM

createElement函数的参数,在这里小编偷个懒抄一下Vue官方文档

// @returns {VNode}
createElement(  // {String | Object | Function}  // 一个 HTML 标签名、组件选项对象,或者  // resolve 了上述任何一种的一个 async 函数。必填项。  'div',   // {Object}  // 一个与模板中 attribute 对应的数据对象。可选。  {  // (详情见下一节)  },   // {String | Array}  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,  // 也可使用字符串来生成“文本虚拟节点”。可选。  [  '先写一些文字',  createElement('h1', '一则头条'),  createElement(MyComponent, {  props: {  someProp: 'foobar'  }  })  ] ) 复制代码

从上面能够看出createElement一共有三个参数,三个参数分别是

  • 第一个参数是须要渲染的组件,能够是组件的标签,好比div;或者是一个组件对象,也就是你每天写的export default {};亦或者能够是一个异步函数。

  • 第二个参数是这个组件的属性,是一个对象,若是组件没有参数,能够传null(关于组件的属性,下文将依次介绍)

  • 第三个参数是这个组件的子组件,能够是一个字符串(textContent)或者一个由VNodes组成的数组

createElement写一个组件吧

表单示例

假设咱们须要开发一个下面这样的表格(element-ui的)

用模板代码去开发

若是咱们用模板代码去开发这个表单,那么代码大概就长这样

<el-form :inline="true" :model="formInline" class="demo-form-inline">
 <el-form-item label="审批人">  <el-input v-model="formInline.user" placeholder="审批人"></el-input>  </el-form-item>  <el-form-item label="活动区域">  <el-select v-model="formInline.region" placeholder="活动区域">  <el-option label="区域一" value="shanghai"></el-option>  <el-option label="区域二" value="beijing"></el-option>  </el-select>  </el-form-item>  <el-form-item>  <el-button type="primary" @click="onSubmit">查询</el-button>  </el-form-item> </el-form> 复制代码

createElement去实现

若是咱们直接将上面的代码转换为用createElement去实现,那么代码将会是这样的

export default {
 methods: {  $_handleChangeUser(value) {  this.formInline.user = value  }  },  render(createElement) {  return createElement(  'ElForm',  {  props: {  inline: true,  model: this.formInline  },  staticClass: 'demo-form-inline'  },  [  createElement(  'ElFormItem',  {  props: {  label: '审批人'  }  },  [  createElement('ElInput', {  props: {  value: this.formInline.user  },  attrs: {  placeholder: '审批人'  },  on: {  input: this.$_handleChangeUser  }  })  ]  ),  createElement(  'ElFormItem',  {  props: {  label: '活动区域'  }  },  [  createElement(  'ElSelect',  {  props: {  value: this.formInline.region,  placeholder: '活动区域'  }  },  [  createElement('ElOption', {  props: {  label: '区域一',  value: 'shanghai'  }  }),  createElement('ElOption', {  props: {  label: '区域二',  value: 'beijing'  }  })  ]  )  ]  ),  createElement('ElFormItem', null, [  createElement(  'ElButton',  {  props: {  type: 'primary'  },  on: {  click: this.$_handleSubmit  }  },  '查询'  )  ])  ]  )  } } 复制代码

看到上面的代码,你可能会惊呼,代码好多啊,好痛苦,想当年发明JSX的人刚开始每天也是写createElement,写的直掉头发,太痛苦了,而后就使劲挠头,当额头锃光发亮的时候,终于想到了一种新的语法,就是JSX。今后以后,头发呼呼的又长回来了(本段纯属虚构)。

看到上面代码,你会发现有一个render函数,这个函数叫作渲染函数,至关于经过createElementJSX去实现功能的主入口方法。并且你熟悉的v-model也没见了,而是用value + input代替了。

是时候使用JSX代替createElement

看到上面用createElement去实现组件,太麻烦了,别说工做效率提升了,就是那些嵌套能够嵌套正确就很赞了,因此咱们须要用JSX去简化整个逻辑。

methods: {
 $_handleInputUser(value) {  this.formInline.user = value  },  $_handleChangeRegion(value) {  this.formInline.region = value  },  $_handleSubmit() {} },  /**  *将 h 做为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。从 Vue 的 Babel 插件的 3.4.0 *版本开始,咱们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入  *const h = this.$createElement,这样你就能够去掉 (h) 参数了。对于更早版本的插件,若是 h 在当前做用域中不可用,应用会抛错。  */ render(h) {  return (  <ElForm inline model={this.formInline} class="demo-form-inline">  <ElFormItem label="审批人">  <ElInput  value={this.formInline.user}  onInput={this.$_handleInputUser}  placeholder="审批人"  ></ElInput>  </ElFormItem>  <ElFormItem label="活动区域">  <ElSelect  value={this.formInline.region}  onChange={this.$_handleChangeRegion}  placeholder="活动区域"  >  <ElOption label="区域一" value="shanghai"></ElOption>  <ElOption label="区域二" value="beijing"></ElOption>  </ElSelect>  </ElFormItem>  <ElFormItem>  <ElButton type="primarty" onClick={this.$_handleSubmit}>  查询  </ElButton>  </ElFormItem>  </ElForm>  )  } 复制代码

看了上面的代码,你们其实会发现用JSXtemplate的语法都属于xml的写法,并且也比较像,但实质上仍是有许多区别的,下面小编将为你们一一分析

没有v-model怎么办,还有其余指令能够用吗?

当你选择使用JSX的时候,你就要作好和指令说拜拜的时候了,在JSX中, 你惟一可使用的指令是v-show,除此以外,其余指令都是不可使用的,有没有感到很慌,这就对了。不过呢,换一个角度思考,指令只是Vue在模板代码里面提供的语法糖,如今你已经能够写Js了,那些语法糖用Js均可以代替了。

v-model

v-modelVue提供的一个语法糖,它本质上是由 value属性(默认) + input事件(默认)组成的,若是对自定义v-model不了解的同窗建议阅读小编的文章进行了解 绝对干货~!学会这些Vue小技巧,能够早点下班和女神约会了。 因此,在JSX中,咱们即可以回归本质,经过传递value属性并监听input事件来实现数据的双向绑定

export default {
 data() {  return {  name: ''  }  },  methods: {  // 监听 onInput 事件进行赋值操做  $_handleInput(e) {  this.name = e.target.value  }  },  render() {  // 传递 value 属性 并监听 onInput事件  return <input value={this.name} onInput={this.$_handleInput}></input>  } } 复制代码

经小编测试,在新版脚手架vue-cli4中,已经默认集成了对v-model的支持,你们能够直接使用<input v-model={this.value}>,若是你的项目比较老,也能够安装插件babel-plugin-jsx-v-model来进行支持

一样的,在JSX中,对于.sync也须要用属性+事件来实现,以下代码所示:

export default {
 methods: {  $_handleChangeVisible(value) {  this.visible = value  }  },  render() {  return (  <ElDialog  title="测试.sync"  visible={this.visible}  on={{ 'update:visible': this.$_handleChangeVisible }}  ></ElDialog>  )  } } 复制代码

v-if 与 v-for

在模板代码里面咱们经过v-for去遍历元素,经过v-if去判断是否渲染元素,在jsx中,对于v-for,你可使用for循环,array.map来代替,对于v-if,可使用if语句,三元表达式等来代替

循环遍历列表

const list = ['java', 'c++', 'javascript', 'c#', 'php']
return (  <ul>  {list.map(item => {  return <li>{item}</li>  })}  </ul> ) 复制代码

使用条件判断

const isGirl = false
return isGirl ? <span>小妹,哥哥教你写Vue</span> : <span>鸟你干啥</span> 复制代码

v-bind

在模板代码中,咱们通常经过 v-bind:prop="value":prop="value"来给组件绑定属性,在JSX里面写法也相似

render() {
 return <input value={this.name}></input> } 复制代码

v-html 与 v-text

在说v-htmlv-text以前,咱们须要先了解一下Vue中的属性,Vue中的属性一共分为三种,第一种是你们写bug时候最经常使用的props,即组件自定义的属性;第二种是attrs,是指在父做用域里面传入的,但并未在子组件内定义的属性。第三种比较特殊,是domProps,经小编不彻底测试,在Vue中,domProps主要包含三个,分别是innerHTML,textContent/innerTextvalue

  • v-html: 在模板代码中,咱们用v-html指令来更新元素的innerHTML内容,而在JSX里面,若是要操纵组件的innerHTML,就须要用到domProps

    export default {
     data() {  return {  content: '<div>这是子君写的一篇新的文章</div>'  }  },  render() {  // v-html 指令在JSX的写法是 domPropsInnerHTML  return <div domPropsInnerHTML={this.content}></div>  } } 复制代码
  • v-text: 看了上面的v-html,你是否是当即就想到了v-textJSX的写法domPropsInnerText,是的,你没有想错

    export default {
     data() {  return {  content: '这是子君写的一篇新的文章的内容'  }  },  render() {  return <div domPropsInnerText={this.content}></div>  } } 复制代码

    但实际上咱们不须要使用domPropsInnerText,而是将文本做为元素的子节点去使用便可

    <div>{this.content}</div> 复制代码

实际上,对于domProps,只有innerHTML才须要使用domPropsInnerHTML的写法,其余使用正常写法便可

我还要监听事件呢

监听事件与原生事件

当咱们开发一个组件以后,通常会经过this.$emit('change')的方式对外暴露事件,而后经过v-on:change的方式去监听事件,很遗憾,在JSX中你没法使用v-on指令,但你将解锁一个新的姿式

render() {
 return <CustomSelect onChange={this.$_handleChange}></CustomSelect>  } 复制代码

JSX中,经过on + 事件名称的大驼峰写法来监听,好比事件icon-click,在JSX中写为onIconClick

有时候咱们但愿能够监听一个组件根元素上面的原生事件,这时候会用到.native修饰符,有点绝望啊,修饰符也是不能用了,但好在也有替代方案,以下代码

render() {
 // 监听下拉框根元素的click事件  return <CustomSelect nativeOnClick={this.$_handleClick}></CustomSelect>  } 复制代码

监听原生事件的规则与普通事件是同样的,只须要将前面的on替换为nativeOn

除了上面的监听事件的方式以外,咱们还可使用对象的方式去监听事件

render() {
 return (  <ElInput  value={this.content}  on={{  focus: this.$_handleFocus,  input: this.$_handleInput  }}  nativeOn={{  click: this.$_handleClick  }}  ></ElInput>  )  } 复制代码

事件修饰符

和指令同样,除了个别的以外,大部分的事件修饰符都没法在JSX中使用,这时候你确定已经习惯了,确定有替代方案的。

  • .stop : 阻止事件冒泡,在JSX中使用event.stopPropagation()来代替

  • .prevent:阻止默认行为,在JSX中使用event.preventDefault() 来代替

  • .self:只当事件是从侦听器绑定的元素自己触发时才触发回调,使用下面的条件判断进行代替

    if (event.target !== event.currentTarget){
     return } 复制代码
  • .enterkeyCode: 在特定键触发时才触发回调

    if(event.keyCode === 13) {
     // 执行逻辑 } 复制代码

除了上面这些修饰符以外,尤大大为了照顾咱们这群CV仔,仍是作了一点优化的,对于.once,.capture,.passive,.capture.once,尤大大提供了前缀语法帮助咱们简化代码

render() {
 return (  <div  on={{  // 至关于 :click.capture  '!click': this.$_handleClick,  // 至关于 :input.once  '~input': this.$_handleInput,  // 至关于 :mousedown.passive  '&mousedown': this.$_handleMouseDown,  // 至关于 :mouseup.capture.once  '~!mouseup': this.$_handleMouseUp  }}  ></div>  )  } 复制代码

对了,还有插槽

插槽就是子组件中提供给父组件使用的一个占位符,插槽分为默认插槽,具名插槽和做用域插槽,下面小编依次为你带来每种在JSX中的用法与如何去定义插槽。

默认插槽

  • 使用默认插槽

使用element-uiDialog时,弹框内容就使用了默认插槽,在JSX中使用默认插槽的用法与普通插槽的用法基本是一致的,以下代码所示:

render() {
 return (  <ElDialog title="弹框标题" visible={this.visible}>  {/*这里就是默认插槽*/}  <div>这里是弹框内容</div>  </ElDialog>  )  } 复制代码
  • 自定义默认插槽

    Vue的实例this上面有一个属性$slots,这个上面就挂载了一个这个组件内部的全部插槽,使用this.$slots.default就能够将默认插槽加入到组件内部

    export default {
     props: {  visible: {  type: Boolean,  default: false  }  },  render() {  return (  <div class="custom-dialog" vShow={this.visible}>  {/**经过this.$slots.default定义默认插槽*/}  {this.$slots.default}  </div>  )  } } 复制代码

具名插槽

  • 使用具名插槽

    有时候咱们一个组件须要多个插槽,这时候就须要为每个插槽起一个名字,好比element-ui的弹框能够定义底部按钮区的内容,就是用了名字为footer的插槽

render() {
 return (  <ElDialog title="弹框标题" visible={this.visible}>  <div>这里是弹框内容</div>  {/** 具名插槽 */}  <template slot="footer">  <ElButton>肯定</ElButton>  <ElButton>取消</ElButton>  </template>  </ElDialog>  )  } 复制代码
  • 自定义具名插槽

在上节自定义默认插槽时提到了$slots,对于默认插槽使用this.$slots.default,而对于具名插槽,可使用this.$slots.footer进行自定义

render() {
 return (  <div class="custom-dialog" vShow={this.visible}>  {this.$slots.default}  {/**自定义具名插槽*/}  <div class="custom-dialog__foolter">{this.$slots.footer}</div>  </div>  )  } 复制代码

做用域插槽

  • 使用做用域插槽

    有时让插槽内容可以访问子组件中才有的数据是颇有用的,这时候就须要用到做用域插槽,在JSX中,由于没有v-slot指令,因此做用域插槽的使用方式就与模板代码里面的方式有所不一样了。好比在element-ui中,咱们使用el-table的时候能够自定义表格单元格的内容,这时候就须要用到做用域插槽

    data() {
     return {  data: [  {  name: '子君'  }  ]  }  },  render() {  return (  {/**scopedSlots即做用域插槽,default为默认插槽,若是是具名插槽,将default该为对应插槽名称便可*/}  <ElTable data={this.data}>  <ElTableColumn  label="姓名"  scopedSlots={{  default: ({ row }) => {  return <div style="color:red;">{row.name}</div>  }  }}  ></ElTableColumn>  </ElTable>  )  } 复制代码
  • 自定义做用域插槽

    使用做用域插槽不一样,定义做用域插槽也与模板代码里面有所不一样。加入咱们自定义了一个列表项组件,用户但愿能够自定义列表项标题,这时候就须要将列表的数据经过做用域插槽传出来。

    render() {
     const { data } = this  // 获取标题做用域插槽  const titleSlot = this.$scopedSlots.title  return (  <div class="item">  {/** 若是有标题插槽,则使用标题插槽,不然使用默认标题 */}  {titleSlot ? titleSlot(data) : <span>{data.title}</span>}  </div>  )  } 复制代码

只能在render函数里面使用JSX

固然不是,你能够定义method,而后在method里面返回JSX,而后在render函数里面调用这个方法,不只如此,JSX还能够直接赋值给变量,好比下面这段代码

methods: {
 $_renderFooter() {  return (  <div>  <ElButton>肯定</ElButton>  <ElButton>取消</ElButton>  </div>  )  }  },  render() {  const buttons = this.$_renderFooter()  return (  <ElDialog visible={this.visible}>  <div>这里是一大坨内容</div>  <template slot="footer">{buttons}</template>  </ElDialog>  )  } 复制代码

小编,你忘了指令

对不起,我如今补上。

基础用法

虽然大部份内置的指令没法直接在JSX里面使用,可是自定义的指令能够在JSX里面使用,就拿element-uiv-loading指令来讲,能够这样用

render() {
 /**  * 一个组件上面可使用多个指令,因此是一个数组  * name 对应指令的名称, 须要去掉 v- 前缀  * value 对应 `v-loading="value"`中的value  */  const directives = [{ name: 'loading', value: this.loading }]  return (  <div  {...{  directives  }}  ></div>  )  } 复制代码

修饰符

有些指令还可使用修饰符,好比上例中的v-loading,你能够经过修饰符指定是否全屏遮罩,是否锁定屏幕的滚动,这时候就须要这样写 v-loading.fullscreen.lock = "loading"

render() {
 /**  * modifiers指定修饰符,若是使用某一个修饰符,则指定这个修饰符的值为 true  * 不使用能够设置为false或者直接删掉  */  const directives = [  {  name: 'loading',  value: this.loading,  modifiers: { fullscreen: true, lock: false }  }  ]  return (  <div  {...{  directives  }}  ></div>  )  } 复制代码

结语

不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。 ——文森特・梵高

本文使用 mdnice 排版

相关文章
相关标签/搜索