前陣子在 Vue3 項目中封裝一個基礎組件的時候用到了 JSX 語法,使用下來的感受就是 —— 某些場景下,JSX 的靈活性對我們編寫代碼還是能夠帶來一定的幫助的。
舉兩個常見的例子:
假設我們現在有如下數據,需要渲染其中的 name 字段:
const data = [
{
name: 'name1',
children: [{ name: 'name1-1' }]
},
{ name: 'name2' }
]
如果使用普通模板寫法,為了遞歸我們可能不得不編寫兩個組件:
父組件 parent.vue:
// parent.vue 父組件
<template>
<div>我是父組件</div>
<Children v-for="item in data" :subData="item" :key="item.name"></Children>
</template>
子組件 children.vue 遞歸調用自身:
// children.vue 子組件
<template>
<span>{{ subData.name }}</span>
// 遞歸調用
<template v-if="subData.children">
<Children v-for="item in subData.children" :subData="item" :key="item.name"></Children>
</template>
</template>
而使用 JSX 則可以靈活地使用一個文件實現遞歸的邏輯:
// name.jsx
const renderChildren = (data: any) => {
return data.map((item: any) => {
if (item.children) {
return renderChildren(item.children)
} else {
return <span>{ item.name }</span>
}
})
}
const render = () => (
<>
<div>我是父組件</div>
{
data.map((item: any) => {
return (
<>
<span>{ item.name }</span>
{ item.children && renderChildren(item.children) }
</>
)
})
}
</>
)
這是一個來自 vue 官網中的例子,如果你需要根據傳入的 level 動態生成 <h1></h1> 到 <h6></h6> 之間的標簽。
你可能會這樣寫:
<template>
<h1 v-if="level === 1"></h1>
<h2 v-else-if="level === 2"></h2>
<h3 v-else-if="level === 3"></h3>
<h4 v-else-if="level === 4"></h4>
<h5 v-else-if="level === 5"></h5>
<h6 v-else-if="level === 6"></h6>
</template>
但是如果學會了 JSX 的寫法,就可以像這樣:
const render = () => {
const level = props.level
const Tag = `h${level}`
return (
<Tag></Tag>
)
}
是不是瞬間簡潔了許多!省下來的時間又可以用來愉快的摸魚啦。
那么接下來我們就來一起看看,如何在 Vue 中使用 JSX吧!
如果是使用 vue-cli 搭建的項目,默認就是支持 JSX 語法的,直接使用就可以。
如果不是 vue-cli 搭建的 webpack 項目,需要按照如下步驟開啟:
npm install @vue/babel-plugin-jsx -D
在 babel 的配置文件中添加:
{
"plugins": ["@vue/babel-plugin-jsx"]
}
根據版本的不同,babel 配置文件可能是 .babelrc 或者 babel.config.js,注意區分。
使用 vite 的項目,同樣需要先安裝插件:
npm install @vitejs/plugin-vue-jsx -D
然后在 vite.config.js 文件中添加以下配置:
// vite.config.js
import vueJsx from '@vitejs/plugin-vue-jsx'
export default {
plugins: [
vueJsx({
// options are passed on to @vue/babel-plugin-jsx
}),
],
}
更多的配置請參考 babel-plugin-jsx。
在 JSX 語法中,可以通過一對大括號 {} 來使用 JS 表達式:
const name = 'zhangsan'
// 通過一對大括號 {} 來包裹 JS 表達式內容
const list1 = <div>{name}</div>
// 同樣可以通過大括號 {} 來給標簽傳遞動態屬性
const id = 1
const list2 = <div id={id}>{name}</div>
或許你還看到過雙大括號 {{}} 這種令人迷惑的寫法,其實它表示 綁定的是個 JS 對象:
const name = 'zhangsan'
// 雙大括號 {{}} 表示的是綁定的是個 JS 對象
// 可以拆分成 {} 和 { width: '100px' } 來理解
const list1 = <div style={{width:'100px'}}>{name}</div>
Vue3 新增了新特性 Fragment,使得我們在模板語法中能夠返回多個根節點:
<template>
<div>Fragment</div>
<span>yes</span>
</template>
Vue 的編譯器在編譯時,會把這種包含多個根節點的模板被表示為一個片段(Fragment)。
但是在 JSX 中,一組元素必須被包裹在一個閉合標簽中返回;因此下面這種寫法是不允許的:
// 錯誤寫法 ?
const render = () => (
<div>Fragment</div>
<span>yes</span>
)
正確做法是用一對閉合標簽包裹:
// 正確寫法 ?
const render = () => (
<div>
<div>Fragment</div>
<span>yes</span>
</div>
)
那如果我們不想引入額外的標簽該怎么辦呢?可以用 <></> 來包裹我們想要返回的內容,如下:
const render = () => (
<>
<div>Fragment</div>
<span>yes</span>
</>
)
乍一看,你是不是覺得 <></> 和我們在使用模板寫法時的 <template></template> 作用很相似?
但是實際上,JSX 中被 <></> 標簽包裹的內容,會被當做 Fragment 來處理;并且針對 Fragment Vue 在編譯和渲染時會有特定的優化策略。
而對于 <template></template>,Vue 只會將其作為一個普通的元素渲染;所以要注意別搞混咯。
在 Vue2 的時代,使用 JSX 時傳遞屬性還是比較麻煩的。
因為 Vue2 中將屬性又細分成了 組件屬性、HTML Attribute 以及 DOM Property 等等,不同的屬性寫法也大相徑庭,如下:
const render = () => {
return (
<div
// 傳遞一個 HTML Attribute,屬性名稱是 id 屬性值是 'foo'
id="foo"
// 傳遞 DOM Property 需要使用前綴 `domProps` 來表示,這里表示傳遞給 innerHTML 這個 DOM 屬性的值為 ‘bar’
domPropsInnerHTML="bar"
// 綁定原生事件需要以 `on` 或者 `nativeOn` 為前綴,相當于 @click.native
onClick={this.clickHandler}
nativeOnClick={this.nativeClickHandler}
// 綁定自定義事件需要用 `props` + 事件名 的方式
propsOnCustomEvent={this.customEventHandler}
// class(類名)、style(樣式)、key、slot 和 ref 這些特殊屬性寫法
class={{ foo: true, bar: false }}
style={{ color: 'red', fontSize: '14px' }}
slot="slot"
key="key"
ref="ref"
// 如果是循環生成的 ref(相當于 v-for),那么需要添加 refInFor 這個標識
// 用來告訴 Vue 將 ref 生成一個數組,否則只能獲取到最后一個
refInFor>
</div>
)
}
而在 Vue3 的 JSX 中傳遞各種屬性的方式已經簡化了許多,下面會拆開細講,各位小伙伴們請接著往下看~
傳遞 DOM Property 時去掉了 domProps 前綴,可以直接書寫:
const render = () => {
return (
<div innerHTML="bar"></div>
)
}
Vue3 JSX 傳遞 HTML Attribute 與 Vue2 JSX 相同,直接書寫就行:
const render = () => {
return (
<div id="foo" type="email"></div>
)
}
如果需要動態綁定:
const placeholderText = 'email';
const render = () => {
return (
<input
type="email"
placeholder={placeholderText}
/>
)
};
Vue3 JSX 傳遞類名與樣式的方法,與 Vue2 JSX 相同:
const render = () => {
return (
<div
class={{ foo: true, bar: false }}
style={{ color: 'red', fontSize: '14px' }}
>
</div>
)
}
定義好 ref 后用 JS 表達式綁定就行:
const divRef = ref()
const render = () => {
return (
<div ref={divRef}></div>
)
}
在 Vue 模板寫法中,綁定事件時我們使用 v-on 或者 @ 符號:
<template>
<div @click="handleClick">事件綁定</div>
</template>
而在 Vue3 的 JSX 中,會把以 on 開頭,并緊跟著大寫字母的屬性當作事件監聽器來解析;
上面的模板寫法換成 JSX 就是:
const render = (
<div onClick={handleClick}>事件綁定</div>
)
注意:這里一定要以 on 開頭,并且緊跟著大寫字母。
錯誤寫法 ?:onclick、click;
正確寫法 ?:onClick、onClickChange、onClick-change (雖然但是,不會真的有人這么寫吧?)
如果你不喜歡這種寫法,還可以通過打開 babel 的 transformOn 配置,然后通過屬性 on 綁定一個對象,一次性傳遞多個事件:
// babel 配置
{
"plugins": [
[
"@vue/babel-plugin-jsx",
{
"transformOn": true
}
]
]
}
// 通過屬性 on 綁定對象 批量傳遞多個事件
const render = () => (
<div on={{ click: handleClick, input: handleInput }}> 事件綁定 </div>
)
對于 .passive、.capture 和 .once 事件修飾符,可以使用駝峰寫法將他們拼接在事件名稱后面。
比如 onClick + Once,就代表監聽 click 事件觸發,且只觸發一次:
const render = () => (
<input
onClickCapture={() => {}}
onKeyupOnce={() => {}}
onMouseoverOnceCapture={() => {}}
/>
)
而像其余的 .self、.prevent 等事件和按鍵修飾符,則需要使用 withModifiers 函數。
withModifiers 函數接收兩個參數:
import { withModifiers } from 'vue'
const count = ref(0)
const render = () => (
<input
onClick={
withModifiers(
// 第一個參數是回調函數
() => count.value++,
// 第二個參數是修飾符組成的數組
['self', 'prevent']
)
}
/>
)
上面的寫法就相當于我們在 Vue 模板中這樣寫:
<template>
<input @click.stop.prevent="() => count++" />
</template>
在 JSX 中是沒有 v-for 這個自定義指令的,我們需要用 map 方法來替代:
const render = () => (
<ul>
{
items.value.map(({ id, text }) => {
return (
<li key={id}>{text}</li>
)
})
}
</ul>
)
上面的寫法,其實就相當于我們在模板中這樣寫:
<ul>
<li v-for="{ id, text } in items" :key="id">
{{ text }}
</li>
</ul>
同樣,在 JSX 中也是沒有 v-if 這個指令的~
但是細想一下,其實 v-if 的功能就是做判斷嘛,比如我們的模板長這樣:
<div>
<div v-if="ok">yes</div>
<span v-else>no</span>
</div>
那么換成用 JSX 就可以使用 三元表達式 或者 && 連接符 來實現這個功能,我們可以這樣寫:
// 使用三元表達式
const render = () => (
<div>
{ ok.value ? <div>yes</div> : <span>no</span> }
</div>
)
// 使用 && 連接符
const render = () => (
<div>
{ ok.value && <div>yes</div> }
{ !ok.value && <span>no</span> }
</div>
)
可以直接使用v-show 指令,也可以寫成 vShow 這種形式:
const show = ref(false)
// v-show
const render = () => (
<div v-show={show}></div>
)
// 或者 vShow
const render = () => (
<div vShow={show}></div>
)
正常情況下與我們在模板中使用 v-model 無異:
const value = ref('')
const render = () => (
<input v-model={value} />
)
const value = ref('')
// 將默認的 arg 從 modelValue 修改為 childrenProp
const render = () => (
<input v-model:childrenProp={value} />
)
但如果你需要在 JSX 中使用 v-model 的內置修飾符,如.lazy、.trim、.number,那么你需要傳遞一個數組:
const render = () => (
<input v-model={[value, ['trim']]} />
)
如果你想同時修改默認 arg,并且使用修飾符;那么傳遞的數組的第二個參數需要定義為你設置的 arg,且是個字符串:
const render = () => (
<input v-model={[value, 'childrenProp', ['trim']]} />
)
上面的寫法相當于模板中:
<input v-model:childrenProp.trim="value"></input>
JSX 中自定義指令的使用方法和 v-model 十分相似,只需要把 v-model 替換成你對應的自定義指令就可以啦:
const App = {
directives: { custom: customDirective },
setup() {
const value = ref()
return () => <children v-custom:childrenProp={value} />;
},
};
帶修飾符的自定義指令,寫法如下:
const App = {
directives: { custom: customDirective },
setup() {
const value = ref()
return () => (
<children v-custom={[value, 'childrenProp', ['a', 'b']]} />;
)
},
};
在 Vue3 的 jsx 中,使用插槽同樣分為兩步走:
首先,在接收插槽的組件中,給插槽留個“座位”;我們可以從 setup 函數的第二個參數解構出 slots,拿到外部傳入的所有插槽:
// 自定義組件 customComp.jsx
export default {
name: 'CustomComp',
props: ['message'],
// 外部傳入的插槽信息都在 slots 中
setup(props, { slots }) {
return () => (
<>
// 傳入的 default 默認插槽會被展示這里,如果沒有傳入默認插槽,則展示文本 foo
<h1>{slots.default ? slots.default() : 'foo'}</h1>
// 傳入的具名插槽 bar 會被展示在這里
<h2>{slots.bar?.()}</h2>
// 作用域插槽 footer,外部可以通過作用域拿到 text 的值
<h2>{slots.footer?.({ text: props.message })}</h2>
</>
)
}
}
在 Vue3 的 jsx 中傳入插槽時,需要使用 v-slots 代替 v-slot:
// 定義好我們需要的插槽
const slots = {
// 這部分內容會傳到具名插槽 bar 中
bar: () => <span>B</span>,
// 這部分內容會傳到作用域插槽 footer 中
footer: ({ text }) => <span>{text}</span>
};
const render = () => (
// 使用 v-slots 將定義好的插槽 slots 傳入自定義組件 CustomComp
<CustomComp v-slots={slots}>
// 這部分內容,會傳入組件 CustomComp 的默認插槽中
<div>A</div>
</CustomComp>
);
或者還可以直接用一個對象同時定義好默認插槽和具名插槽:
export default {
setup() {
// 將默認插槽和具名插槽用對象的形式定義好:
const slots = {
default: () => <div>A</div>,
bar: () => <span>B</span>,
// 作用域插槽,能夠拿到對應的信息 text
footer: ({ text }) => <span>{text}</span>
};
// 直接使用 v-slots 將整個插槽對象傳遞給自定義組件 CustomComp
return () => <CustomComp v-slots={slots} />
}
}
另外,如果 babel 的配置項 enableObjectSlots 不為 false 時,傳入多個插槽還可以寫成對象的形式,對象的 key 為插槽名稱, value 為一個函數,函數的返回就是插槽的默認占位:
修改 babel 配置:
{
"plugins": [
[
"@vue/babel-plugin-jsx",
{
"enableObjectSlots": true
}
]
]
}
jsx 具體寫法:
const render = () => (
<>
<CustomComp>
{{
// 給自定義組件 CustomComp 傳入一個默認插槽,內容是 <div>A</div>
default: () => <div>A</div>,
// 給自定義組件 CustomComp 傳入一個具名插槽 bar,內容是 <span>B</span>
bar: () => <span>B</span>,
// 給自定義組件 CustomComp 傳入一個具名插槽 footer,內容是 <span>B</span>
footer: ({ text }) => <span>{ text }</span>,
}}
</CustomComp>
// 相當于: {{ default: () => 'foo' }},給自定義組件 CustomComp 傳入了一個默認插槽
<CustomComp>{() => 'foo'}</CustomComp>
</>
)
以函數的形式傳遞插槽,子組件就可以懶調用這些插槽了。
在函數式組件中使用 JSX 十分簡單,直接返回相關內容就行:
const App = () => <div></div>;
我們知道 Vue3 中的 setup 函數如果 返回的是個函數,那么這個函數的返回值會被作為模板內容渲染,并且會忽略 template 的內容。
因此在普通的單文件組件中,我們可以直接在 setup 函數中返回我們的 JSX 內容:
import { defineComponent } from 'vue';
const App = defineComponent({
setup() {
const count = ref(0);
return () => (
<div>{count.value}</div>
);
},
});
以上就是 Vue3 中使用 JSX 的全部內容啦!如果文章對你有幫助的話,還希望各位小伙伴不要吝嗇你的贊~
另外,如果文章有紕漏 or 你有任何疑惑,都歡迎在評論區討論。
作者:hprep
鏈接:https://juejin.cn/post/7272308621710213161
文:https://www.cnblogs.com/dadouF4/p/10032888.html
JavaScript:
基本概念:
JavaScript一種直譯式腳本語言,是一種動態類型、弱類型、基于原型的語言,內置支持類型。它的解釋器被稱為JavaScript引擎,為瀏覽器的一部分,廣泛用于瀏覽器客戶端的腳本語言。
組成部分
ECMAScript,描述了該語言的語法和基本對象。
文檔對象模型(DOM),描述處理網頁內容的方法和接口。
瀏覽器對象模型(BOM),描述與瀏覽器進行交互的方法和接口。
基本特點
JavaScript是一種屬于網絡的腳本語言,已經被廣泛用于Web應用開發,常用來為網頁添加各式各樣的動態功能,為用戶提供更流暢美觀的瀏覽效果。通常JavaScript腳本是通過嵌入在HTML中來實現自身的功能的。
是一種解釋性腳本語言(代碼不進行預編譯)。
主要用來向HTML(標準通用標記語言下的一個應用)頁面添加交互行為。
可以直接嵌入HTML頁面,但寫成單獨的js文件有利于結構和行為的分離。
跨平臺特性,在絕大多數瀏覽器的支持下,可以在多種平臺下運行(如Windows、Linux、Mac、Android、iOS等)。
Javascript腳本語言同其他語言一樣,有它自身的基本數據類型,表達式和算術運算符及程序的基本程序框架。Javascript提供了四種基本的數據類型和兩種特殊數據類型用來處理數據和文字。而變量提供存放信息的地方,表達式則可以完成較復雜的信息處理。
日常用途
嵌入動態文本于HTML頁面。
對瀏覽器事件做出響應。
讀寫HTML元素。
在數據被提交到服務器之前驗證數據。
檢測訪客的瀏覽器信息。
控制cookies,包括創建和修改等。
基于Node.js技術進行服務器端編程。
TypeScript:
基本概念:
TypeScript是一種由微軟開發的自由和開源的編程語言。它是JavaScript的一個超集,而且本質上向這個語言添加了可選的靜態類型和基于類的面向對象編程。安德斯·海爾斯伯格,C#的首席架構師,已工作于TypeScript的開發。
TypeScript擴展了JavaScript的語法,所以任何現有的JavaScript程序可以不加改變的在TypeScript下工作。TypeScript是為大型應用之開發而設計,而編譯時它產生 JavaScript 以確保兼容性。
TypeScript 支持為已存在的 JavaScript 庫添加類型信息的頭文件,擴展了它對于流行的庫如 jQuery,MongoDB,Node.js 和 D3.js 的好處。
特性
類 Classes
接口 Interfaces
模塊 Modules
類型注解 Type annotations
編譯時類型檢查 Compile time type checking
Arrow 函數 (類似 C# 的 Lambda 表達式)
JavaScript 與 TypeScript 的區別
TypeScript 是 JavaScript 的超集,擴展了 JavaScript 的語法,因此現有的 JavaScript 代碼可與 TypeScript 一起工作無需任何修改,TypeScript 通過類型注解提供編譯時的靜態類型檢查。
TypeScript 可處理已有的 JavaScript 代碼,并只對其中的 TypeScript 代碼進行編譯。
JSX:
JSX就是Javascript和XML結合的一種格式。React發明了JSX,利用HTML語法來創建虛擬DOM。當遇到<,JSX就當HTML解析,遇到{就當JavaScript解析。
jsx常用語法:
擊上方藍字關注“小鄭搞碼事”,每天都能學到知識,搞懂一個問題!
從寫React項目開始,我們熟悉了一種語法,叫JSX,它很好的將邏輯和模板融合在一起。當然,有一點我們應該很清楚,在React項目開發的時候,我們可以使用JSX來編寫代碼,也可以使用純JavaScript來編寫代碼,這樣的話,即使不學JSX也可以正常開發React項目。
然而,facebook官方推薦我們使用JSX來寫React代碼。為什么呢?下面從四個方面介紹,帶你學會使用JSX。
HTML類型的標簽第一個字母用小寫來表示,React組件標簽第一個字母用大寫來表示。
比如:
上面代碼是一個HTML類型的標簽定義,需要知道兩點:
第一,當一個標簽里面內容為空的時候,可以直接使用自閉和標簽;
第二,因為JSX本身是JavaScript語法,所以一些JavaScript中的保留字要用其他的方式書寫,就像上面class要寫成className。
這里寫的是一個React組件,注意名字第一個字母大寫。
標簽對標簽(或組件名),{}對JS代碼,無屬性默認為true
什么意思了?就是說在JSX語法中,當遇到標簽的時候就解釋成組件或HTML標簽,當遇到{}包裹的時候就當成JavaScript代碼來執行。
比如:
上面代碼中,{}內會當表達式處理,isLoggIn為true時輸出組件<Nav/>
否則輸出<LoginForm/>
當省略一個屬性的值的時候,如<Nav isDisable/>
。JSX會自動把它的值認為是true(isDisable=true)。
在子組件中寫注釋的時候要用{}
比如:
用“三點”操作符來簡化多個屬性的編寫。舉個例子:
顯然,上面這種寫法,當出現更多屬性的時候,會顯得很難看。下面看一個更優雅的寫法。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。