Vue
中能夠經過render函數代替template
來得到徹底的JavaScript
編程能力。Vue官網上錨點標題的例子,說明了render
函數在某些場景下能夠有效地簡化代碼。咱們能夠經過createElement
函數來編寫render
函數,可是createElement
的寫法過於繁瑣,邏輯稍微複雜一點就會產生一堆代碼,並且不易閱讀。官方文檔中指出能夠經過Babel插件,在render
函數中使用JSX
語法,讓代碼更接近模板語法。Babel
轉化插件官方文檔已經對JSX
語法進行了說明,本文將結合實例說明如何在Vue
中書寫JSX
。html
經過Vue CLI
快速建立demo項目vue
vue create learn-vue-jsx
複製代碼
默認的@vue/babel-preset-app
已經包含了轉化JSX
語法的插件git
@babel/plugin-syntax-jsx
babel-helper-vue-jsx-merge-props
babel-plugin-transform-vue-jsx
複製代碼
引入element-ui
庫,對在使用第三方庫中涉及JSX
的用法進行說明github
vue add element
複製代碼
從最簡單的例子開始,template版本和render函數版本的Hello World
編程
template版本element-ui
<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函數版本bash
<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版本babel
<el-button
size="medium"
type="primary"
round
loading>
按鈕
</el-button>
複製代碼
render函數版本app
render() {
return (
<el-button
type="primary"
size="medium"
round
loading>
按鈕
</el-button>
);
}
複製代碼
效果以下: ide
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
回調事件沒有指定的報錯。控制檯輸出結果以下: