vue中正確使用jsx語法的姿勢分享
目錄
- 前言
- 虛擬DOM
- 什麼是虛擬DOM
- 虛擬DOM的優點
- 渲染函式是什麼
- x
- 在3中編寫jsx的兩種方式
- 用法
- 最後
- 參考
前言
又到了愉快的摸魚時間,我覺得不能荒廢,H5頁面我一直用的vant,出於對原始碼的好奇,我從git上拉了一份vant原始碼,裡面竟然都是jsx寫的元件,於是我開始了對在vue中使用jsx的探索
虛擬DOM
什麼是虛擬DOM
在這之前,先了解下虛擬DOM,vue和react框架都在內部使用了虛擬DOM,這樣做的原因是通過js操作DOM的計算成本很高,雖然js更新速度很快,但是查詢dom並更新的成本很高。那麼有什麼方法可以優化呢,vue等框架使用js物件,通過改變js物件,最後進行批量處理,一次更新DOM,所以虛擬DOM本質上就是一個js物件
虛擬DOM的優點
- 從原先的操作真實DOM到操作虛擬DOM,降低查詢成本
- 通過diff比對,我們可以更快的定位資料的變化,從而更新DOM
- 更好的ui更新
- 抽象渲染過程,帶來了實現跨平臺的能力,如vue3中的createRenderer API
渲染函式是什麼
渲染函式是用來生成虛擬DOM的。我們在vue單檔案中編寫模板語法,最終會在底層實現中被編譯成渲染函式
Vue 推薦在絕大多數情況下使用模板來建立你的 HTML。然而在一些場景中,你真的需要 的完全的能力。這時你可以用渲染YRvrNllab函式,它比模板更接近編譯器。
當出現以下場景,雖然下列寫法也能實現想要的效果,但是他不僅冗長,而且我們為每個級別標題重複書寫了 。當我們新增錨元素時,我們必須在每個 v-if/v-else-if 分支中再次重複它
const { createApp } = Vue const app = createApp({}) app.component('anchored-heading',{ template: ` <h1 v-if="level === 1"> <slot></slot> </h1> <h2 v-else-if="level === 2"> <slot></slot> </h2> <h3 v-else-if="level === 3"> <slot></slot> </h3> <h4 v-else-if="level === 4"> <slot></slot> </h4> <h5 v-else-if="level=== 5"> <slot></slot> </h5> <h6 v-else-if="level === 6"> <slot></slot> </h6> `,props: { level: { type: Number,required: true } } })
雖然模板在大多陣列件中都非常好用,但是顯然在這裡它就不合適了。那麼,我們來嘗試使用 render 函式重寫上面的例子:
const { createApp,h } = Vue const app = createAYRvrNllabpp({}) app.component('anchored-heading',{ render() { return h( 'h' + this.level,// tag name {},// props/attributes this.$slots.default() // array of children ) },required: true } } })
jsx
這樣寫渲染函式有點痛苦,有沒有更接近模板的寫法呢,vue提供了一個babel-plugin-jsx babel外掛來讓vue支援jsx寫法
我這邊使用的vuecli建立的vue3 + ts專案,腳手架已經集成了jsx和ts的相關依賴
在vue3中編寫jsx的兩種方式
直接將檔案字尾名從vue改成tsx或者jsx
在vue3中,可以直接使用render選項編寫
import { defineComponent } from "vue"; export default defineComponent({ name: "Jsx",render() { return <div>我是一個div</div>; },});
也可以在setup中返回
import { defineComponent } from "vue"; export default defineComponent({ name: "Jsx",setup() { return () => <div>我是div</div>; },});
兩種方式都可以,具體看個人習慣,setup中訪問不到this,但是render中可以通過this訪問當前vue例項
用法
class繫結,和react的jsx繫結的有區別,react中使用className,vue中使用class
setup() { return () => <div class="test">我是div</div>; },
style繫結
setup() { return () => <div style={{ color: "red" }}>我是div</div>; },
props繫結
// 父元件 setup() { return () => ( <div style={{ color: "red" }}> <span>我是父元件</span> <Mycom msg={"我是父元件傳的值"} /> </div> );
// 子元件,setup的第一個引數,可以獲取props裡的值 setup(props) { return () => <div>我是子元件{props.msg}</div>; },
事件繫結
setup() { function eventClick() { console.log("點選"); } return () => <button onClick={eventClick}>按鈕</button>; },
元件自定義事件
// 子元件 import { defineComponent } from "vue"; export default defineComponent({ name: "Mycom",emits: ["event"],setup(props,{ emit }) { function sendData() { emit("event","子元件傳遞的資料"); } return () => ( <div> <span>自定義事件</span> <button onClick={sendData}>傳遞資料</button> </div> ); },});
// 父元件 // @ts-nocheck // 這樣寫在jsx中沒問題,但是在tsx中會報ts型別錯誤,所以我在上面忽略了當前檔案ts監測@ts-nocheck import { defineComponent } from "vue"; import Mycom from "./mycom"; export default defineComponent({ name: "Jsx",setup() { function getSon(msg: string) { console.log(msg); } return () => ( <div> <Mycom onEvent={getSon} /> </div> ); },});
也可以這樣解決ts型別報錯
setup() { function getSon(msg: string) { console.log(msg); } return () => ( <div> <Mycom {...{ onEvent: getSon }} /> </div> ); },
插槽
// 父元件 setup() { const slots = { test: () => <div>具名插槽</div>,default: () => <div>預設插槽</div>,}; return () => ( <div> <Mycom v-slots={slots}></Mycom> </div> ); },
setup(props,{ slots }) { // 子元件 return () => ( <div> <span>插槽</span> {slots.default?.()} {slots.test?.()} </div> ); },
指令,v-if,v-for等指令在jsx中無法使用,jsx只支援v-model和v-show指令
setup() { const inputData = ref(""); return () => ( <div> <span v-show={true}>顯示</span> <span v-show={false}>隱藏</span> <input type="text" v-model={inputData.value} /> <span>{inputData.value}</span> </div> ); },
最程式設計客棧後
話不多說,我先開啟vant原始碼,準備開啟我的第一個元件原始碼閱讀 src =>button=>button.tsx
到此這篇關於vue中正確使用jsx的文章就介紹到這了,更多相關vue中使用jsx內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!
參考
- vue渲染函式
- vuejsx文件
- issues