Vue 中使用jsx示例

8,743 阅读2分钟

了解什么是JSX

为什么要在Vue中使用jsx呢,首先我们需要了解JSX是什么,它解决了什么问题,最后如何使用它。

jsx的定义

JSX 是一种类似于 XML 的 JavaScript 语法扩展 JSX 不是由引擎或浏览器实现的。相反,我们将使用像 Babel 这样的转换器将 JSX 转换为常规 JavaScript。基本上,JSX 允许我们在 JavaScript 中使用类似 HTML 的语法。

jsx的优势

  1. 可以将 模版分离 这样模版的每个部分更加独立,又可以随机的组合,复用性更高。相比与组件的组合,粒度更细
  2. 使用 js 可配置每项要渲染的 dom,更加动态可配置化

小结

虽然了解了jsx的相关定义,可能对它还是比较陌生,首先要知道无论在 Vue中还是React,使用JSX 都是可选的。如:

// react 中使用jsx
class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.toWhat}</div>;
  }
}

// 同等效果的代码可以如下,只不过是babel帮我们做了这些
class Hello extends React.Component {
  render() {
    return React.createElement('div', null, `Hello ${this.props.toWhat}`);
  }
}

了解Vue组件

在使用 Vue 时通常我们通过new Vue()是创建一个根实例,而组件是可复用的 Vue 实例 它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。

new Vue({
  data: {
    foo: 1
  },
  computed: {
    bar: function () { /* ... */ }
  },
  methods: {
    baz: function () { /* ... */ }
  }
})

为了处理多个组件映射关系,将路由实例对象作为参数传递给 Vue构造函数。new Vue()创建的是根实例,而每个页面组件实例通过 router 来控制。结构如下:

  export default {
    name: '',
    props:{},
    data:{},
    methods:{}
  }

可以看到路由映射的组件其实也是暴露出一个对象,在Vue就是通过一个对象去描述组件。而默认情况下,我们使用 template 作为模版,如果想使用 jsx 来渲染模版写格式如下,其实就是省略来 template 模版,取而代之使用 render 方法 return 要渲染的模版。并且其他都是选填项,render 方法必须返回出要渲染的模版。

export default {
    name: '',
    props:{},
    data:{
        return {
            text:'hello jsx'
        }
    },
    render(){
        return <div>{text}</div>
    },
    methods:{}
  }

Vue 中使用jsx

常用 React 的同学对 jsx 的语法比较熟悉,其实很多用法可以参照 react 的官方文档如下面我们会提到的常用语法

js 环境

在 Vue 模版中渲染一个变量的方式

<div id="app">
  {{ message }}
</div>

包括使用 v-bind 绑定一个变量值

<span v-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>

而在 jsx 中是用 {} 表示的相同的代码表示如下

render(){
    return (
        <div id="app">
            <div>
              { message }
            </div>
            <span title={message}>
                鼠标悬停几秒钟查看此处动态绑定的提示信息!
            </span>
        </div>
    )
}

需要解释一下的是,在 Vue 中 "" 表示一个js环境,你可以在其中做一些简单的运算 <img :src="'/path/to/images/' + fileName">,在 jsx 中{}表示 js 环境。<span>{3+2}</span>

v-if 语法糖

Vue常用的 v-if 来控制dom的显示隐藏,本质就是 dom 的渲染和卸载。那么在 jsx 中如何实现

data(){
    return {
        isShow:false
    }
},
render(){
    return <div>
        { this.isShow ? <span>显示啦</span>:null}
    </div>
}

上面代码中 { } 表示一个js环境,判断this.isShow 的真假来控制 dom 的显示与否

绑定事件

Vue 中使用 v-on 来绑定事件 简写@

<button @click="onButtonClick">按钮</button>

而在jsx中绑定事件前面都会加一个 on- 作为前缀

<button onClick={this.onButtonClick}>按钮</button>
  1. 如何在 jsx 中传参
<button onClick={this.onButtonClick.bind(this,value)}>按钮</button>

上面对面中的 value 就是传递给 onButtonClick 函数的参数。首先通过 { } 建立起js环境,然后通过 this.onButtonClick 拿到定义在 methods 中定义的函数,通过 bind 函数给当前函数传递参数 value 并返回一个待执行的新函数。最终,onClick 绑定的是 bind 返回的新函数。

  1. 如何绑定子组件 emit 的事件,假设在 child.vue 中派发了一个 emit 事件,jsx中如何去绑定监听事件
// father.js
export default {
    components:{
        child
    },
    render(){
        return  <div>
        <child onCancel={this.onCancel} />
    </div>
    }
}
// child.vue
methods:{
    onCancelClick(){
        this.$emit('cancel')
    }
}
  1. 其他类型的事件的绑定 , 类比 onClick 就是前面加上 on-
    <input  onChange={this.onValueChange} />

v-for 枚举实现

在 Vue 中我们可以用 v-for 指令基于一个数组来渲染一个列表。

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>

在 jsx 中在 { } 使用数组的 map 方法遍历生成一个新的数组

<div>
    {this.tagList.map(item => {
        return <div>{item}</div>
    })}
</div>

slot

Vue 中在标签中间添加内容的方式,向组件传递内容,slot 位置内容会被传入的内容替代

// 传值
<alert-box>
  Something bad happened.
</alert-box>

// 接收
Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <slot></slot>
    </div>
  `
})

jsx 中通过 this.$slots 对象可以拿到组件标签中的内容

<Temp>
    123
    <span>ccc</span>
</Temp>
// Temp.js
export default {
  render(){
    return <h1>{this.$slots.default}</h1>
  }
}
  1. 具名插槽
<Temp>
    123
    <span slot="text">ccc</span>
</Temp>
// Temp.js
export default {
  render(){
    return <h1>{this.$slots.text}</h1>
  }
}
  1. 解构插槽

export default {
  data(){
    return {
    list: [
        {
          "id":11,
          "name":"保险百科"
        }
      ],
      propList: [
        {
          props: {
            prop: "name",
            label: "分类名称"
          }
        },
      ]
    }
  },
  render(){
    let slot = {
      scopedSlots :{
        default :props=>{
          return <h1>{props.row.name}</h1>
        }
      }
    }
    return (
      <el-Table data={this.list}  >
      {this.propList.map((item, index) =>{
        return (
          <el-table-column {...item} {...slot} ></el-table-column>
        )
      }
      )}
    </el-Table>
    )
  }
}

props 属性传递

Vue 中父组件传递 props 给子组件 通过 v-bind,如果有多个值需要传递,则需要声明多个props,或者传递一个对象给子组件,

<my-component :prop="someThing"></my-component>

而 jsx 中可以使用... 扩展符来实现多个值的传递,注意传递的属性需要声明在 props 对象下

 render(){
    let value = {
      props:{
        type:'primary',
        disabled:'disabled'
      }
    }
    return (
      <el-button {...value}>按钮</el-button>
    )
  }

codepen 完整示例

codepen 上没有找到 babel-plugin-transform-vue-jsx 插件,无法正常展示,可以copy代码到本地项目使用

参考

babel-plugin-transform-vue-jsx
渲染函数 & JSX