简介
Vue
中可以通过render函数代替template
来获得完全的JavaScript
编程能力。Vue官网上锚点标题的例子,说明了render
函数在某些场景下可以有效地简化代码。我们可以通过createElement
函数来编写render
函数,但是createElement
的写法过于繁琐,逻辑稍微复杂一点就会产生一堆代码,而且不易阅读。官方文档中指出可以通过Babel插件,在render
函数中使用JSX
语法,让代码更接近模板语法。Babel
转化插件官方文档已经对JSX
语法进行了说明,本文将结合实例说明如何在Vue
中书写JSX
。
建立demo项目
通过Vue CLI
快速创建demo项目
vue create learn-vue-jsx
复制代码
默认的@vue/babel-preset-app
已经包含了转化JSX
语法的插件
@babel/plugin-syntax-jsx
babel-helper-vue-jsx-merge-props
babel-plugin-transform-vue-jsx
复制代码
引入element-ui
库,对在使用第三方库中涉及JSX
的用法进行说明
vue add element
复制代码
书写JSX
从最简单的例子开始,template版本和render函数版本的Hello World
template版本
<template>
<p
id="helloWorld"
:class="{'hello-world': true}"
:style="{'color': 'red'}"
@click="onClick">
{{this.msg}}
</p>
</template>
<script>
export default {
data() {
return {
msg: 'Hello World'
}
},
methods: {
onClick() {
alert('Hello World');
}
}
}
</script>
复制代码
render函数版本
<script>
export default {
data() {
return {
msg: 'Hello World'
}
},
methods: {
onClick() {
alert('Hello World');
}
},
render() {
return (
<p
id="helloWorld"
class={{'hello-world': true}}
style={{'color': 'red'}}
onClick={this.onClick}>
{this.msg}
</p>
);
}
}
</script>
复制代码
使用第三方库
- 使用
element-ui
的el-button
组件
template版本
<el-button
size="medium"
type="primary"
round
loading>
按钮
</el-button>
复制代码
render函数版本
render() {
return (
<el-button
type="primary"
size="medium"
round
loading>
按钮
</el-button>
);
}
复制代码
效果如下:
因为在全局加载了element-ui
库,所以在
JSX
中可以识别
el-button
组件。如果是自己编写的控件,需要在
components
中引入该控件,或者像
babel-plugin-transform-vue-jsx
文档中介绍的,直接在
render
函数中使用import进来的控件,注意这里使用的控件
首字母必须是
大写的,插件才能识别。
import MyButton from './MyButton';
export default {
render() {
return (
<MyButton>按钮</MyButton>
);
}
};
复制代码
- 使用
element-ui
的el-input
组件
template版本
<div>
<el-input
v-model="input"
placeholder="请输入内容">
</el-input>
<p>{{input}}</p>
</div>
复制代码
render函数版本
methods: {
onInput(value) {
this.input = value;
}
},
render() {
return (
<div>
<el-input
value={this.input}
placeholder="请输入内容"
onInput={this.onInput}>
</el-input>
<p>{this.input}</p>
</div>
);
}
复制代码
大部分的Vue
的内置指令在JSX
都是不支持的,所以需要用其他方式实现。像v-model
指令其实是value
属性和input
事件的语法糖。v-if
指令可以使用if
语句实现,v-for
指令可以使用array.map
语句实现。比较例外的是v-show
指令可以在JSX
使用。具体例子如下:
template版本
<div>
<p>v-if指令</p>
<div v-if="isIf">v-if指令内容</div>
<p>v-for指令</p>
<div v-for="item in list">{{item}}</div>
<p>v-show指令</p>
<div v-show="isShow">v-show指令内容</div>
</div>
复制代码
render函数版本
render() {
return (
<div>
<p>v-if指令</p>
{
this.isIf ? <div>v-if指令内容</div> : ''
}
<p>v-for指令</p>
<div>
{
this.list.map((item) => {
return item
})
}
</div>
<p>v-show指令</p>
<div v-show="isShow">v-show指令内容</div>
</div>
);
}
复制代码
- 使用
element-ui
的el-loading
组件
在JSX
中使用自定义指令传递argument
和modifiers
的写法比较繁琐,以el-loading组价的指令方式为例:
template版本
<el-button
type="primary"
@click="openFullScreen"
v-loading.fullscreen.lock="fullscreenLoading">
全屏Loading
</el-button>
复制代码
render函数版本
render() {
const directives = [
{
name: 'loading',
value: this.fullscreenLoading,
modifiers: { fullscreen: true, lock: true }
}
];
return (
<el-button
type="primary"
onClick={this.openFullScreen}
{...{ directives}}>
全屏Loading
</el-button>
);
}
复制代码
babel-plugin-transform-vue-jsx
在官方文档中还介绍了另一种书写Vue
指令的方法,但是尝试只有v-loading={this.fullscreenLoading}
这种写法是生效的,不能设置argument
和modifiers
等参数。
- 使用
element-ui
的el-table
组件
el-table
组件提供了自定义表头和自定义列模板的能力,可以通过自定义插槽实现。具体例子如下:
template版本
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column label="日期" prop="date"></el-table-column>
<el-table-column label="姓名" prop="name"></el-table-column>
<el-table-column>
<template slot="header">
<el-input v-model="search" size="mini" placeholder="输入关键字搜索"/>
</template>
<template slot-scope="scope">
<el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [
{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
},
{
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
},
{
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}
],
search: ""
};
},
methods: {
handleEdit(index, row) {
console.log(index, row);
},
handleDelete(index, row) {
console.log(index, row);
}
}
};
</script>
复制代码
render函数版本
<script>
export default {
data() {
return {
tableData: [
{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
},
{
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
},
{
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}
],
search: ''
};
},
methods: {
updateSearch(value) {
this.search = value;
},
handleEdit(index, row) {
return () => {
console.log(index, row);
};
},
handleDelete(index, row) {
return () => {
console.log(index, row);
};
}
},
render() {
let that = this;
return (
<el-table data={this.tableData} style={{ width: '100%' }}>
<el-table-column label="日期" prop="date"></el-table-column>
<el-table-column label="姓名" prop="name"></el-table-column>
<el-table-column {...{
scopedSlots: {
header: scope => {
return (
<el-input size="mini" placeholder="输入关键字搜索" value={that.search} onInput={that.updateSearch}/>
);
},
default: scope => {
return [
<el-button size="mini" onClick={that.handleEdit(scope.$index, scope.row)}>编辑</el-button>,
<el-button size="mini" type="danger" onClick={that.handleDelete(scope.$index, scope.row)}>删除</el-button>
];
}
}
}}>
</el-table-column>
</el-table>
);
}
};
</script>
复制代码
这个例子比较复杂,主要涉及了v-model
指令、作用域插槽、事件内联处理等。v-model
指令通过设置value
属性和监听input
事件来实现。作用域插槽的JSX
写法可以参考官网渲染函数 & JSX的插槽章节进行理解。网上一直没有找到对于事件内联处理的JSX
写法介绍,如果按照template
模板的方式编写事件内联处理,即类似这种格式onClick={that.handleEdit(scope.$index, scope.row)}
,会发现在第一次渲染时就触发了事件回调方法,并有click
回调事件没有指定的报错。控制台输出结果如下:
所有评论(0)