一文詳解如何在Vue3+Vite中使用JSX
JSX介紹
JSX(JavaScript 和 XML),是一個(gè) HTML-in-JavaScript 的語(yǔ)法擴(kuò)展,首先在 React 中被進(jìn)入。JSX 可以很好地描述 UI 應(yīng)該呈現(xiàn)出它應(yīng)有交互的本質(zhì)形式。JSX 是在 JavaScript 語(yǔ)法上的拓展,因此類似于 HTML 的代碼可以和 JS 共存。例如:
const button = <MyButton color="blue" shadowSize={2}> Click Me </MyButton>
該 button 常量稱為 JSX 表達(dá)式??梢允褂盟谖覀兊膽?yīng)用程序中渲染 <MyButton>
標(biāo)簽。瀏覽器是無(wú)法讀取直接解析 JSX 的。JSX 表達(dá)式經(jīng)過(guò)( Babel 或 Parcel 之類的工具)編譯之后是這樣的:
React.createElement( MyButton, {color: 'blue', shadowSize: 2}, 'Click Me' )
實(shí)際上,JSX 僅僅只是 React.createElement(component, props, ...children) 函數(shù)的語(yǔ)法糖??梢允褂?React.createElement() 自己編寫 UI 來(lái)跳過(guò)編譯步驟。但是,這樣做會(huì)失去 JSX 的聲明性優(yōu)勢(shì),并且代碼變得更難以閱讀。編譯是開(kāi)發(fā)過(guò)程中的一個(gè)額外步驟,但是 React 社區(qū)中的許多開(kāi)發(fā)人員都認(rèn)為 JSX 的可讀性值得。另外,流行的工具使 JSX-to-JavaScript 編譯成為其設(shè)置過(guò)程的一部分。除非您愿意,否則不必自己配置編譯
React 并不強(qiáng)制要求使用 JSX。當(dāng)你不想在構(gòu)建環(huán)境中配置有關(guān) JSX 編譯時(shí),不在 React 中使用 JSX 會(huì)更加方便。例如,用 JSX 編寫的代碼:
class Hello extends React.Component { render() { return <div>Hello {this.props.toWhat}</div>; } } const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<Hello toWhat="World" />);
可以編寫為不使用 JSX 的代碼:
class Hello extends React.Component { render() { return React.createElement('div', null, `Hello ${this.props.toWhat}`); } } const root = ReactDOM.createRoot(document.getElementById('root')); root.render(React.createElement(Hello, {toWhat: 'World'}, null));
在 Vue3 中使用 JSX
Vue 使用單文件組件,把 template 模板、相關(guān)腳本和 CSS 一起整合放在 .vue 結(jié)尾的一個(gè)單文件中。這些文件最終會(huì)通過(guò) JS 打包或構(gòu)建工具(例如 Webpack、Vite)處理。
<template>
元素包含了所有的標(biāo)記結(jié)構(gòu)和組件的展示邏輯。template 可以包含任何合法的 HTML,以及 Vue 特定的語(yǔ)法。通過(guò)設(shè)置<template>
標(biāo)簽的lang
屬性,例如可以通過(guò)設(shè)置<template lang="pug">
就可以在使用 Pug 模板來(lái)替代標(biāo)準(zhǔn) HTML。而 .vue 文件中的
<script>
標(biāo)簽包含組件中所有的非顯示邏輯,并且需要默認(rèn)導(dǎo)出一個(gè) JS 對(duì)象。該對(duì)象是在本地注冊(cè)組件、定義屬性、處理本地狀態(tài)、定義方法等的地方。在構(gòu)建階段這個(gè)包含了 template 模板的對(duì)象會(huì)被處理和轉(zhuǎn)換成為一個(gè)有 render() 函數(shù)的 Vue 組件。組件的 CSS 樣式寫在
<style>
標(biāo)簽里,如果添加了scoped
屬性,Vue 會(huì)把樣式的范圍限制到單文件組件的內(nèi)容里。這是類似于 CSS-in-JS 的解決方案,只不過(guò)允許書寫純粹的 CSS。如果通過(guò) CLI 創(chuàng)建項(xiàng)目時(shí)選擇了 CSS 預(yù)處理器,則可以將lang
屬性添加到<style>
標(biāo)簽中,以便 Webpack 可以在構(gòu)建時(shí)處理內(nèi)容。
雖然 jsx 最早是由 React 引入,但實(shí)際上 JSX 語(yǔ)法并沒(méi)有定義運(yùn)行時(shí)語(yǔ)義,并且能被編譯成各種不同的輸出形式。如果你之前使用過(guò) JSX 語(yǔ)法,那么請(qǐng)注意 Vue 的 JSX 編譯方式與 React 中 JSX 的編譯方式不同,因此不能在 Vue 應(yīng)用中使用 React 的 JSX 編譯。與 React JSX 語(yǔ)法的一些明顯區(qū)別包括:
- 可以使用 HTML attributes 比如
class
和for
作為 props - 不需要使用className
或htmlFor
。 - 傳遞子元素給組件 (比如 slots) 的方式不同。
Vue 的類型定義也提供了 TSX 語(yǔ)法的類型推導(dǎo)支持。當(dāng)使用 TSX 語(yǔ)法時(shí),確保在 tsconfig.json
中配置了 "jsx": "preserve"
,這樣的 TypeScript 就能保證 Vue JSX 語(yǔ)法編譯過(guò)程中的完整性。
安裝插件(@vitejs/plugin-vue-jsx)
vite 官方提供了官方的插件來(lái)支持在 vue3 中使用 jsx/tsx,直接安裝就行。
npm i @vitejs/plugin-vue-jsx -D
安裝完之后在 vite.config.js 文件中的 plugins 字段中添加 jsx 支持:
import vueJsx from "@vitejs/plugin-vue-jsx"; export default defineConfig({ plugins: [ vueJsx(), ] })
這樣就可以在項(xiàng)目中使用 jsx/tsx 了。
新建 jsx 文件
在項(xiàng)目中新建 jsx 或 tsx 后綴的文件,語(yǔ)法和 js 文件類似,但是和 .vue 文件中的 <script>
標(biāo)簽一樣需要默認(rèn)導(dǎo)出一個(gè) JS 對(duì)象。該對(duì)象是在本地注冊(cè)組件、定義屬性、處理本地狀態(tài)、定義方法等的地方。
import HelloWorld from './HelloWorld.vue' export default { setup() { return () => <HelloWorld msg="11" />; }, };
語(yǔ)法
1、插值。與 vue 模板語(yǔ)法中的插值一樣,但是雙大括號(hào) {{}} 變?yōu)榱藛未罄ㄌ?hào) {}。大括號(hào)內(nèi)支持任何有效的 JavaScript 表達(dá)式,比如:2 + 2,user.firstName,formatName(user) 等。
// 模板語(yǔ)法 <span>{{ a + b }}</span> // jsx/tsx <span>{ a + b }</span>
2、class 類名綁定。有兩種方式,使用模板字符串或者使用數(shù)組。
// 模板字符串 <div className={ `header ${ isBg ? 'headerBg' : '' }` }>header</div> // 數(shù)組 <div class={ [ 'header', isBg && 'headerBg' ] } >header</div>
3、style 樣式綁定。需要使用雙大括號(hào)。
const color = 'red' const element = <sapn style={{ color, fontSize: '16px' }}>style</sapn>
4、條件渲染。由于 jsx 本身具有 js 語(yǔ)法,所以不再需要使用 v-if 指令,使用 if/else 和三元表達(dá)式都可以實(shí)現(xiàn)。但是支持 v-show 指令。
const element = (name) => { if (name) { return <h1>Hello, { name }</h1> } else { return <h1>Hello, Stranger</h1> } } const element = icon ? <span class="icon"></span> : null; // 以上代碼等效于: const element = icon && <span class="icon"></span>;
5、列表渲染。同樣,由于 jsx 本身具有 js 語(yǔ)法,所以不再需要使用 v-for 指令,使用 JS 數(shù)組的 map 方法即可。
const listData = [ {name: 'Tom', age: 18}, {name: 'Jim', age: 20}, {name: 'Lucy', age: 16} ] return () => ( <div> <div class={'box'}> <span>姓名</span> <span>年齡</span> </div> { prop.listData.map(item => <div class={'box'}> <span>{item.name}</span> <span>{item.age}</span> </div> }) </div> )
6、標(biāo)簽屬性綁定。也是使用大括號(hào)包裹,不能使用 v-bind 指令。而 vue 組件中通過(guò) <div v-bind="properties"></div>
批量綁定標(biāo)簽屬性,在 JSX 中需要使用 <div {...properties}></div>
。
const const element = <a href={href}>Vue3</a>
7、事件綁定。使用的也是 單大括號(hào) {},不過(guò)事件綁定不是以 @為前綴了,而是改成了 on,與原生相同。例如:click 事件是 onClick 或 onclick。
const confirm = () => { // 確認(rèn)提交 } <button onClick={confirm}>確定</button>
如果要帶參數(shù),需要使用箭頭函數(shù)進(jìn)行包裹:
const confirm = (name) => { // 確認(rèn)提交 } <button onClick={() => confirm('Are you sure')}>確定</button>
8、事件修飾符。需要使用 withModifiers 方法,接收兩個(gè)參數(shù),第一個(gè)參數(shù)是綁定的事件,第二個(gè)參數(shù)是需要使用的事件修飾符。
import { withModifiers, defineComponent, ref } from 'vue' const App = defineComponent({ setup() { const count = ref(0); const inc = () => { count.value++; }; return () => ( <div onClick={ withModifiers(inc, ['self']) }>{ count.value }</div> ); }, }) export default App
注意:Vue 模板中 ref 變量是可以直接解構(gòu)的,但是在 jsx 中不行,需要添加 .value,比如上面的 { count.value }。
9、v-model 雙向綁定。需要使用單大括號(hào) {}。如果綁定屬性則需要一個(gè)數(shù)組,第一個(gè)元素為綁定的值,第二個(gè)元素為綁定的屬性。
// 綁定值 <input v-model="show" /> // vue <input v-model={show.value} /> // jsx // 綁定屬性 <input v-model:prop="show" /> // vue <input v-model={[show.value,'prop']} /> // jsx // 修飾符寫法 <input v-model:prop.trim="show" /> // vue <input v-model={[show.value,'prop',['trim']]} /> // jsx
10、slot 插槽。jsx/tsx 中無(wú)法使用 slot 標(biāo)簽,定義插槽方式一:通過(guò) setup 函數(shù)的第一個(gè)參數(shù) ctx 上下文對(duì)象的 slots 的屬性,setup 函數(shù)默認(rèn)接收兩個(gè)參數(shù):
- props - 組件傳入的參數(shù)對(duì)象。
- ctx - 上下文對(duì)象,上下文對(duì)象暴露了其他一些在 setup 中可能會(huì)用到的值,包括:
- attrs - 透?jìng)鞯?Attributes(非響應(yīng)式的對(duì)象,等價(jià)于 $attrs)。
- slots - 插槽(非響應(yīng)式的對(duì)象,等價(jià)于 $slots)。
- emit - 觸發(fā)事件的函數(shù)(等價(jià)于 $emit)。
- expose - 暴露公共屬性的函數(shù)。
如果解構(gòu)了 props
對(duì)象,解構(gòu)出的變量將會(huì)丟失響應(yīng)性,因此推薦通過(guò) props.xxx
的形式來(lái)使用其中的 props。如果確實(shí)需要解構(gòu) props
對(duì)象,或者需要將某個(gè) prop 傳到一個(gè)外部函數(shù)中并保持響應(yīng)性,可以使用toRefs()和toRef()這兩個(gè)工具函數(shù):
import { toRefs, toRef } from 'vue' export default { setup(props) { // 將 `props` 轉(zhuǎn)為一個(gè)其中全是 ref 的對(duì)象,然后解構(gòu) const { title } = toRefs(props) // `title` 是一個(gè)追蹤著 `props.title` 的 ref console.log(title.value) // 或者,將 `props` 的單個(gè)屬性轉(zhuǎn)為一個(gè) ref const title = toRef(props, 'title') } }
ctx 上下文對(duì)象是非響應(yīng)式的,可以安全地解構(gòu):
export default { setup(props, { attrs, slots, emit, expose }) { ... } }
attrs 和 slots 都是響應(yīng)式(有狀態(tài))的對(duì)象,它們總是會(huì)隨著組件自身的更新而更新。這意味著你應(yīng)當(dāng)避免解構(gòu)它們,并始終通過(guò) attrs.x 或 slots.x 的形式使用其中的屬性。此外,和 props 不同,attrs 和 slots 的屬性都不是響應(yīng)式的。如果想要基于 attrs 或 slots 的改變來(lái)執(zhí)行副作用,那么應(yīng)該在 onBeforeUpdate 生命周期鉤子中編寫相關(guān)邏輯。
expose 函數(shù)用于顯式地限制該組件暴露出的屬性,當(dāng)父組件通過(guò)模板引用訪問(wèn)該組件的實(shí)例時(shí),將僅能訪問(wèn) expose 函數(shù)暴露出的內(nèi)容:
export default { setup(props, { expose }) { // 讓組件實(shí)例處于 “關(guān)閉狀態(tài)” // 即不向父組件暴露任何東西 expose() const publicCount = ref(0) const privateCount = ref(0) // 有選擇地暴露局部狀態(tài) expose({ count: publicCount }) } }
通過(guò) ctx 上下文對(duì)象的 slots 的屬性可以獲取插槽對(duì)象后,就可以定義插槽了。
import { defineComponent } from 'vue' export default defineComponent({ setup(props, { slots }) { // 邏輯 return () => { return <p> <button>{ slots.test?.() }</button> <button>{ slots.default?.() }</button> </p> } }, }) // 在引用的組件中 <template #test>slot-test</template> <template #>slot-default</template>
定義插槽方式二:使用 renderSlot 函數(shù)。
import { renderSlot } from 'vue' <button> { renderSlot(slots, 'default') } </button>
而如果在 jsx 中使用插槽,可以直接通過(guò)標(biāo)簽屬性 slot,或通過(guò) v-slots 指令。
import HelloWorld from './HelloWorld' export default defineComponent({ setup() { return () => ( <div class={'box'}> <HelloWorld v-slots={{ title: () => { return <p>我是title插槽</p> }, default: () => { return <p>我是default插槽</p> } }} /> </div> ) } })
11、CSS Modules。引入局部樣式,相當(dāng)于 vue 組件中 <style>
標(biāo)簽的 scoped 屬性。
import styles from './index.module.scss' <div class={styles.wrap}></div>
補(bǔ)充知識(shí):注意事項(xiàng)
在使用JSX之前需要引入 JSX 的包并引用
// 再導(dǎo)入之前 先導(dǎo)入 @vitejs/plugin-vue pnpm i @vitejs/plugin-vue // 在vite.config 中導(dǎo)入并配置 @vitejs/plugin-vue import vue from '@vitejs/plugin-vue' export default { plugins: [vue()], }
注意: 再導(dǎo)入 JSX 之前 vite 的版本必須在4.0.0之后
// 導(dǎo)入 '@vitejs/plugin-vue-jsx' pnpm i @vitejs/plugin-vue-jsx // 在vite.config 中導(dǎo)入并配置 @vitejs/plugin-vue-jsx import vueJsx from '@vitejs/plugin-vue-jsx' export default { plugins: [ vueJsx({ // options are passed on to @vue/babel-plugin-jsx }), ], }
總結(jié)
到此這篇關(guān)于如何在Vue3+Vite中使用JSX的文章就介紹到這了,更多相關(guān)Vue3+Vite使用JSX內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
element-ui中el-table不顯示數(shù)據(jù)的問(wèn)題解決
這篇文章主要介紹了element-ui中el-table不顯示數(shù)據(jù)的問(wèn)題解決,這是最近在做列表首頁(yè)時(shí)遇到的一個(gè)問(wèn)題,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07vue構(gòu)建單頁(yè)面應(yīng)用實(shí)戰(zhàn)
本篇文章主要介紹了vue構(gòu)建單頁(yè)面應(yīng)用實(shí)戰(zhàn),使用 SPA,沒(méi)有頁(yè)面切換,就沒(méi)有白屏阻塞,可以大大提高 H5 的性能,達(dá)到接近原生的流暢體驗(yàn)。2017-04-04vue.js使用v-if實(shí)現(xiàn)顯示與隱藏功能示例
這篇文章主要介紹了vue.js使用v-if實(shí)現(xiàn)顯示與隱藏功能,結(jié)合簡(jiǎn)單實(shí)例形式分析了使用v-if進(jìn)行判斷實(shí)現(xiàn)元素的顯示與隱藏功能,需要的朋友可以參考下2018-07-07vue踩坑記錄之?dāng)?shù)組定義和賦值問(wèn)題
這篇文章主要給大家介紹了關(guān)于vue踩坑記錄之?dāng)?shù)組定義和賦值問(wèn)題的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Vue(定時(shí)器)解決mounted不能獲取到data中的數(shù)據(jù)問(wèn)題
這篇文章主要介紹了Vue(定時(shí)器)解決mounted不能獲取到data中的數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07Vue監(jiān)聽(tīng)器簡(jiǎn)單使用及注意事項(xiàng)說(shuō)明
這篇文章主要介紹了Vue監(jiān)聽(tīng)器簡(jiǎn)單使用及注意事項(xiàng)說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08vue3+vite加載本地js/json文件不能使用require淺析
這篇文章主要給大家介紹了關(guān)于vue3+vite加載本地js/json文件不能使用require的相關(guān)資料,require 是屬于 Webpack 的方法,在v3+vite的項(xiàng)目里面并不適用,需要的朋友可以參考下2023-07-07Vue select 綁定動(dòng)態(tài)變量的實(shí)例講解
這篇文章主要介紹了Vue select 綁定動(dòng)態(tài)變量的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10