jsx是什么?
JSX(JavaScript XML)是一种文件扩展名为 .jsx 的语法扩展,它被设计为与 JavaScript 一起使用,特别是在 React 库中。JSX 看起来很像 XML 或 HTML,并且它允许你在 JavaScript 文件中编写类似 HTML 的代码。当然,vue框架也是支持jsx的语法的,今天我们就以vue3的框架来记录一下如何在vue3里使用jsx的语法。
vue3里安装支持jsx的插件
默认的情况下,vue3+vite的项目不支持jsx,如果想支持jsx,需安装插件@vitejs/plugin-vue-jsx。
安装@vitejs/plugin-vue-jsx
npm i @vitejs/plugin-vue-jsx
在vite.config.js里配置插件
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({
base: "/",
server: {
open: true,
hmr: true,
},
plugins: [vue(), vueJsx()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
jsx语法
模板语法混合和纯jsx语法
模板语法混合
我们的文件如果以 .vue 结尾,那么我们就可以把jsx的函数式组件和模板语法结合起来使用。我们只需要在script标签上,加上lang=“jsx”,就可以使用jsx的语法了。如下:
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { ref } from "vue";
const componentName = ref("子组件")
const childComponent = () => {
return <h1>{componentName.value}</h1>
}
</script>
效果图:
纯jsx的语法
我们的文件如果以 .jsx 结尾,那么我们就可以纯粹的使用jsx的函数式组件进行开发,如下:
export default function Basic() {
return (
<div>
<h1>Basic</h1>
<p>JSX is a syntax extension for JavaScript. It is not necessary to use JSX, but if you decide to use it, you must configure your environment to transpile it to regular JavaScript.</p>
</div>
)
}
效果图:
变量,插值
- 函数式组件,return jsx的dom的时候,需要用圆括号()包裹,不是花括号{}
- 在jsx里使用变量,使用单层{}包裹即可,并非模板语法里的双层{ {}}
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { ref } from "vue";
const componentName = ref("子组件")
const componentDes = ref("这是一个子组件")
const childComponent = () => {
return (
<div>
<h1>{componentName.value}</h1>
<p>{componentDes.value}</p>
</div>
)
}
</script>
效果图:
注意,当函数式组件只有一个dom的时候,return的时候是可以省略掉括号的,如下:
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { ref } from "vue";
const componentName = ref("子组件")
const childComponent = () => {
return <h1>{componentName.value}</h1>
}
</script>
样式
style
<template>
<childComponent />
</template>
<script setup lang="jsx">
const styleOptions = {
color: 'red',
fontSize: '20px'
}
const childComponent = () => {
return (
<>
<div style={styleOptions}>hello world</div>
<div style={
{ color: 'blue', fontSize: '16px' }}>hello vue.hello jsx</div>
</>
)
}
</script>
效果图:
class
class 类名绑定。有两种方式,使用className字符串或者使用class数组。
<template>
<div class="child-component">
<childComponent />
</div>
</template>
<script setup lang="jsx">
import { ref } from 'vue';
const isRed = ref(false)
const childComponent = () => {
const changeColor = () => {
isRed.value = !isRed.value
}
return (
<>
<div className={`fontWeight600 ${isRed.value ? 'showRed' : ''}`}>hello world</div>
<div class={['fontWeight600', isRed.value ? 'showRed' : '']}>hello vue,hello jsx</div>
<br />
<button onClick={changeColor}>change Color</button>
</>
)
}
</script>
<style scoped lang="less">
.child-component {
:deep(.fontWeight600) {
font-weight: 600;
}
:deep(.showRed) {
color: red;
}
}
</style>
效果图:
注册事件
jsx里的注册事件和 template 中的 v-on:click或者@click不同,偏向原生写法如click事件onClick={handleSearch}、input事件onInput={handleSearch}。
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { reactive } from "vue";
const info = reactive({
name: '赵云',
age: 18,
occupation: ''
});
const childComponent = () => {
const handleSearch = () => {
info.occupation = '镇东将军';
}
return (
<div>
<div>我是{info.name},我今年{info.age}岁了</div>
<div>我的职业是:{info.occupation}</div>
<el-button type="primary" onClick={handleSearch}>查询职业</el-button>
</div>
)
}
</script>
效果图:
模拟v-if 用于条件性地渲染元素
在jsx里是不存在v-if这样的指令的,那么我们该如何实现v-if这样的效果呢?其实我们可以使用js里的逻辑运算符来实现条件性地渲染元素。如下:
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { ref,reactive } from "vue";
const isShowProfile = ref(false);
const info = reactive({
name: '赵云',
age: 18,
occupation: '镇东将军'
});
const childComponent = () => {
const handleSearch = () => {
isShowProfile.value = !isShowProfile.value;
}
return (
<div>
<div>我是{info.name},我今年{info.age}岁了</div>
{ isShowProfile.value && <div>我的职业是:{info.occupation}</div> }
<el-button type="primary" onClick={handleSearch}>{ isShowProfile.value ? '隐藏' : '显示'}资料</el-button>
</div>
)
}
</script>
效果图:
模拟v-show 用于条件性地显示元素
vue中的v-show本质上只是控制样式display的none或者block,然后对dom元素进行显隐。但是jsx里并没有v-show这样的指令。我们仍然可以使用js里的逻辑运算符来实现条件性地显隐元素
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { ref,reactive } from "vue";
const isShowProfile = ref(false);
const info = reactive({
name: '赵云',
age: 18,
occupation: '镇东将军'
});
const childComponent = () => {
const handleSearch = () => {
isShowProfile.value = !isShowProfile.value;
}
return (
<div>
<div>我是{info.name},我今年{info.age}岁了</div>
<div style={
{display: isShowProfile.value ? 'block' : 'none',color: 'red'}}>我的职业是:{info.occupation}</div>
<el-button type="primary" onClick={handleSearch}>{ isShowProfile.value ? '隐藏' : '显示'}资料</el-button>
</div>
)
}
</script>
style这里的两层花括号{ {}},简单说明一下,最外层是用来放插值变量的,里层的花括号其实是一个样式对象,事实上还是单层{}包裹变量的。
效果图:
模拟v-model 表单数据双向绑定
用于在表单输入和应用状态之间创建双向数据绑定。这意味着当用户在表单控件(如输入框、选择框等)中输入数据时,绑定的数据会自动更新;反之,当数据在 JavaScript 代码中发生变化时,表单控件中的内容也会自动更新。(类似于受控组件的概念)。
我们需要明白vue中v-model其实只是一个语法糖,它就是:value,@input 两个方法组合起来的而已。那么根据这一规则,我们就能很好的模拟出v-model的效果。如下:
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { ref } from "vue";
const content = ref('')
const childComponent = () => {
const handleInput = (e) => {
content.value = e.target.value
}
return (
<div>
<input value={content.value} onInput={handleInput}></input>
<div>输入的内容是:{content.value}</div>
<button onClick={() => content.value = ''}>重置</button>
</div>
)
}
</script>
效果图:
模拟v-for 用于循环渲染数据
用于在模板中基于一个数组或对象渲染一个列表。它允许你遍历数据源,并为每个元素生成一个模板副本。
<template>
<childComponent />
</template>
<script setup lang="jsx">
import { reactive } from "vue";
const list = reactive([
{
rank: 1,
name: '吕布',
factions: '群雄'
},
{
rank: 2,
name: '关羽',
factions: '蜀汉'
},
{
rank: 3,
name: '张飞',
factions: '蜀汉'
},
{
rank: 4,
name: '赵云',
factions: '蜀汉'
},
{
rank: 5,
name: '马超',
factions: '蜀汉'
}
])
const childComponent = () => {
return (
<>
<h1>三国武将排行自以为是版</h1>
{ list.map(v => <div>第{v.rank}名:{v.name},他所在的阵营是{v.factions}</div>) }
</>
)
}
</script>
效果图:
插槽
插槽(Slot)是一种用于在组件中插入内容的机制。插槽允许你将父组件的内容插入到子组件的模板中,从而实现更灵活的组件复用。
默认插槽
通过useSlots这个api获取插槽的内容
<template>
<div class="child-component">
<childComponent>
这是默认插槽的内容
</childComponent>
</div>
</template>
<script setup lang="jsx">
import { useSlots } from 'vue';
const childComponent = () => {
const slots = useSlots();
return (
<>
<div>这是子组件</div>
<div>{slots.default ? slots.default() : '默认插槽'}</div>
</>
)
}
</script>
效果图:
具名插槽
<template>
<div class="child-component">
<childComponent>
<template #user>
<div>这是具名插槽</div>
</template>
</childComponent>
</div>
</template>
<script setup lang="jsx">
import { useSlots } from 'vue';
const childComponent = () => {
const slots = useSlots();
return (
<>
<div>这是子组件</div>
<div>{slots.user ? slots.user() : '具名插槽'}</div>
</>
)
}
</script>
效果图:
作用域插槽
<template>
<div class="child-component">
<childComponent>
<template #user="scope">
<div>这是具名插槽,{
{ scope.content }}</div>
</template>
<template #vip="{ content }">
<div>这是具名插槽,{
{ content }}</div>
</template>
</childComponent>
</div>
</template>
<script setup lang="jsx">
import { useSlots } from 'vue';
const childComponent = () => {
const slots = useSlots();
const options = {
id: 1,
content: '这是子组件中的内容',
}
const otherOptions = {
id: 2,
content: '这是子组件中其他的内容',
}
return (
<>
<div>这是子组件</div>
<div>{slots.user ? slots.user(options) : '具名插槽user'}</div>
<div>{slots.vip ? slots.vip(otherOptions) : '具名插槽vip'}</div>
</>
)
}
</script>
效果图:
组件传值
父传子
父组件把父组件里面的数据传递到子组件里,子组件通过形参props就能拿到父组件传递过来的数据
<template>
<div class="child-component">
<childComponent :parentData="parentData" />
</div>
</template>
<script setup lang="jsx">
const parentData = {
id: '01',
name: 'parent',
des: '这是一条父组件里的数据'
}
const childComponent = (props) => {
return (
<>
<h3>父组件传来的数据</h3>
<p>id: {props.parentData.id}</p>
<p>name: {props.parentData.name}</p>
<p>des: {props.parentData.des}</p>
</>
)
}
</script>
效果图:
子传父
子组件需要改变父组件的数据,我们可以通过在子组件里调用父组件传递过来的函数来完成
<template>
<div class="child-component">
<childComponent :parentData="parentData" :editParentData="editParentData" />
</div>
</template>
<script setup lang="jsx">
import { reactive } from 'vue'
const parentData = reactive({
id: '01',
name: 'parent',
des: '这是一条父组件里的数据'
})
const editParentData = () => {
parentData.des = '父组件数据被修改了'
}
const childComponent = ({ parentData, editParentData }) => {
return (
<>
<h3>父组件传来的数据</h3>
<p>id: {parentData.id}</p>
<p>name: {parentData.name}</p>
<p>des: {parentData.des}</p>
<button onClick={() => editParentData()}>修改父组件数据</button>
</>
)
}
</script>
效果图:
provide和inject
在 Vue.js 中,provide 和 inject 是用于在组件树中共享数据的 API。它们通常用于在祖先组件和后代组件之间传递数据,而不需要通过 props 逐层传递。
provide 是在祖先组件中使用的,用于提供数据。你可以使用 provide 函数来提供一个或多个值,这些值可以被后代组件通过 inject 获取。
<template>
<div class="child-component">
<childComponent />
</div>
</template>
<script setup lang="jsx">
import { reactive,provide,inject } from 'vue'
const parentData = reactive({
id: '01',
name: 'parent',
des: '这是一条父组件里的数据'
})
provide('parentData', parentData)
const childComponent = () => {
const parentData = inject('parentData')
return (
<>
<h3>父组件传来的数据</h3>
<p>id: {parentData.id}</p>
<p>name: {parentData.name}</p>
<p>des: {parentData.des}</p>
</>
)
}
</script>
效果图:
拓展
在vue3中使用jsx的优势
在Vue 3中使用JSX(或TSX,如果你的项目使用TypeScript)有以下优势:
与React生态兼容: Vue 3对JSX的支持使得开发者可以利用一些原本为React设计的工具和库。虽然不是所有React特有的工具都能直接用在Vue上,但很多通用的UI库或者工具链可以更容易地集成到Vue项目中。
更直观的组件构建: JSX提供了一种类似HTML的语法来定义组件的结构,这可以让开发者更直观地表达UI逻辑。对于熟悉HTML和CSS的前端开发人员来说,这样的语法可能更加友好。
逻辑与视图紧密耦合: 使用JSX时,你可以在同一个地方处理业务逻辑和UI渲染代码,这种方式可以使组件的实现更加紧凑,便于理解和维护。不过这也意味着需要良好的编码习惯来避免代码混乱。
支持TypeScript: 如果你在使用TypeScript,那么TSX(TypeScript + JSX)提供了更强的类型检查,帮助捕捉错误并提高代码质量。Vue 3本身也增强了对TypeScript的支持,所以结合TSX可以更好地发挥静态类型系统的优势。
更好的性能优化机会: Vue 3引入了编译器宏(如
defineComponent
),它允许你在编写JSX的同时享受性能上的优化。例如,defineComponent
可以帮助确保组件是正确配置的,并且还能带来一些运行时的性能提升。动态特性: 在JSX中,你可以轻松地插入JavaScript表达式,这对于创建动态内容非常有用。比如根据状态条件性地渲染元素、绑定事件处理器等操作都变得更加简洁明了。
与Composition API无缝协作: Vue 3推出了新的Composition API,它提供了一种更灵活的方式来组织和重用逻辑。当与JSX一起使用时,你可以将逻辑清晰地分解成函数,并直接在JSX模板中调用这些函数,从而实现高度模块化的代码结构。
社区和文档支持: 随着越来越多的开发者开始探索Vue 3中的JSX/TSX,相关的资源、插件和支持也在不断增长。这意味着遇到问题时更容易找到解决方案或最佳实践。
迁移路径: 对于那些已经习惯了React风格开发模式的团队,采用Vue 3的JSX可能会减少迁移成本,因为他们不需要大幅改变现有的编码习惯。
尽管有上述优点,但在Vue 3中使用JSX也有一些需要注意的地方。例如,Vue的单文件组件(SFC)格式提供了样式和模板的内置支持,而使用JSX则通常需要额外配置和工具链设置。此外,部分Vue特有的特性(如指令)在JSX中可能没有直接的对应方式,需要通过其他方法实现相同的功能。因此,在决定是否使用JSX之前,应该权衡项目的具体需求和技术栈。
jsx相比h函数的优势
在Vue 3中,JSX 和 h
函数(也称为hyperscript函数)都是用于创建虚拟DOM节点的方式,但它们各有特点。下面列出的是JSX相比h
函数的一些优势:
更直观的语法: JSX提供了一种类似HTML的语法来描述UI结构,这使得代码更加直观易读。对于那些习惯于使用HTML和CSS的前端开发人员来说,JSX的学习曲线较低。
表达式内联: 在JSX中,你可以直接将JavaScript表达式嵌入到模板中,只需要用花括号包裹即可。这种方式让动态生成元素属性、样式或内容变得非常方便。
更好的工具链支持: JSX有更广泛的社区支持,这意味着有更多的编辑器插件、预处理器和其他工具可以帮助你编写、调试和优化你的代码。
与TypeScript的无缝集成: 如果你在项目中使用TypeScript,那么TSX(TypeScript + JSX)提供了更强的类型检查,有助于提高代码的质量和减少错误。虽然
h
函数也可以与TypeScript一起使用,但通常情况下JSX/TSX会提供更流畅的体验。逻辑与视图的紧密耦合: 使用JSX时,业务逻辑和UI渲染可以写在同一地方,这可以让组件的实现更加紧凑且易于理解。尽管这种做法有时会被批评为违反了关注点分离的原则,但在某些场景下它确实能带来便利。
社区和生态系统的成熟度: JSX已经被广泛应用于React等框架中,因此拥有一个庞大而活跃的社区。这意味着你可以更容易地找到教程、最佳实践以及解决问题的方法。
组合性和可复用性: JSX允许你像组合HTML标签一样轻松地组合和嵌套组件,这有助于构建复杂但可维护的应用程序。
性能优化: 虽然
h
函数和JSX在性能上没有显著差异,但一些开发者认为JSX的编译过程可能会引入额外的优化机会,比如通过静态分析提前确定某些属性或事件处理程序是否为常量。更接近React的开发体验: 对于那些熟悉React的人来说,使用JSX可以使他们更容易适应Vue 3中的开发模式,因为两者在这方面有很多相似之处。
然而,值得注意的是,h
函数也有它的优点。例如,它是纯JavaScript函数调用,不需要特殊的编译步骤,并且在某些情况下可能更适合进行深度定制或低级别的操作。此外,在小型项目或者对工具链配置敏感的情况下,h
函数可能更简单直接。
最终选择哪种方式取决于具体的项目需求和个人偏好。如果你追求更简洁、直观的代码风格,并且不介意设置适当的工具链,那么JSX可能是更好的选择。如果你希望保持最简单的依赖关系或需要高度定制化的能力,则h
函数可能更适合。
jsx的发展史
JSX(JavaScript XML)的发展与React框架的发展紧密相关,因为JSX是随着React的出现而被引入的一种语法扩展。以下是JSX发展的一些关键点:
2010年:Facebook开源了PHP语言的一种扩展叫XHP,可以在PHP中创建复合组件。XHP的语法和JSX很相似,它为后来JSX的设计提供了灵感。
2011年:Jordan Walke创建了FaxJS,这是React.js的早期原型。
2013年5月:Facebook发布了UI框架库React,并引入了新的JSX语法。JSX使得UI层可以用组件开发,它允许开发者混合JavaScript和类似HTML的标记来描述用户界面。
2013年后:随着React的逐渐流行,JSX也得到了更广泛的应用和认可。JSX提供了一种直观的方式来构建React组件,虽然最初看起来可能有些奇怪,但它有助于提高代码的可读性和可维护性。
2015年:Babel等工具开始支持将JSX转换为标准的JavaScript,这促进了JSX在React项目中的使用。由于浏览器本身不支持JSX,因此需要通过编译步骤将其转换成浏览器可以理解的JavaScript代码。
持续发展:随着时间的推移,JSX已经成为React生态系统中不可或缺的一部分。尽管有一些关于是否应该使用JSX的讨论,比如有人认为纯JavaScript也能达到同样的效果,但JSX因其简洁性和表达力仍然被广大开发者所接受。
总的来说,JSX从一个针对React特定需求的实验性想法,发展成为一种流行的编写React组件的方式,它的设计哲学是以JavaScript为中心,在JavaScript中嵌入HTML,从而提供了一种强大的方式来定义用户界面。