什么是JSX?
JSX是React出的一种对JavaScript的语法扩展。
在Vue中大多数情况推荐使用模板语法,通过template中的Vue指令进行快速开发。但是template也是存在一些缺陷的,扩展难度大,造成逻辑冗余。这时候我们就需要JavaScript
的完全编程能力,结合render函数
与JSX
进行功能扩展。
基础用法
1. 嵌入表达式
在JSX中,我们可以在大括号中使用任何有效的JS表达式
// 动态渲染内容 let name = "Josh Perez" let element = <h1>Hello {name}</h1> // 动态绑定属性 let imgUrl = 'http://xxx.jpg' let img = <img src={imgUrl} /> 复制代码
在<>
中写HTML标签,在{}
中写js代码
JSX代码本身也是js表达式,它表示的是一个js对象。
// 在vue的render函数中,以下两种写法是等价的 render: function(createElement) { let name = "Josh Perez" let element = <h1 class="title">Hello {name}</h1> return element } // ===》》 render: function(createElement) { let name = "Josh Perez" let element = createElement( 'h1', {class: title}, `Hello ${name}` ) return element }
事实上, Babel会把JSX语法转义成JS对象,也就是上述代码的后一种写法。
2. 绑定事件 & v-on
在JSX中事件的命名采用小驼峰式(camelCase),而不是纯小写。
methods: { deleteRow(id) { // todo } }, render: (h) => { return <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> } 复制代码
3. 条件渲染 & v-if
在JSX中无法使用Vue中的v-if-else
指令,可以使用Javascript中的运算符if
或者三元运算符
代替
// if运算符 let isEnable = props.isEnable render(h) { if(isEnable) { return <button>禁用</button> } else { return <button>启用</button> } }
// 三元运算符 let isEnable = props.isEnable render(h) { return <button>{isEnable ? '禁用' : '启用'}</button> }
4. 列表 & v-for
在JSX中可以通过map
实现v-for
render: (h) => { const numbers = [1, 2, 3, 4, 5] let listItems = numbers.map(number => <li>{number}</li> ) return ( <ul>{listItems}</ul> ) }
实现key
属性的绑定
render: (h) => { const numbers = [1, 2, 3, 4, 5] let listItems = numbers.map(number => <li key={number.toString()}>{number}</li> ) return ( <ul>{listItems}</ul> ) }
在JSX中嵌入map
render: (h) => { const numbers = [1, 2, 3, 4, 5] let listItems = return ( <ul> { numbers.map(number => <li key={number.toString()}>{number}</li> ) } </ul> ) }
实践 & Table组件的封装
Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。但是在开发过程中template
并不能满足我们的所有需求,在一些组件开发中比较常见
下面是我在工作中根据业务场景对Element UI中的table组件进行的扩展,或许可以加深一些对JSX的理解.
在开发后台管理系统时,不可避免的要进行大量数据列表的展示。目前比较流行的第三方组件库 Element UI,其列表渲染的代码如下:
<el-table :data="list"> <el-table-column prop="name" lable="姓名" width="150" align="center"></el-table-column> <el-table-column prop="age" lable="年龄" width="150" align="center"></el-table-column> . . . </el-table>
每一列都需要写一次el-table-column
标签,尤其在处理财务报表这种展示大批量相似数据时,是非常痛苦的。
我们期待的效果是这样的:
<table :data="list" :columns="columns">
data() { return { list: [] columns: [] } }
将列的描述信息放到js中,这样可以省去不少代码,代码结构也会清晰很多。具体实现过程如下:
table组件的封装
基于Element UI 对table组件进行二次封装
<template> <el-table :data="data" v-bind="$attrs"> <template v-for="(column, index) of columns"> <!-- render --> <el-table-column v-if="column.render" v-bind="Object.assign({}, defaultColumnConfig, column)" :key="index"> <template slot-scope="scope"> <extend :render="column.render" :params="scope"></extend> </template> </el-table-column> <!-- no render --> <el-table-column v-else v-bind="Object.assign({}, defaultColumnConfig, column)" :key="index"></el-table-column> </template> </el-table> </template> <script> import extend from './extend.js' export default { components: { extend }, props: { data: { type: Array }, columns: { type: Array }, defaultColumnConfig: { type: Object } } } </script>
通过extend
组件实现render
函数
export default { props: { params: { type: Object }, render: { type: Function } }, render(h) { return this.render(h, this.params) } }
具体用法 & demo
<template> <y-table :data="list" :columns="columns" :defaultColumnConfig="defaultColumnConfig" border></y-table> </template> <script> import YTable from "./index" export default { components: { YTable }, data() { return { list: [ {name: 1, age: 2, sex: 0} ], defaultColumnConfig: { width: 100, align: 'center' }, columns:[ { prop: 'name', label: '姓名', age: 0, width: 200 }, { prop: 'age', label: '年龄', age: 1 }, { label: '性别', render(h,scope) { return h( 'span', { style: {color: 'red'} }, scope.row.sex === 0 ? '男' : '女' ) } }, { label: '性别', render(h,scope) { return ( <span style="color: red">{scope.row.sex === 0 ? '男' : '女'}</span> ) } } ] } } } </script>