【学习笔记】《玩转Vue3全家桶》--JSX:应对更灵活的开发场景,需要JSX

前言: 一般,React中有JSX,那么,Vue中的JSX呢?大家知道嘛?实际上,Vue中不仅有JSX,而且Vue还借助JSX发挥了JavaScript动态化的优势。而且,Vue中的JSX在组件库、路由库这类开发场景中,也发挥这重要的作用。

一、h函数

    在聊 JSX 之前,先给你简单介绍一下 h 函数,因为理解了 h 函数之后,才能更好地理解 JSX 是什么。
    举个例子,我们需要通过一个值的范围在数字 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>

    这样的代码看起来很冗余,可以用vue3中的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' + 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'
</script>

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

二、JSX是什么

    先来了解一下JSX是什么,JSX来源自React框架,下面这段代码就是JSX的语法,给变量title赋值了一个h1标签。

const element = <h1 id="app">Hello, Geekbang!</h1>

    这种在 JavaScript 里面写 HTML 的语法,就叫做 JSX,算是对 JavaScript 语法的一个扩展。上面的代码直接在 JavaScript 环境中运行时,会报错。JSX 的本质就是下面代码的语法糖,h 函数内部也是调用 createVnode 来返回虚拟 DOM。在之后的课程中,对于那些创建虚拟 DOM 的函数,我们统一称为 h 函数。

const element = createVnode('h1',{id:"app"},'hello Geekbakg');

    在从JSX到createVNode函数的转化过程中,需要安装一个JSX插件。在项目的根目录下,打开命令行,执行下面的代码来安装插件。

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

    插件安装完成后,我们进入根目录下,打开 vite.config.js 文件去修改 vite 配置。在 vite.config.js 文件中,我们加入下面的代码。这样,在加载 JSX 插件后 ,现在的页面中就可以支持 JSX 插件了。

import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue';

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

    进入 src/componentns/Heading.jsx 中,把 setup 函数的返回函数改成下面代码中所示的内容,这里我们使用变量 tag 计算出标签类型,直接使用渲染,使用一个大括号把默认插槽包起来就可以了。

setup(props, { slots }) {
    const tag = 'h'+props.level
    return () => <tag>{slots.default()}</tag>
}

    进入到 src/components 下面新建文件 Todo.jsx,在下面的代码中,我们使用 JSX 实现了一个简单版本的清单应用。我们首先使用 defineComponent 的方式来定义组件,在 setup 返回的 JSX 中,使用 vModel 取代 v-model,并且使用单个大括号包裹的形式传入变量 title.value ,然后使用 onClick 取代 @click。循环渲染清单的时候,使用.map 映射取代 v-for,使用三元表达式取代 v-if。

import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup(props) {
    let title = ref('')
    let todos = ref([{ title: "学习 Vue 3", done: true },{ title: "睡觉", done: false }]);
    function addTodo(){
        todos.value.push({
            title:title.value
        })
        title.value = ''
    }
    return () => <div>
        <input type="text" vModel={title.value} />
        <button onClick={addTodo}>click</button>
        <ul>
            {
                todos.value.length ? todos.value.map(todo=>{
                    return <li>{todo.title}</li>
                }): <li>no data</li>
            }
        </ul>
    </div>
  }
})

    通过这个例子,能够认识到,使用JSX的本质,还是在写JavaScript 。在 Element3 组件库设计中,我们也有很多组件需要用到 JSX,比如时间轴 Timeline、分页 Pagination、表格 Table 等等。

三、JSX和Template

    有时候,在官网搜索的时候,会有顶部的搜索框、也有相关栏目的一些类别。如果没有类别的分类,可能需要在几百的列表里搜索自己需要的内容。这就说明,接受了固定分类的限制,就能降低选择的成本。这就告诉我们一个朴实无华的道理:我们接受一些操作上的限制,但同时也会获得一些系统优化的收益。
    在 Vue 的世界中也是如此,template 的语法是固定的,只有 v-if、v-for 等等语法。也就是说,template 遇见条件渲染就是要固定的选择用 v-if。
    而JSX只是h函数的一个语法糖,本质就是JavaScript,想实现条件渲染可以用if else,也可以用三元表达式,还可以用任意合法的JavaScript语法。也就是说,JSX可以支持更动态的需求。而template则因为语法限制的原因,不能够像JSX那样可以支持更动态的需求。这是 JSX 相比于 template 的一个优势。
    JSX相比于template还有一个优势,是可以在一个文件内返回多个组件,我们可以像下面的代码一样,在一个文件内返回Button、Input、Timeline等多个组件。

export const Button = (props,{slots})=><button {...props}>slots.default()</button>
export const Input = (props)=><input {...props} />
export const Timeline = (props)=>{
  ...
}

    那么 template 有什么优势呢?template 代码在编译结果的时候,相比于自己去写 h 函数,在 template 解析的结果中,有以下几个性能优化的方面。
    在template和JSX这两者的选择问题上,只是选择框架时角度不同而已。我们实现业务需求的时候,也是优先使用template,动态性要求较高的组件使用JSX实现,尽可能地利用 Vue本身的性能优化
总结:
    对比了JSX和template的优缺点,template由于语法固定,可以在编译层面做的优化较多,比如静态标记就真正做到了按需更新;而JSX由于动态性太强,只能在有限的场景下做优化,虽然,性能不如template好,但在某些动态性要求较高的场景下,JSX成了标配,这也是诸多组件库使用JSX的主要原因。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值