Vue2.0 + JSX

JSX 简介

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

应用场景

为了让大家更方便的去理解JSX的作用及用法,小编先为大家罗列了几个可能会用到JSX的应用场景。

在消息框内添加html
在开发过程中,经常会用到消息框,使用消息框可能的一种写法是这样的

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函数,这个函数叫做渲染函数,相当于通过createElement或JSX去实现功能的主入口方法。而且你熟悉的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>
    )
  }

看了上面的代码,大家其实会发现用JSX与template的语法都属于xml的写法,而且也比较像,但实质上还是有许多区别的

猜你喜欢

转载自blog.csdn.net/weixin_45959504/article/details/107865735
JSX