你好,我是大圣。

在上一讲中,我给你介绍了如何使用 Chrome 和 Vue Devtools 来调试项目,相信你已经拥有了调试复杂项目的能力。今天,我们来聊一个相对独立的话题,就是 Vue 中的 JSX。你肯定会有这样的疑惑,JSX 不是 React 的知识点吗?怎么 Vue 里也有?

实际上,Vue 中不仅有 JSX,而且 Vue 还借助 JSX 发挥了 Javascript 动态化的优势。此外,Vue 中的 JSX 在组件库、路由库这类开发场景中,也发挥着重要的作用。对你来说,学习 JSX,可以让你实现更灵活的开发需求,这一讲我们重点关注一下 Vue 中的 JSX。

h 函数

在聊 JSX 之前,我需要先给你简单介绍一下 h 函数,因为理解了 h 函数之后,你才能更好地理解 JSX 是什么。下面,我会通过一个小圣要实现的需求作为引入,来给你讲一下 h 函数。

在 Vue 3 的项目开发中,template 是 Vue 3 默认的写法。虽然 template 长得很像 HTML,但 Vue 其实会把 template 解析为 render 函数,之后,组件运行的时候通过 render 函数去返回虚拟 DOM,你可以在 Vue Devtools 中看到组件编译之后的结果。

Untitled

在上面的示意图中,调试窗口右侧代码中的 sfc_render 函数就是清单应用的 template 解析成 JavaScript 之后的结果。所以除了 template 之外,在某些场景下,我们可以直接写 render 函数来实现组件。

先举个小例子,我给小圣模拟了这样一个需求:我们需要通过一个值的范围在数字 1 到 6 之间的变量,去渲染标题组件 h1~h6,并根据传递的 props 去渲染标签名。对于这个需求,小圣有点拿不准了,不知道怎么实现会更合适,于是小圣按照之前学习的 template 语法,写了很多的 v-if:

<h1 v-if="num==1">{{title}}</h1>
  <h2 v-if="num==2">{{title}}</h2>
  <h3 v-if="num==3">{{title}}</h3>
  <h4 v-if="num==4">{{title}}</h4>
  <h5 v-if="num==5">{{title}}</h5>
  <h6 v-if="num==6">{{title}}</h6>

从上面的代码中,你应该能感觉到,小圣这样的实现看起来太冗余。所以这里我教你一个新的实现方法,那就是 Vue 3 中的h 函数。

由于 render 函数可以直接返回虚拟 DOM,因而我们就不再需要 template。我们在 src/components 目录下新建一个文件 Heading.jsx ,要注意的是,这里 Heading 的结尾从.vue 变成了 jsx。

在下面的代码中, 我们使用 defineComponent 定义一个组件,组件内部配置了 props 和 setup。这里的 setup 函数返回值是一个函数,就是我们所说的 render 函数。render 函数返回 h 函数的执行结果,h 函数的第一个参数就是标签名,我们可以很方便地使用字符串拼接的方式,实现和上面代码一样的需求。像这种连标签名都需要动态处理的场景,就需要通过手写 h 函数来实现。

import { defineComponent, h } from 'vue'

export default defineComponent({
  props: {
    level: {
      type: Number,
      required: true
    }
  },
  setup(props, { slots }) {
    return () => h(
      'h' + props.level, // 标签名
      {}, // prop 或 attribute
      slots.default() // 子节点
    )
  }
})

然后,在文件 src/About.vue 中,我们使用下面代码中的 import 语法来引入 Heading,之后使用 level 传递标签的级别。这样,之后在浏览器里访问 http://localhost:9094/#/about 时,就可以直接看到 Heading 组件渲染到浏览器之后的结果。

<template>
  <Heading :level="3">hello geekbang</Heading>
</template>

<script setup>
import Heading from './components/Head.jsx'
</script>

上面的代码经过渲染后的结果如下:

Untitled

手写的 h 函数,可以处理动态性更高的场景。**但是如果是复杂的场景,h 函数写起来就显得非常繁琐,需要自己把所有的属性都转变成对象。**并且组件嵌套的时候,对象也会变得非常复杂。不过,因为 h 函数也是返回虚拟 DOM 的,所以有没有更方便的方式去写 h 函数呢?答案是肯定的,这个方式就是 JSX。