部落格地址:https://ainyi.com/92
最初用到 JSX,就是做這個部落格的時候。iview 表格元件,不支援像 element 那樣直接寫 html 程式碼渲染,只能通過 render 函式渲染,也就是 JSX 語法
這個說起來不陌生,JSX 是 react 框架的老本行了,玩 react 的同學肯定對這個也玩的很溜(最近在公司做的某些專案也是 react)
那我還是記錄一下在 Vue JSX 的使用吧
JSX 定義
JSX 是一種 JavaScript 的語法擴充套件,多運用於 React 架構中。JSX = Javascript + XML,即在 Javascript 裡面寫 XML,即具備 Javascript 的靈活性,又有 html 的語義化和直觀性
應用場景
有人說,Vue 的模板語法簡單易上手,實現的功能也幾乎足夠。JSX 不好上手,寫起來程式碼量也多,用來幹啥呢
那你就忽略了 JavaScript 的靈活性了
- 一些複雜表單的實現
在這插播一個使用 vue 模板語法實現複雜表單的傳送門:Element 動態渲染、移除表單並新增驗證 & 動態新增、移除驗證 - 函式式元件
函式式元件
簡單說一下函式式元件
函式式元件就是函式是元件。使用過 React 的同學,應該不會對函式式元件感到陌生
函式式元件,我們可以理解為沒有內部狀態,沒有生命週期鉤子函式,沒有 this(不需要例項化的元件)
在日常開發中,經常會開發一些純展示性的業務元件,比如一些詳情頁面,列表介面等,它們有一個共同的特點是隻需要將外部傳入的資料進行展現,不需要有內部狀態,不需要在生命週期鉤子函式裡面做處理,這時候你就可以考慮使用函式式元件
export default {
// 通過配置 functional 屬性指定元件為函式式元件
functional: true,
// 元件接收的外部屬性
props: {
avatar: {
type: String
}
},
/**
* 渲染函式
* @param {*} h
* @param {*} context 函式式元件沒有 this, props, slots 等,都在 context 上面掛著
*/
render(h, context) {
const { props } = context
if (props.avatar) {
return <img src={props.avatar}></img>
}
return <img src="default-avatar.png"></img>
}
}
使用函式式元件的原因:
- 最主要最關鍵的原因是函式式元件不需要例項化,無狀態,沒有生命週期,所以渲染效能要好於普通元件
- 函式式元件結構比較簡單,程式碼結構更清晰
函式式元件與普通元件的區別
- 函式式元件需要在元件上宣告functional
- 函式式元件不需要例項化,所以沒有 this,this通過render函式的第二個引數來代替
- 函式式元件沒有生命週期鉤子函式,不能使用計算屬性、watch 等等
- 函式式元件不能通過 $emit 對外暴露事件,呼叫事件只能通過context.listeners.click的方式呼叫外部傳入的事件
- 因為函式式元件是沒有例項化的,所以在外部通過ref去引用元件時,實際引用的是 HTMLElement
- 函式式元件的props可以不用顯示宣告,所以沒有在props裡面宣告的屬性都會被自動隱式解析為 prop,而普通元件所有未宣告的屬性都被解析到 $attrs 裡面,並自動掛載到元件根元素上面(可以通過 inheritAttrs 屬性禁止)
模板語法宣告函式式元件
在 Vue2.5 之前,使用函式式元件只能通過 JSX 的方式,在之後可以通過模板語法來宣告函式式元件
<!-- 在template 上面新增 functional屬性 -->
<template functional>
<img :src="props.avatar ? props.avatar : 'default-avatar.png'" />
</template>
<!-- 上面第 6 條,可以省略宣告 props -->
瞭解 createElement
學習 JSX 之前,先了解 createElement
字面意思,建立元素 大名鼎鼎的虛擬DOM應該都知道吧,就是它的返回值 => 插播一個以前寫過的VNode傳送門:virtual DOM
關於 createElement 方法,有三個引數:
- 第一個引數主要用於提供 dom 的 html 內容,型別可以是字串、物件或函式。比如 “div” 就是建立一個 div 標籤
- 第二個引數(型別是物件)主要用於設定這個 dom 的一些樣式、屬性、傳的元件的引數、繫結事件之類,具體可以參考 官方文件 裡這一小節的說明
- 第三個引數(型別是陣列,陣列元素型別是 VNode)主要用於說是該節點下有其他結點的話,就放在這裡
使用例子:
export default {
methods: {
$_handleChangeUser(value) {
this.formInline.user = value
}
},
render(h) {
return h(
'ElForm',
{
props: {
inline: true,
model: this.formInline
},
staticClass: 'demo-form-inline'
},
[
h(
'ElFormItem',
{
props: {
label: '使用者名稱'
}
},
[
h('ElInput', {
props: {
value: this.formInline.user
},
attrs: {
placeholder: '請輸入使用者名稱'
},
on: {
input: this.$_handleChangeUser
}
})
]
)
]
)
}
}
看起來寫法十分複雜,若頁面上這麼多元素,頻繁使用 createElement 方法難免程式碼臃腫,這時就應該使用 JSX 代替 createElement 了
JSX
我們再來用 JSX 語法重新實現上面的程式碼
methods: {
$_handleInputUser(value) {
this.formInline.user = value
}
},
render(h) {
return (
<el-form inline model={this.formInline} class="demo-form-inline">
<el-form-item label="使用者名稱">
<el-input
value={this.formInline.user}
onInput={this.$_handleInputUser}
placeholder="請輸入使用者名稱"
></el-input>
</el-form-item>
</el-form>
)
}
跟 react 一模一樣了
將 h 作為 createElement 的別名 是 Vue 生態系統中的一個通用慣例,實際上也是 JSX 所要求的
從 Vue 的 Babel 外掛的 3.4.0 版本開始,我們會在以 ES2015 語法宣告的含有 JSX 的任何方法和 getter 中 (不是函式或箭頭函式中) 自動注入const h = this.$createElement,這樣就可以去掉 (h) 引數了。對於更早版本的外掛,如果 h 在當前作用域中不可用,應用會報錯
Vue JSX 中指令的使用
我們使用 Vue 模板語法,指令用的爽歪歪,像 v-model, v-if, v-for, @, 插槽等等
但是,這些都在 JSX 中無法使用。那麼如何實現相同的功能呢
注意:新版 vue-cli4 中,已經預設整合了 JSX 語法對 v-model 的支援,可以直接使用
<input v-model={this.value}>
如果你的專案比較老,也可以安裝外掛 babel-plugin-jsx-v-model 來進行支援
自定義 v-model
v-model 是 Vue 提供的一個語法糖,它本質上是由 value 屬性 + input 事件組成的(都是原生的預設屬性)
所以在 JSX 中,我們可以通過傳遞 value 屬性並監聽 input 事件來實現資料的雙向繫結
export default {
data() {
return {
name: ''
}
},
methods: {
// 監聽 onInput 事件進行賦值操作
$_handleInput(e) {
this.name = e.target.value
}
},
render() {
// 傳遞 value 屬性 並監聽 onInput事件
return <input value={this.name} onInput={this.$_handleInput}></input>
}
}
封裝元件如下:
子元件
<template>
<div>
<input :value="value" @change="$_handleChange" />
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
data() {
return {}
},
methods: {
$_handleChange(e) {
this.$emit('input', e.target.value)
}
}
}
</script>
父元件
<template>
<div class="home">
<krry-input v-model="say"></krry-input>
</div>
</template>
<script>
export default {
name: 'Home',
components: {
KrryInput: () => import('@/components/KrryInput')
},
data() {
return {
say: 'haha'
}
}
}
</script>
再次重申:新版 vue-cli4 中,已經預設整合了 JSX 語法對 v-model 的支援,可以直接使用 <input v-model={this.value}>
el-form 的 :model 屬性
注意 el-form 的 :model 屬性,在 JSX 中是這樣寫的 props={{ model: this.data }}
,比較特別
<el-form
ref="form"
labelWidth="140px"
rules={this.rules}
props={{ model: this.data }}>
<el-form-item label="券碼編號: ">
<el-input v-model={this.data.voucherCode}></el-input>
</el-form-item>
<el-form>
v-if | v-for
這兩個就比較常見,搞 react 都知道,v-if 可以使用三元表示式,true or false 來渲染元件;v-for 就使用 map 方法來實現
const list = ['宮', '商', '角', '徵', '羽']
return (
<ul>
{ list.map(ele => <li>{ele}</li>) }
</ul>
)
v-html | v-text
在 JSX 裡面,如果要設定 dom 元素的 innerHTML,就用到 domProps
export default {
data() {
return {
content: '<div>這是我的自定義的 html 元素</div>'
}
},
render() {
// v-html 指令在 JSX 的寫法是 domPropsInnerHTML
return <div domPropsInnerHTML={this.content}></div>
}
}
v-text 就沒啥好說的了,<div domPropsInnerText={this.content}></div>
還不如直接使用 <div>{this.content}</div>
監聽事件
監聽事件想到用 onChange, onClick
需要注意的是,傳引數不能使用 onClick={this.removePhone(params)}
,這樣子會每次 render 的時候都會自動執行一次方法
應該使用 bind,或者箭頭函式來傳參
<button type="button" onClick={this.handleClick.bind(this, 11)}></button>
<button type="button" onClick={() => this.handleClick(11)}></button>
除此之外,還可以使用物件的方式去監聽事件
render() {
return (
<el-input
value={this.content}
on={{
focus: this.$_handleFocus,
input: this.$_handleInput
}}
nativeOn={{
click: this.$_handleClick
}}
></el-input>
)
}
使用範圍
不僅僅在 render 函式裡面使用 JSX,而且還可以在 methods 裡面返回 JSX,然後在 render 函式裡面呼叫這個方法
JSX 還可以直接賦值給變數
methods: {
$_renderFooter() {
return (
<div>
<el-button>確定</el-button>
<el-button>取消</el-button>
</div>
)
}
},
render() {
const buttons = this.$_renderFooter()
return (
<el-dialog visible={this.visible}>
<div>彈窗內容</div>
<template slot="footer">{buttons}</template>
</el-dialog>
)
}
部落格地址:https://ainyi.com/92