全网最全最通俗易懂的vue3+jsx(setup语法糖版)

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)有以下优势:

  1. 与React生态兼容: Vue 3对JSX的支持使得开发者可以利用一些原本为React设计的工具和库。虽然不是所有React特有的工具都能直接用在Vue上,但很多通用的UI库或者工具链可以更容易地集成到Vue项目中。

  2. 更直观的组件构建: JSX提供了一种类似HTML的语法来定义组件的结构,这可以让开发者更直观地表达UI逻辑。对于熟悉HTML和CSS的前端开发人员来说,这样的语法可能更加友好。

  3. 逻辑与视图紧密耦合: 使用JSX时,你可以在同一个地方处理业务逻辑和UI渲染代码,这种方式可以使组件的实现更加紧凑,便于理解和维护。不过这也意味着需要良好的编码习惯来避免代码混乱。

  4. 支持TypeScript: 如果你在使用TypeScript,那么TSX(TypeScript + JSX)提供了更强的类型检查,帮助捕捉错误并提高代码质量。Vue 3本身也增强了对TypeScript的支持,所以结合TSX可以更好地发挥静态类型系统的优势。

  5. 更好的性能优化机会: Vue 3引入了编译器宏(如defineComponent),它允许你在编写JSX的同时享受性能上的优化。例如,defineComponent可以帮助确保组件是正确配置的,并且还能带来一些运行时的性能提升。

  6. 动态特性: 在JSX中,你可以轻松地插入JavaScript表达式,这对于创建动态内容非常有用。比如根据状态条件性地渲染元素、绑定事件处理器等操作都变得更加简洁明了。

  7. 与Composition API无缝协作: Vue 3推出了新的Composition API,它提供了一种更灵活的方式来组织和重用逻辑。当与JSX一起使用时,你可以将逻辑清晰地分解成函数,并直接在JSX模板中调用这些函数,从而实现高度模块化的代码结构。

  8. 社区和文档支持: 随着越来越多的开发者开始探索Vue 3中的JSX/TSX,相关的资源、插件和支持也在不断增长。这意味着遇到问题时更容易找到解决方案或最佳实践。

  9. 迁移路径: 对于那些已经习惯了React风格开发模式的团队,采用Vue 3的JSX可能会减少迁移成本,因为他们不需要大幅改变现有的编码习惯。

尽管有上述优点,但在Vue 3中使用JSX也有一些需要注意的地方。例如,Vue的单文件组件(SFC)格式提供了样式和模板的内置支持,而使用JSX则通常需要额外配置和工具链设置。此外,部分Vue特有的特性(如指令)在JSX中可能没有直接的对应方式,需要通过其他方法实现相同的功能。因此,在决定是否使用JSX之前,应该权衡项目的具体需求和技术栈。

 jsx相比h函数的优势

在Vue 3中,JSX 和 h 函数(也称为hyperscript函数)都是用于创建虚拟DOM节点的方式,但它们各有特点。下面列出的是JSX相比h函数的一些优势:

  1. 更直观的语法: JSX提供了一种类似HTML的语法来描述UI结构,这使得代码更加直观易读。对于那些习惯于使用HTML和CSS的前端开发人员来说,JSX的学习曲线较低。

  2. 表达式内联: 在JSX中,你可以直接将JavaScript表达式嵌入到模板中,只需要用花括号包裹即可。这种方式让动态生成元素属性、样式或内容变得非常方便。

  3. 更好的工具链支持: JSX有更广泛的社区支持,这意味着有更多的编辑器插件、预处理器和其他工具可以帮助你编写、调试和优化你的代码。

  4. 与TypeScript的无缝集成: 如果你在项目中使用TypeScript,那么TSX(TypeScript + JSX)提供了更强的类型检查,有助于提高代码的质量和减少错误。虽然h函数也可以与TypeScript一起使用,但通常情况下JSX/TSX会提供更流畅的体验。

  5. 逻辑与视图的紧密耦合: 使用JSX时,业务逻辑和UI渲染可以写在同一地方,这可以让组件的实现更加紧凑且易于理解。尽管这种做法有时会被批评为违反了关注点分离的原则,但在某些场景下它确实能带来便利。

  6. 社区和生态系统的成熟度: JSX已经被广泛应用于React等框架中,因此拥有一个庞大而活跃的社区。这意味着你可以更容易地找到教程、最佳实践以及解决问题的方法。

  7. 组合性和可复用性: JSX允许你像组合HTML标签一样轻松地组合和嵌套组件,这有助于构建复杂但可维护的应用程序。

  8. 性能优化: 虽然h函数和JSX在性能上没有显著差异,但一些开发者认为JSX的编译过程可能会引入额外的优化机会,比如通过静态分析提前确定某些属性或事件处理程序是否为常量。

  9. 更接近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,从而提供了一种强大的方式来定义用户界面。 

### Vue3 JSX Setup 语法糖 使用插槽 示例教程 #### 插槽基础概念 在Vue中,插槽(slot)允许父组件向子组件传递内容。通过`useSlots()`函数可以在组合API模式下的`setup`方法里访问这些插槽的内容。 #### 创建带有默认和具名插槽的组件 下面是一个简单的例子来展示如何利用JSX以及Setup语法糖创建一个能够接收来自其父级传入的不同类型的插槽内容的自定义组件: ```javascript <script lang="jsx"> import { defineComponent, useSlots } from "vue"; export default defineComponent({ setup(props) { const slots = useSlots(); return () => ( <article> {/* 默认插槽 */} <header>{slots.default ? slots.default() : null}</header> {/* 具名插槽 - title */} <section> {slots.title && <h1>{slots.title()}</h1>} </section> {/* 更多可能存在的其他具名插槽... */} </article> ); } }); </script> ``` 此代码片段展示了怎样在一个基于JSX编写的Vue单文件组件内使用`useSlots()`获取并渲染由父组件提供的插槽内容[^1]。 为了使上述功能正常工作,在Vite项目环境中还需要安装特定于JSX的支持库以便正确解析`.jsx`扩展名的文件,并配置相应的Babel或TypeScript加载器以处理React风格的JSX语法。 当希望给这个新创建的组件指定名字时,则可以通过额外增加一个普通的`<script>`标签来进行声明[^2]: ```html <!-- 组件名称 --> <script> export default { name: &#39;MyCustomComponent&#39;, }; </script> <!-- 前面提到过的包含setup逻辑的部分保持不变 --> <script setup lang="jsx">...</script> ``` 这样就完成了整个过程——既实现了对插槽的支持又设置了组件的名字。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零凌林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值