• Vue 3 第二十二章:组件十(组件高级特性-组件的渲染函数和JSX/TSX语法)


    1. 渲染函数

    渲染函数是一种将组件的模板转换成虚拟 DOM 的方法。在 Vue 中,我们可以选择使用渲染函数来代替模板语法。

    渲染函数可以让我们更加灵活地定义组件的模板,并且可以让我们在编写组件时使用JavaScript的全部语言特性。在Vue3中,我们可以使用 h 函数来创建虚拟 DOM 元素。h 函数接受三个参数:元素名元素的属性子元素

    下面是一个简单的示例代码:

    // ChildComponent.vue
    <script lang="ts">
    import { h } from "vue";
    
    export default {
      render() {
        return h("div", { class: "container", style: { color: "red" } }, [
          h("h1", "Hello, world!"),
          h("p", "h函数的基本使用"),
        ]);
      },
    };
    script>
    
    
    // Parent.vue
    <template>
      <div>
        <child-component>child-component>
      div>
    template>
    
    <script setup lang="ts">
    import ChildComponent from "./ChildComponent.vue";
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    渲染到页面上的效果如下:
    在这里插入图片描述

    在上面的代码中,我们定义了一个渲染函数,并使用 h 函数来创建 div 元素、h1 元素和 p 元素。我们将这些元素作为子元素传递给 div 元素,并将 div 元素作为渲染函数的返回值。当我们在组件中使用渲染函数时,我们可以将其返回值渲染到页面上。

    需要注意的是,在Vue3中,我们可以选择使用模板语法渲染函数来定义组件的模板。使用哪种方式取决于我们的个人喜好和项目的需求。

    2. JSX / TSX 语法

    JSX 是一种将XML语法嵌入到JavaScript中的语法。在 Vue3 中,我们可以使用JSX语法来编写组件的模板。使用JSX语法可以让我们更加灵活地定义组件的模板,并且可以让我们在编写组件时使用JavaScript的全部语言特性。

    要在 Vue3 中使用 JSX 语法,我们需要安装 @vitejs/plugin-vue-jsx 插件,并在 vite.config.ts 文件中配置该插件。然后,我们就可以在组件中使用 JSX 语法了。

    下面是一个简单的示例代码:

    2.1. 基本使用

    1)安装 @vitejs/plugin-vue-jsx

    npm install @vitejs/plugin-vue-jsx -D
    
    • 1

    2)配置vite.config.ts
    在这里插入图片描述
    3)配置 tsconfig.json 文件
    在这里插入图片描述

    // vite.config.ts
    import { fileURLToPath, URL } from "node:url";
    
    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue";
    import vueJsx from "@vitejs/plugin-vue-jsx";
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [vue(), vueJsx()],
      resolve: {
        alias: {
          "@": fileURLToPath(new URL("./src", import.meta.url)),
        },
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3)使用TSX

    // index.tsx
    const tsxDom = () => {
      return (
        <>
          <div class="container">
            <h1>Hello, world!</h1>
            <p>This is a tsx.</p>
          </div>
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    // tsx.vue
    <template>
      <div>
        <tsxDom>tsxDom>
      div>
    template>
    
    <script setup lang="ts">
    import tsxDom from "@/tsx/index";
    script>
    
    <style scoped>style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在页面上渲染的结果如下:
    在这里插入图片描述

    2.2. 使用 vue 中的语法

    2.2.1. {} 语法

    // index.tsx
    // tsx 中使用{}语法
    import { ref } from "vue";
    
    let hello = ref<string>("Hello, world!");
    const tsxDom = () => {
      return (
        <>
          <div>
            <h1>{hello.value}</h1>
            <p>This is a tsx.</p>
          </div>
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.2.2. v-model 使用

    注意:使用 v-model 时,需要 .value 获取值

    // index.tsx
    // tsx 中使用 v-model 语法
    import { ref } from "vue";
    let hello = ref<string>("Hello, world!");
    
    const tsxDom = () => {
      return (
        <>
          <input type="text" v-model={hello.value} />
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2.3. v-show 使用

    注意:使用 v-show 时,需要 .value 获取值

    // index.tsx
    // tsx 中使用 v-show 语法
    import { ref } from "vue";
    let flag = ref<boolean>(false);
    
    const tsxDom = () => {
      return (
        <>
          <div v-show={flag.value}>显示</div>
          <div v-show={!flag.value}>隐藏</div>
        </>
      );
    };
    
    export default tsxDom;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.2.4. v-if 不支持,实现v-if功能

    使用v-if时,报错:

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/e9096405f14b4bc189b0ac8742cbb085.png

    此时需要采用三元运算来实现 v-if 的功能:

    // tsx 中模拟 v-if 语法
    import { ref } from "vue";
    let flag = ref<boolean>(false);
    
    const tsxDom = () => {
      return <>{flag.value ? <div>显示</div> : <div>隐藏</div>}</>;
    };
    
    export default tsxDom;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.2.5. v-for 不支持,实现 v-for 功能

    // tsx 中模拟 v-for 语法
    import { reactive } from "vue";
    interface Data {
      name: string;
      age: number;
    }
    let list = reactive<Data[]>([
      { name: "张三", age: 12 },
      { name: "李四", age: 18 },
    ]);
    
    const tsxDom = () => {
      return (
        <>
          {
          	{/* 此处完全遵循 react 语法即可 */}
            list.map((item) => {
              return <div>{item.name}</div>;
            })
          }
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.2.6. v-bind 不支持,模拟 v-bind

    • 使用时,用 {} 包裹
    • 以 data- 开头的自定义属性
    // tsx 中模拟 v-bind 语法
    import { reactive } from "vue";
    
    interface List {
      name: string;
      age: number;
    }
    
    let list = reactive<List[]>([
      {
        name: "张三",
        age: 10,
      },
      {
        name: "李四",
        age: 18,
      },
    ]);
    
    const tsxDom = () => {
      return (
        <>
          {
            list.map((item) => {
              return <div data-name={item.name}>{item.age}</div>;
            })
          }
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    渲染效果如下:
    在这里插入图片描述

    2.2.7. v-on 使用

    • jsx/tsx中事件的使用与 react 一致
    • 以 on 开头
    • 接收一个回调函数
      注意:不能使用vue中的修饰符
    // tsx 中模拟 v-on 语法
    
    const handleClick = () => {
      console.log("被点击了");
    };
    
    const tsxDom = () => {
      return (
        <>
          <button onClick={() => handleClick()}>点击</button>
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    测试效果如下:

    在这里插入图片描述

    2.2.8. Props 使用

    • 通过父组件传递参数
    • jsx/tsx接收props并使用
    // tsx.vue
    <template>
      <div>
        <tsxDom :userName="userName" :userAge="userAge">tsxDom>
      div>
    template>
    
    <script setup lang="ts">
    import { ref } from "vue";
    import tsxDom from "@/tsx/props";
    
    const userName = ref<string>("张三");
    const userAge = ref<number>(18);
    script>
    
    <style scoped>style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    // props.tsx
    // tsx 中接收及使用props
    interface Props {
      userName?: string;
      userAge?: number;
    }
    
    const handleClick = (props: Props) => {
      console.log(props);
    };
    
    const tsxDom = (props: Props) => {
      return (
        <>
          <div>{props.userName}</div>
          <div>{props.userAge}</div>
          <button onClick={() => handleClick(props)}>点击</button>
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试效果如下:
    在这里插入图片描述

    2.2.9. Emit 使用

    • jsx/tsx组件中通过第二个参数 ctx 拿到 emit 方法
    • 通过 emit() 方法传递一个自定义事件以及参数
    • 父组件通过 v-on:自定义事件 拿到传递过来的值
    // tsx.vue
    <template>
      <div>
        <tsxDom
          :userName="userName"
          :userAge="userAge"
          @on-click="handleClick"
        >tsxDom>
      div>
    template>
    
    <script setup lang="ts">
    import { ref } from "vue";
    import tsxDom from "@/tsx/emit";
    
    const userName = ref<string>("张三");
    const userAge = ref<number>(18);
    
    const handleClick = (value: string) => {
      console.log(value); // 张三
    };
    script>
    
    <style scoped>style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    tsx 中通过emit给父组件传参

    // emit.tsx
    interface Props {
      userName?: string;
      userAge?: number;
    }
    
    const handleClick = (props: Props, ctx: any) => {
      ctx.emit("on-click", props.userName);
    };
    
    const tsxDom = (props: Props, ctx: any) => {
      return (
        <>
          <div>{props.userName}</div>
          <div>{props.userAge}</div>
          <button onClick={() => handleClick(props, ctx)}>点击</button>
        </>
      );
    };
    
    export default tsxDom;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.2.10. v-slot 使用

    • 子组件通过 centex.slots 传参
    • 父组件通过 v-slots={} 接收参数
    import { defineComponent, reactive } from "vue";
    
    const state = reactive({ name: "张三" });
    const list = reactive([1, 2, 3]);
    
    export const tsxDom = (props: any, ctx: any) => (
      <>
        {/* 默认插槽 */}
        <div>{ctx.slots.default ? ctx.slots.default() : "默认"}</div>
        {/* 声明名称为state的插槽 并传值,不传值为ctx.slots.state() */}
        <div>{ctx.slots.state && ctx.slots.state(state)}</div>
        {/* 声明名称为list的插槽 并传值,不传值为slots.list() */}
        <div>{ctx.slots.list && ctx.slots.list(list)}</div>
      </>
    );
    
    export default defineComponent({
      setup() {
        type State = {
          name: string;
        };
    
        // 使用插槽
        const slots = {
          // default: () => 
    匿名插槽
    ,
    state: (state: State) => <div>{state.name}</div>, list: (list: number[]) => list.map((item) => <div>{item}</div>), }; return () => ( <> {/* 引用组件, 使用插槽:v-slots={slots} */} <tsxDom v-slots={slots} /> </> ); }, });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    测试效果如下:


    在这里插入图片描述

    总结

    在 Vue3 中,我们可以选择使用模板语法、渲染函数或 JSX 语法来定义组件的模板。使用哪种方式取决于我们的个人喜好和项目的需求。渲染函数可以让我们更加灵活地定义组件的模板,并且可以让我们在编写组件时使用 JavaScript 的全部语言特性。JSX 语法是一种将 XML 语法嵌入到 JavaScript 中的语法,可以让我们更加灵活地定义组件的模板。

  • 相关阅读:
    继认证后弄清Spring Security实现授权
    C++&QT---QT-day2
    如何在vi编辑器中复制内容到终端
    [代码随想录]回溯、贪心算法篇
    【FPGA】优化设计指南(一):设计原则
    3.5 数据更新
    天翎知识管理系统:智能化搜索引擎,快速定位知识资源
    NewStarCTF2023week2-游戏高手
    C 语言指针
    【无标题】
  • 原文地址:https://blog.csdn.net/to_the_Future/article/details/130606568