JSX是什么
JSX(JavaScript 和 XML),是一个 HTML-in-JavaScript 的语法扩展,首先在 React 中被进入,它允许我们在JavaScript中编写类似HTML的代码,并将其转换为JavaScript对象。Vue3中引入了对JSX的支持,使得我们可以更加灵活地编写组件模板,不再局限于Vue2.x中的模板语法。JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。
形如:
const element = <div id="root">Hello World</div>
或
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
JSX 和 template 的区别
- 语法上有很大区别:
•JSX 本质就是 js 代码,可以使用 js 的任何能力
•template 只能嵌入简单的 js 表达式,其他需要指令,如 v-if
•插值不同,jsx 使用单括号 { },template 使用双括号 {{ }}
•JSX 已经成为 ES 规范,template 还是 Vue 自家规范
- 本质是相同的:
•都会被编译为 js 代码(render 函数)
基本用法
安装使用
vite 官方提供了官方的插件来支持在 vue3 中使用 jsx
yarn add @vitejs/plugin-vue-jsx
安装完之后在 vite.config.js 文件中的 plugins 字段中添加 jsx 支持:
import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({
plugins: [
vueJsx(),
]
})
插值表达式
// template
<template>
<span>{{ a + b }}</span>
</template>
// jsx
render: () => {
return (
<span>{ a + b }</span>
)
}
条件渲染
- jsx只保留了 v-show指令,没有 v-if指令
- 使用 if/else和三目表达式来实现判断
// template v-if v-show
<template>
<div v-if="show">是</div>
<div v-else>否</div>
<div v-show="show">我是v-show</div>
</template>
// jsx
render: () => {
const show = ref(true);
return (
<div>
<div>{show.value ? <div>是</div> : <div>否</div>}</div>
<div v-show={!show.value}>我是v-show</div>
</div>
)
}
// jsx if-else
render: () => {
const isShow = false
const element = () => {
if (isShow) {
return <div>是</div>
} else {
return <div>否</div>
}
}
return (
<div>
{
element()
}
<div v-show={!show.value}>我是v-show</div>
</div>
)
}
样式绑定
- class类名绑定有两种方式,使用模板字符串或者使用数组。
- style绑定需要使用 双大括号
// template
<template>
<div class="static" :class="{ active: isActive }"></div>
<div :class="[isActive ? activeClass : '', static]"></div>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
</template>
// jsx
// jsx 模板字符串
<div className={`static ${ isActive ? 'active' : '' }`}>header</div>
//jsx 数组
<div class={ [ 'static', isActive && 'active' ] } >header</div>
// jsx 样式绑定需要使用双大括号。
render: () => {
const width = '100px'
return (
<button style={{ width, fontSize: '16px' }}></button>
)
}
列表循环
// template v-for
<template>
<ul>
<li v-for="item in list" :key="item">{{ item }}</li>
</ul>
</template>
// jsx 数组 .map 函数
render: () => {
return <>
<ul>
{ list.map(item => <li>{item}</li>)}
</ul>
</>
}
事件处理
- jsx绑定事件使用的是 单大括号 {},事件绑定不是以 @为前缀了,而是改成了 on,例如:click 事件是 onClick
- 如果需要使用事件修饰符,就需要借助withModifiers方法啦,withModifiers 方法接收两个参数,第一个参数是绑定的事件,第二个参数是需要使用的事件修饰符,或者在有些修饰符不生效的情况下采用链式驼峰的形式进行设置,如@click.once变为 onClickOnce
// template
<template>
<button @click="clickButton('button1')"> 点击</button>
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
</template>
// jsx 数组 .map 函数
render: () => {
const clickButton = val => {
console.log(val)
}
return (
<div>
<button onClick={() =>clickButton('button1')}>点击</button>
<a onClick={withModifiers(() => doThis(), ['stop'])}></a>
<a onClickOnce={doThis}></a>
</div>
)
}
标签属性绑定
// template
<template>
<div v-bind="properties"></div>
</template>
//jsx
render: () => {
return (
<div {...properties}></div>
)
}
V-model
// 一般用法
<input v-model="value" /> // template
<input v-model={value} /> // jsx
// 指定绑定值写法
<input v-model:modelValue="value" /> // template
<input v-model={[value,'modelValue']} /> // jsx
// 修饰符写法
<input v-model:modelValue.trim="value" /> // template
<input v-model={[value,'modelValue',['trim']]} /> // jsx
插槽
定义插槽
- jsx/tsx中是没有
slot标签的,定义插槽需要使用{}或者使用renderSlot函数 - setup 函数默认接收两个参数 1. props 2. ctx 上下文 其中包含 slots、attrs、emit 等
// template
<template>
<div>
<slot></slot>
<slot name="title"></slot>
</div>
</template>
// jsx
import { renderSlot } from "vue"
export default defineComponent({
// 从ctx中解构出来 slots
setup(props, { slots }) {
return () => (
<div>
{ renderSlot(slots, 'default') } // 等价于 { slots.default?.() }
{ slots.title?.() }
</div>
)
}
})
- 使用插槽通过 v-slots 来使用插槽
// template
<template>
<yun-table
>
<template #action="{ row }">
<el-button type="action" @click="handleDel(row)">
删除
</el-button>
</template>
</yun-table>
</template>
// jsx
render: (form) => {
const slots = {
action: ({ row }) => <el-button type="action" onClick={() => handleDel(row)}>
删除
</el-button>,
};
return (
<yun-table
v-slots={slots}
>
</yun-table>
);
},
jsx基础模板
import { defineComponent, onMounted, ref } from 'vue';
export default defineComponent({
// props: ['xx'],
setup(props,{ emit }) {
onMounted(() => {
// ...
})
return () => (
<div></div>
)
}
})
组件运用
在 .vue 文件中使用 jsx组件
// 父
<template>
<div class="home">
<JSXDemo />
</div>
</template>
<script setup lang="jsx">
import JSXDemo from '@/components/JSXDemo.vue'
</script>
// JSXDemo.vue
<script>
import { ref } from 'vue'
export default {
setup () {
const countRef = ref(200)
const render = () => {
return <p>DEMO1--{countRef.value}</p>
}
return render
}
}
</script>
在.jsx文件格式中父子组件属性传递
// 父组件
import { defineComponent, ref } from 'vue'
import JSXChild from './JSXChild.jsx'
export default defineComponent(() => { // 传入 setup 函数
const countRef = ref(360)
const render = () => {
return <>
<p>数量--{countRef.value}</p>
<JSXChild a={countRef.value + 100}></JSXChild> // vue3的template会自动解析ref的.value,在jsx中ref的.value是不会被自动解析的
</>
}
return render
})
// 子组件 JSXChild.jsx
import { defineComponent } from 'vue'
export default defineComponent({ // 传入组件配置
props: ['a'],
setup (props) {
const render = () => {
return <>
<p>child {props.a}</p>
</>
}
return render
}
})
经验总结
我们该怎么选择 JSX 和 template ?
- template优势:template 的语法是固定的,有 v-if、v-for 等等语法。按照这种固定格式的语法书写的代码, Vue3 在编译层面就可以很方便地去做静态标记的优化,减少Diff过程。比如静态提升,类型标记,树结构打平等来提高虚拟 DOM 运行时性能。这也是 Vue 3 的虚拟 DOM 能够比 Vue 2 快的一个重要原因。
- JSX优势:template 因为语法限制原因,不能够像 JSX 那样可以支持更动态的需求。每一个 .vue 文件结尾的文件都是一个组件,而且只能 export default 出一个组件,JSX 则不同 ,是可以在一个文件内返回多个组件的,比如我们写一个页面的时候其实可能会需要把一些小的节点片段拆分到小组件里面进行复用,这些小组件在jsx里面,写个简单的函数组件就能搞定,那如何选择呢?
在实现业务需求的时候,优先使用 template,尽可能地利用 Vue 本身的性能优化,如列表、弹窗和抽屉。而对于动态性要求较高的组件可以使用 JSX 来实现,比如动态表单,封装动态递归组件。而对于公司项目来说,大多数业务需求都是表单类的,那就赶紧用上jsx吧,用久了你就会发现,哎,真香。