您好,登錄后才能下訂單哦!
今天小編給大家分享一下Vue中的JSX如何使用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
JSX是一種Javascript的語法擴展,即具備了Javascript
的全部功能,同時又兼具html
的語義化和直觀性。它可以讓我們在JS中寫模板語法:
const el = <div>Vue 2</div>;
上面這段代碼既不是 HTML 也不是字符串,被稱之為 JSX,是 JavaScript 的擴展語法。JSX 可能會使人聯想到模板語法,但是它具備 Javascript 的完全編程能力。
當開始寫一個只能通過 level
prop 動態生成標題 (heading) 的組件時,你可能很快想到這樣實現:
<script type="text/x-template" id="anchored-heading-template">
<h2 v-if="level === 1">
<slot></slot>
</h2>
<h3 v-else-if="level === 2">
<slot></slot>
</h3>
<h4 v-else-if="level === 3">
<slot></slot>
</h4>
</script>
這里用template模板并不是最好的選擇,在每一個級別的標題中重復書寫了部分代碼,不夠簡潔優雅。如果嘗試用 JSX 來寫,代碼就會變得簡單很多:
const App = {
render() {
const tag = `h${this.level}`
return <tag>{this.$slots.default}</tag>
}
}
或者如果你寫了很多 render
函數,可能會覺得下面這樣的代碼寫起來很痛苦:
createElement(
'anchored-heading', {
props: {
level: 1
}
}, [
createElement('span', 'Hello'),
' world!'
]
)
特別是對應的模板如此簡單的情況下:
<anchored-heading :level="1">
<span>Hello</span> world!
</anchored-heading>
這時候就可以在 Vue 中使用 JSX 語法,它可以讓我們回到更接近于模板的語法上:
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
在開發過程中,經常會用到消息提示組件Message,可能的一種寫法是這樣的:
Message.alert({
messge: '確定要刪除?',
type: 'warning'
})
但是希望message
可以自定義一些樣式,這時候你可能就需要讓Message.alert
支持JSX
了(當然也可以使用插槽/html
等方式解決)
Message.alert({
messge: <div>確定要刪除<span style="color:red">xxx</span>的筆記?</div>,
type: 'warning'
})
此外,一個 .vue
文件里面只能寫一個組件,這個在一些場景下可能不太方便,很多時候寫一個頁面的時候其實可能會需要把一些小的節點片段拆分到小組件里面進行復用,這些小組件其實寫個簡單的函數組件就能搞定了。平時可能會由于SFC的限制讓我們習慣于全部寫在一個文件里,但不得不說可以嘗試一下這種方式。
// 一個文件寫多個組件
const Input = (props) => <input {...props} />
export const Textarea = (props) => <input {...props} />
export const Password = (props) => <input type="password" {...props} />
export default Input
比如這里封裝了一個 Input 組件,我們希望同時導出 Password 組件和 Textarea 組件來方便用戶根據實際需求使用,而這兩個組件本身內部就是用的 Input 組件,只是定制了一些 props。在 JSX 里面就很方便,寫個簡單的函數組件基本上就夠用了,通過 interface 來聲明 props 就好了。但是如果是用模板來寫,可能就要給拆成三個文件,或許還要再加一個 index.js
的入口文件來導出三個組件。
由于 JSX 的本質就是 JavaScript,所以它具有 JavaScript 的完全編程能力。再舉個例子,我們需要通過一段邏輯來對一組 DOM 節點做一次 reverse,如果在模板里面寫,那估計要寫兩段代碼。
雖然這個例子可能不太常見,但是不得不承認,在一些場景下,JSX 還是要比模板寫起來更加順手。
從 Vue 2 開始,template 在運行之前,會被編譯成 JavaScript 的 render function
。
Vue 推薦在絕大多數情況下使用 template 來創建你的 HTML。然而在一些場景中,就需要使用 render 函數,它比 template 更加靈活。這些 render function
在運行時階段,就是傳說中的 Virtual DOM
。
在 Vue 2 中,JSX 的編譯需要依賴 @vue/babel-preset-jsx
和 @vue/babel-helper-vue-jsx-merge-props
這兩個包。前面這個包來負責編譯 JSX 的語法,后面的包用來引入運行時的 mergeProps
函數。
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
并在babel.config.js中添加配置:
module.exports = {
presets: ['@vue/babel-preset-jsx'],
}
模板代碼里文本插值默認是用雙大括號:
<h2>{{ msg }}</h2>
在JSX中則需要使用單大括號:
const name = 'Vue'
const element = <h2>Hello, { name }</h2>
和模板語法中的文本插值一樣,大括號內支持任何有效的JS表達式,比如:2 + 2
,user.firstName
,formatName(user)
等。
在模板代碼里面我們通過v-for
去遍歷元素,通過v-if
去判斷是否渲染元素,在JSX中,對于v-for
,可以使用for
循環或者array.map
來代替,對于v-if
,可以使用if-else
語句,三元表達式
等來代替
使用if-else
語句
const element = (name) => {
if (name) {
return <h2>Hello, { name }</h2>
} else {
return <h2>Hello, Stranger</h2>
}
}
使用三元表達式
const element = icon ? <span class="icon"></span> : null;
使用數組的map方法
const list = ['java', 'c++', 'javascript', 'c#', 'php']
return (
<ul>
{list.map(item => {
return <li>{item}</li>
})}
</ul>
)
在模板代碼中,一般通過 v-bind:prop="value"
或:prop="value"
來給組件綁定屬性,在JSX
里面就不能繼續使用v-bind指令了,而是通過單大括號的形式進行綁定:
const href = 'https://xxx.com'
const element = <a href={href}>xxx</a>
const properties = {a: 1, b: 2}
此外,模板代碼中能通過<div v-bind="properties"></div>
批量綁定標簽屬性。
在JSX中也有相應的替換方案:<div {...properties}></div>
。
class綁定同樣也是使用單大括號的形式
const element = <div className={`accordion-item-title ${ disabled ? 'disabled' : '' }`}></div>
const element = <div class={
[ 'accordion-item-title', disabled && 'disabled' ]
}
>Item</div>
style綁定需要使用雙大括號
const width = '100px'
const element = <button style={{ width, fontSize: '16px' }}></button>
在模板代碼中通過v-on指令監聽事件,在JSX中通過on
+ 事件名稱的大駝峰寫法來監聽,且綁定事件也是用大括號,比如click事件要寫成onClick
,mouseenter事件要寫成onMouseenter
const confirm = () => {
// 確認提交
}
<button onClick={confirm}>確定</button>
有時候我們希望可以監聽一個組件根元素上面的原生事件,這時候會用到.native
修飾符,但是在JSX中同樣也不能使用,不過也有替代方案,監聽原生事件的規則與普通事件是一樣的,只需要將前面的on
替換為nativeOn
,如下
render() {
// 監聽下拉框根元素的click事件
return <CustomSelect nativeOnClick={this.handleClick}></CustomSelect>
}
除了上面的監聽事件的方式之外,我們還可以使用對象的方式去監聽事件
render() {
return (
<ElInput
value={this.content}
on={{
focus: this.handleFocus,
input: this.handleInput
}}
nativeOn={{
click: this.handleClick
}}
></ElInput>
)
}
對于 .passive
、.capture
和 .once
這些事件修飾符,Vue 提供了相應的前綴可以用于 on
:
例如:
on: {
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
}
對于所有其它的修飾符,私有前綴都不是必須的,因為你可以在事件處理函數中使用事件方法:
大多數指令并不能在JSX中使用,對于原生指令,只有v-show
是支持的。
而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>
}
}
此外,在腳手架vue-cli4
中,已經默認集成了對v-model
的支持,可以直接使用<input v-model={this.value}>
,如果項目比較老,也可以安裝插件babel-plugin-jsx-v-model
來進行支持。
同樣的,在JSX
中,對于.sync
也需要用屬性+事件來實現,如下代碼所示:
export default {
methods: {
handleChangeVisible(value) {
this.visible = value
}
},
render() {
return (
<ElDialog
title="測試.sync"
visible={this.visible}
on={{ 'update:visible': this.handleChangeVisible }}
></ElDialog>
)
}
}
(1)默認插槽:
使用element-ui
的Dialog
時,彈框內容就使用了默認插槽,在JSX
中使用默認插槽的用法與普通插槽的用法基本是一致的,如下
render() {
return (
<ElDialog title="彈框標題" visible={this.visible}>
{/*這里就是默認插槽*/}
<div>這里是彈框內容</div>
</ElDialog>
)
}
自定義默認插槽:
在Vue
的實例this
上面有一個屬性$slots
,這個上面就掛載了一個這個組件內部的所有插槽,使用this.$slots.default
就可以將默認插槽加入到組件內部
export default {
props: {
visible: {
type: Boolean,
default: false
}
},
render() {
return (
<div class="custom-dialog" vShow={this.visible}>
{/**通過this.$slots.default定義默認插槽*/}
{this.$slots.default}
</div>
)
}
}
(2)具名插槽
有時候我們一個組件需要多個插槽,這時候就需要為每一個插槽起一個名字,比如element-ui
的彈框可以定義底部按鈕區的內容,就是用了名字為footer
的插槽
render() {
return (
<ElDialog title="彈框標題" visible={this.visible}>
<div>這里是彈框內容</div>
{/** 具名插槽 */}
<template slot="footer">
<ElButton>確定</ElButton>
<ElButton>取消</ElButton>
</template>
</ElDialog>
)
}
自定義具名插槽:
在上節自定義默認插槽時提到了$slots
,對于默認插槽使用this.$slots.default
,而對于具名插槽,可以使用this.$slots.footer
進行自定義
render() {
return (
<div class="custom-dialog" vShow={this.visible}>
{this.$slots.default}
{/**自定義具名插槽*/}
<div class="custom-dialog__foolter">{this.$slots.footer}</div>
</div>
)
}
(3)作用域插槽
有時讓插槽內容能夠訪問子組件中才有的數據是很有用的,這時候就需要用到作用域插槽,在JSX
中,因為沒有v-slot
指令,所以作用域插槽的使用方式就與模板代碼里面的方式有所不同了。比如在element-ui
中,我們使用el-table
的時候可以自定義表格單元格的內容,這時候就需要用到作用域插槽
data() {
return {
data: [
{
name: 'xxx'
}
]
}
},
render() {
return (
{/**scopedSlots即作用域插槽,default為默認插槽,如果是具名插槽,將default該為對應插槽名稱即可*/}
<ElTable data={this.data}>
<ElTableColumn
label="姓名"
scopedSlots={{
default: ({ row }) => {
return <div style="color:red;">{row.name}</div>
}
}}
></ElTableColumn>
</ElTable>
)
}
自定義作用域插槽:
使用作用域插槽不同,定義作用域插槽也與模板代碼里面有所不同。加入我們自定義了一個列表項組件,用戶希望可以自定義列表項標題,這時候就需要將列表的數據通過作用域插槽傳出來。
render() {
const { data } = this
// 獲取標題作用域插槽
const titleSlot = this.$scopedSlots.title
return (
<div class="item">
{/** 如果有標題插槽,則使用標題插槽,否則使用默認標題 */}
{titleSlot ? titleSlot(data) : <span>{data.title}</span>}
</div>
)
}
只需要導入進來,不用再在components屬性聲明了,直接寫在jsx中:
import MyComponent from './my-component'
export default {
render() {
return <MyComponent>hello</MyComponent>
},
}
我們可以定義method
,然后在method
里面返回JSX
,然后在render
函數里面調用這個方法,不僅如此,JSX
還可以直接賦值給變量,比如:
methods: {
renderFooter() {
return (
<div>
<ElButton>確定</ElButton>
<ElButton>取消</ElButton>
</div>
)
}
},
render() {
const buttons = this.renderFooter()
return (
<ElDialog visible={this.visible}>
<div>內容</div>
<template slot="footer">{buttons}</template>
</ElDialog>
)
}
假設該消息聊天記錄的消息類型只有三種:文本,圖片,引用。一條消息里面可以包括任意類型的內容,引用類型消息內部可以不斷嵌套引用其他任意類型消息。效果圖大致如下:
消息數據結構如下:
message: [
// 每個數組的第一個參數為消息類型:0:文本 1:圖片 2:引用。第二個參數為具體內容
[
0,
'文本'
],
[
1,
'圖片鏈接xxx'
],
[
2,
[
[
0,
'引用文本文本文本'
],
[
1,
'引用圖片鏈接xxx'
]
]
]
]
主要有兩個思路:
1、思路一:在render里返回一段用array.map渲染的消息模板,對于三種消息類型,使用if-else進行判斷分別渲染,對于引用類型的消息,可以封裝一個方法進行渲染,方法里面如果還有引用類型消息就繼續遞歸渲染。
methods: {
// 展示引用消息
showQuote (msg) {
return (
<div class="content-quote">
<span class="quote-title">引用:</span>
{msg.map(item => {
if (item[0] === 0) {
return <p class="content-text">{item[1]}</p>
} else if (item[0] === 1) {
return (
<el-image
class="content-img"
src={item[1]}
preview-src-list={[item[1]]}>
</el-image>
)
} else {
return this.showQuote(item[1])
}
})}
</div>
)
}
},
render (h) {
return (
<ul class="chat-record-list">
{this.recordList.map(item => {
return (
<li
class="chat-record-item"
key={item.timeStamp}
>
<div class="title">
<span class="person-info">
{ `${item.sendUserNick}(${item.sendUserNet}) → ${item.receiverNick}(${item.receiverNet})` }
</span>
<span class="sendtime">
{ this.formatTime('YYYY-mm-dd HH:MM:SS', item.timeStamp) }
</span>
</div>
<div class="content">
{item.message.map(msg => {
if (msg[0] === 0) {
return <p class="content-text">{msg[1]}</p>
} else if (msg[0] === 1) {
return (
<el-image
class="content-img"
src={msg[1]}
preview-src-list={[msg[1]]}>
</el-image>
)
} else {
// 遞歸渲染引用類型消息
return this.showQuote(msg[1])
}
})}
</div>
</li>
)
})}
</ul>
)
}
2、思路二:第一種思路中封裝的showQuote里面的代碼與render中渲染消息內容的代碼基本相似,因此其實現方式不夠優雅。其實可以將整個消息的渲染封裝成一個組件,在該組件內引入自己,然后再渲染自己。由于具體細節代碼與上述類似,這里只給出思路代碼,具體細節請忽略
// 當前組件就是RecordMessage組件,自己引入自己
import RecordMessage from './RecordMessage.vue'
export default {
props: {
message: {
type: Array,
default: () => []
}
},
render () {
const parseMessage = msg => {
const type = msg[0]
if (type === 0) {
// 文本
} else if (type === 2) {
// 圖片
} else {
// 引用類型
return (
<div>
<div>引用:</div>
{
msg[1].map(subMsg => (
// 自己遞歸渲染自己
<recored-message>
</recored-message>
))
}
</div>
)
}
}
return parseMessage(this.message)
}
以上就是“Vue中的JSX如何使用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。