簡介
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-jsxbabel-helper-vue-jsx-merge-propsbabel-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
回撥事件沒有指定的報錯。控制檯輸出結果如下:
通過簡單改寫事件回撥方法,將事件處理的過程放入一個匿名函式並返回,就可以實現事件內聯處理的效果。