如何在Vue中書寫JSX

oliverbi發表於2019-01-27

簡介

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>
複製程式碼

使用第三方庫

  1. 使用element-uiel-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>
);

}複製程式碼

效果如下:

如何在Vue中書寫JSX

因為在全域性載入了element-ui庫,所以在JSX中可以識別el-button元件。如果是自己編寫的控制元件,需要在components中引入該控制元件,或者像babel-plugin-transform-vue-jsx文件中介紹的,直接在render函式中使用import進來的控制元件,注意這裡使用的控制元件首字母必須是大寫的,外掛才能識別。

import MyButton from './MyButton';
export default {
render() {
return ( <
MyButton>
按鈕<
/MyButton>
);

}
};
複製程式碼
  1. 使用element-uiel-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>
);

}複製程式碼
  1. 使用element-uiel-loading元件

JSX中使用自定義指令傳遞argumentmodifiers的寫法比較繁瑣,以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
}
這種寫法是生效的,不能設定argumentmodifiers等引數。

  1. 使用element-uiel-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回撥事件沒有指定的報錯。控制檯輸出結果如下:

如何在Vue中書寫JSX

通過簡單改寫事件回撥方法,將事件處理的過程放入一個匿名函式並返回,就可以實現事件內聯處理的效果。

來源:https://juejin.im/post/5bf6ea4df265da611c269ee0

相關文章