vue中正確使用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。然而在一些場景中,你真的需要 JavaScript 的完全編程的能力。這時你可以用渲染函數,它比模板更接近編譯器。

當出現以下場景,雖然下列寫法也能實現想要的效果,但是他不僅冗長,而且我們為每個級別標題重復書寫瞭 。當我們添加錨元素時,我們必須在每個 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 = createApp({})

app.component('anchored-heading', {
  render() {
    return h(
      'h' + this.level, // tag name
      {}, // props/attributes
      this.$slots.default() // array of children
    )
  },
  props: {
    level: {
      type: Number,
      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內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

參考

    vue渲染函數

  • vuejsx文檔
  • issues

推薦閱讀: