Vue 中使用 JSX 或 TSX 语法

121 阅读1分钟

1. 什么是 JSX

JSX是Facebook工程团队创造的JS的类似 XML 的语法糖,不是由引擎或浏览器实现,而是使用Babel之类的转置器将JSX转换成常规的JS,允许在JS中使用类似Html的语法。

2. JSX 用在Vue中

<template>
    <div>
    <p>{{ countRef }}</p>
    </div>
</template>

<script>
import {ref} from 'vue'
export default {
    setup (){       // Vue 3新的API setup 可以定义响应式数据
      const countRef = ref(1)
      return {
          countRef
      }  
    }
}
</script>
  • 上例是用vue文件的模版写法,再来看一段另一种用render函数的写法:
// Demo2
<script>
import {ref} from 'vue'
export default {
    setup (){       // Vue 3新的API setup 可以定义响应式数据
      const countRef = ref(1)
      const render = () => {
        return <div>Hello {countRef.value} </div>
      }
      return render
    }
}
</script>
<template>
  <div id="app">
    <Demo2 />
  </div>
</template>

<script>
import Demo2 from 'xx/Demo2.vue'
export default {
    components:{
        Demo2
    }
}
</style>

以上的例子还可以用 JSX 文件的写法:

// 子组件
import { defineComponent, ref} from 'vue'

export default defineComponent({
    setup (props){
      const render = () => {
        return <div> Child </div>
      }
      return render
    }
)}    
import { defineComponent, ref} from 'vue'
import Child from './Child'
export default defineComponent({
    // 一种是 传函数 的写法
    const countRef = ref(1)
      const render = () => {
        return <div>Hello {countRef.value} </div>
      }
      return render
    // 另一种是 传对象 的写法 可以接受更多的参数
    props:{
        name:{
            type:String,
        }
    },
    setup (props){       
      const countRef = ref(1)
      const render = () => {
        return (
        <div>
            Hello {props.name} {countRef.value}
            <Child />     // 使用子组件
        </div>
        )
      }
      return render
    }
})
<template>
  <div id="app">
    <Demo3 name="JIANLONG"/>
  </div>
</template>

<script>
import Demo2 from 'xx/Demo2.vue'
export default {
    components:{
        Demo2
    }
}
</style>

3. 为什么要用 JSX

构建一个<TextField />组件,可以是普通的单行文本输入或多行输入(文本区域),假如绑定的信息有很多:

  • 传统的模板写法:
<div> 
    <textarea v-if="multiline" v-model="content" :name="name" :placeholder="placeholder" :aria-invalid="false"> 
    <input v-else v-model="content" :name="name" :placeholder="placeholder" :aria-invalid="false"> 
</div>
  • JSX 写法:
render (createElement) { 
    const inputAttributes = { 
        class: 'input-field has-outline', // class definition 
        onClick: this.handleClick // event handler
        backdrop: false // custom prop 
        } 
    const inputMarkup = this.multiline 
        ? <textarea {...inputAttributes}></textarea> 
        : <input {...inputAttributes}/> 
        return inputMarkup 
}
  • 配置插件,让vue支持jsx
    @vitejs/plugin-vue-jsx[@vue/babel-plugin-jsx]插件)
pnpm i -D @vitejs/plugin-vue-jsx@1.3.9  
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
    plugins: [
        vue(),
        vueJsx({
            transformOn:true,
            mergeProps:true
        })
     ]
})

4. template 对比 TSX

  • template
<script setup lang="ts">

// 加setup后 声明的所有变量或函数可自动在template中使用

import { ref } from "vue";

const count = ref(0);
const onClick = () => {
    count.value += 1;
};
</script>

<template>
  <div>
    {{ count }}
  </div>
  <div>
    <button @click="onClick">+1</button>
  </div>
</template>
  • TSX
import { defineComponent, ref } from "vue";

export const App = defineComponent({
    setup() {
      const refCount = ref(0)
      const onClick = ()=>{
        refCount.value += 1  // react 不能实现
      }
      return () => <>  
      <div>
        {refCount.value}
      </div>
      <div>
        <button onClick={onClick}>+1</button>
      </div>
      </>
    }
})
  • 上例中的<>写法,类似于react的Fragment组件,代替div作为外层不可见的包裹元素
  • 命名时注意用onClick 类似于react的方法
  • 引用ref的变量命名时注意加refCount
  • 上面的写法,既可以使用到vue的数据响应式,又可以使用到react的丰富JSX语法