簡介
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
回撥事件沒有指定的報錯。控制檯輸出結果如下: