极致爽文,tsx、jsx语法应用vue3

2024-05-27
23708 分钟
...

JSX语法让程序员体验到前所未有的舒爽,而在Vue3中,快速过渡到TSX也是轻而易举的。除了下文介绍的TSX用法外,其他方面与Vue3的使用方式并无二致。TSX所需学习的内容并不繁杂,只是稍微改变了开发习惯,取消了模板语法,让我们能够像编写JavaScript一样书写页面。

通过执行npm init vue来创建一个Vue工程。

工程地址

你可以在以下地址找到本文使用的工程示例:gitee.com/z-shichao/v…

组件定义:defineComponent

在Vue3中,defineComponent是一个用于定义组件的函数API。它的作用主要有三个方面:

  1. 创建组件选项对象:defineComponent允许你定义一个包含组件配置信息的选项对象,如组件的属性、方法、生命周期钩子等。
  2. 类型推断支持:使用defineComponent可以帮助TypeScript更好地推断组件的类型,从而提高开发效率和代码的健壮性。
  3. 提供组件定义上下文:defineComponent内部对组件选项对象进行一些处理,包括生命周期钩子的转换、模板的编译等,从而提供了一个统一的组件定义上下文,使得组件的定义更加清晰和一致。

这里有个专门讲解defineComponent的文章 :juejin.cn/post/699461…

tsx有种书写模式:函数语法,选项语法


import { defineComponent } from "vue"
export default defineComponent({
  setup() {
    return () => (<div>vue3+tsx</div>)
  }
})

export default defineComponent(() => {
  return () => (<div>vue3+tsx</div>)
})

使用选项语法还是函数语法主要取决于个人习惯。下文示例将使用选项式进行讲解。

注意:返回的是一个函数,在函数中返回的是一个JSX元素。JSX元素中必须有一个根标签。如果不想使用根标签,可以使用<></>代替,该标签不会在页面中渲染。

插值语法

export default defineComponent({
  setup() {
    let text = ref<string>('我是文本内容')
    return () => (<div>{text.value}</div>)
  }
})

注意:使用ref定义响应式数据时,在模板中需要使用.value来访问。

事件绑定

export default defineComponent({
  setup() {
    let text = '我是文本内容'
    return () => (<>
    <div >{text}</div>
    <button onClick={() => { alert('您点击了我')}}>点我</button>
    </>)
  }
})

事件修饰符

后边加驼峰事件修饰符

export default defineComponent({
  setup() {
    let text = '我是文本内容'
    return () => (<>
    <div >{text}</div>
    <button onClickStop={() => { alert('您点击了我')}}>点我</button>
    </>)
  }
})

注意:使用大括号{}进行插值,使用on+事件名(小驼峰命名法)进行事件绑定,自定义事件也是一样的。

jsx中移除了部分指令:v-bin、v-for、v-if

v-bind:使用大括号{}进行包裹

export default defineComponent({
  setup() {
    let text = '我是文本内容'
    let style = {
      background: 'red'
    }
    return () => (<>
      <div style={style} >{text}</div>
      <button onClick={() => { alert('您点击了我') }}>点我</button>
    </>)
  }
})
  • v-for:使用数组方法map
export default defineComponent({
  setup() {
    let items = ['张三', '李四', '王五']
    return () => (<>
      {items.map((item) => <div>{item}</div>)}
    </>)
  }
})

v-if:使用三元表达式

export default defineComponent({
  setup() {
    let isShow = ref<boolean>(true)
    return () => (<>
      <div>{isShow.value ? <div>我出来饿了</div> : ''}</div>
      <button onClick={() => { isShow.value = !isShow.value}}>点我</button>
    </>)
  }
})

注意:js要写在{}里面,返回值是是一个可渲染的元素或元素组成的数组。

插槽

默认插槽

import { defineComponent, ref } from "vue"
export default defineComponent({
  setup(props, { slots }) {
    let text = ref<string>('我是默认插槽')
    return () => (<Child>{text.value}</Child>)
  }
})
let Child = (props: any, { slots }: any) => {
  return <div>{slots.default()}</div>
}

具名插槽

import { defineComponent, ref } from "vue"
export default defineComponent({
  setup(props, { slots }) {
    let text = ref<string>('我是默认插槽')
    return () => (<Child>
      {{
        default: () => text.value,
        name1: () => '我是插槽1',
        name2: () => '我是插槽2'
      }}
    </Child>)
  }
})
let Child = (props: any, { slots }: any) => {
  return <div>
    <div>{slots.default()}</div>
    <div>{slots.name1()}</div>
    <div>{slots.name2()}</div>
  </div>
}

作用域插槽

export default defineComponent({
  setup(props, { slots }) {
    let text = ref<string>('我是默认插槽')
    return () => (<Child>
      {{
        default: () => text.value,
        name1: () => '我是插槽1',
        name2: (parms: string) => <div>
          {parms}
        </div>
      }}
    </Child>)
  }
})
let Child = (props: any, { slots }: any) => {
  let parms: string = '我是参数'
  return <div>
    <div>{slots.default()}</div>
    <div>{slots.name1()}</div>
    <div>{slots.name2(parms)}</div>
  </div>
}

还可以通过props传参向子组件传递元素

import { defineComponent, ref } from "vue"
export default defineComponent({
  setup(props, { slots }) {
    let text = ref<string>('我是默认插槽')
    return () => (<Child
      childs={[
        <div >我是一</div>,
        <div >我是二</div>
      ]}></Child>)
  }
})
let Child = (props: any, { slots }: any) => {
  return <div>
    <div>{props?.childs[0]}</div>
    <div>{props?.childs[1]}</div>
  </div>
}

使用css scoped

在Vue 3 中,如果想要在 TSX 文件中使用 CSS scoped 样式,通常不能直接在 TSX 文件中实现 scoped 样式。你可以在 .vue 单文件组件中的 <script> 标签中编写 TSX 代码,并且将 <script> 标签的 lang 属性设置为 'tsx',然后在 <style> 标签中添加 scoped 属性来实现 scoped 样式。

<script lang="tsx">
import { defineComponent } from "vue"
export default defineComponent({
    setup() {
        return () => (<>
            <div class='text'>css scoped</div>
        </>)
    }
})
</script>
<style scoped>
.text{
    background: red;
}
</style>

vue3+tsx中定义props:

import { defineComponent, PropType } from "vue"
export default defineComponent({
    setup() {
        return () => (<>
            <div>我是父组件</div>
            <Text name={'我 是 传进来的name'} />
        </>)
    }
})

const Text = defineComponent({
   props:{
      name:{
      type:String as PropType<string>,
      required:true
      }
    },
    setup(props) {
        return () => (<>
            <div>{props.name}</div>
        </>)
    }

})
import { defineComponent ,PropType} from "vue"
export default defineComponent({
    setup() {
        return () => (<>
            <Text name='我 是 传进来的name' />
        </>)
    }
})

注意:在tsx中定义类型时,只能定义比较笼统的Object、String等,这种类型为什么比较笼统呢,因为在js语言中Object在任何变量的原型上都存在,当你定义一个Object时他就会和any类型时类似的,其他类型同理,当我们想要定义某个类型时,我们只能使用as+ PropType的形式。

简化props的使用:

首先要了解定义string会报错呢,因为我们使用了defineComponent帮我们进行了类型推断定义。那我们可不可以不使用,自己使用ts来规范我们的类型呢。当然可以!!!

方案一:

不适用defineComponent 自己使用ts来进行类型定义,但只支持函数语法和react用法几乎一样

可以看到使用ts也可以进行类型定义,并且更为简便。当然参数在子组件里也是可以定义接收的。缺点就是jsx中是不能使用这种方式的。

import { defineComponent } from "vue"
export default defineComponent({
    setup() {
        return () => (<>
            <div>我是父组件</div>
            <Text name='张三'/>
        </>)
    }
})
interface Props {
    name: string,
    age?: number
}
const Text = (props: Props, ctx: any) => {
    return <div>{props.name}</div>
}

方案二:

混编 这个有篇文章可以看一下 juejin.cn/post/714305…

方案三:

使用# Vue Macros构建项目
vue-macros.dev/zh-CN/guide…

vue中jsx语法和模板语法的优缺点:

JSX语法:

优点:

  1. 更灵活的语法: JSX允许在模板中使用JavaScript表达式,使得模板更加灵活和表达能力更强,更容易阅读和理解JSX的代码。
  2. 组件逻辑更直观,易维护: JSX让组件的结构和逻辑更加紧密,因为你可以在组件中直接编写JavaScript逻辑,并且在同一个文件中可以写多个组件,将相关性较强的组件放在一起更易维护。
  3. 性能较好: 使用JSX编写Vue 3组件会比使用模板语法的组件具有更好的性能。因为JSX可以直接将组件转换为纯JavaScript 代码,而模板语法需要在运行时进行解析和编译。性能差异不大。

缺点:

  1. 学习曲线较陡: 对于新手来说,学习JSX语法可能需要一定的时间,特别是对于那些不熟悉React或其他使用JSX的框架的人来说。
  2. 与HTML混合: 有些开发者可能觉得在JavaScript中嵌入HTML的方式不够直观,因为它打破了HTML和JavaScript的分离原则。
  3. 与模板的差异:TSX(或 JSX)语法与 Vue 的模板语法有一定的差异,可能需要一定的适应时间,并且不支持一些模板特有的语法和指令。

模板语法:

优点:

  1. 易于学习和上手: 模板语法更接近传统的HTML,因此对于初学者来说更容易理解和上手。
  2. HTML代码直观: 模板语法使得HTML代码更加直观,可以更清晰地看到组件的结构。
  3. 编辑器支持良好: 大多数编辑器对Vue模板语法有良好的支持,提供语法高亮、自动补全等功能。

缺点:

  1. 表达能力受限: 模板语法相比于JSX在表达能力上受到一定的限制,因为因为它不能像JSX那样直接在模板中使用JavaScript表达式。
  2. 组件与逻辑分离: 使用模板语法时,组件的结构和逻辑可能会分离得较为明显,因为一些逻辑需要放在JavaScript中,而不是直接嵌入模板中。

总的来说,选择JSX还是模板语法取决于个人的偏好、团队的技术栈以及项目的需求。在Vue 3中,JSX语法的支持更加完善,因此如果你对JavaScript更为熟悉,或者项目需要更高的灵活性和表达能力,那么可以考虑使用JSX语法。而对于简单的项目或团队中大多数成员都熟悉HTML的情况下,模板语法可能更为适合。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
vue2025-10-22
shallowRef 与 ref 的区别、场景与坑位
结论先行: 会 深度 地把你放进去的对象转成响应式(递归代理),因此"对象内部的改动"也会触发视图更新。 只在 顶层 做依赖追踪: 更换 才触发更新 ;若你只是"在原对象上改属性",不会触发,除非手动 。 1\. 快速对比 维度 追踪粒度 深度(递归) 仅顶层 对象内部属性变更 会触发更新 不会 触发(除非 或重新赋值) 性能 有递归 & 依赖跟踪开销 更轻量,适合大对象/第三方实例 适用对象 普通标量、普通对象、数组 第三方实例(图表...
学习面试
vue2025-10-20
Vue 3 中的 setup执行时机与异步逻辑详解
Vue 3 中的 执行时机与异步逻辑详解 本文详细介绍 Vue 3 组合式 API 的核心函数 :它在组件生命周期中的执行时机、异步行为( 的影响)、常见用法和最佳实践。 一、 的执行时机 是 Vue 3 组件实例创建后、渲染前执行的函数。 它相当于 Vue 2 中的 和 的合体。 执行流程: 也就是说: 所有响应式数据( 、 、 )都在这里定义; 所有生命周期钩子( 、 等)都在这里注册; 模板渲染会等待 执行完成(或异步返回的 Pr...
学习面试
vue2025-10-08
vue中ref与reactive
Vue 3 的 与 :区别、实现,以及为什么更推荐 1\. 一句话结论 :适合 原始类型 或 独立、细粒度 的状态单元;类型推断明确、解构安全、追踪粒度更小, 默认首选 。 :适合管理 对象/集合 这类结构化状态;但 解构会丢失响应性 , 默认 非深度 ,需要正确的使用姿势配合。 2\. 用法与语义差异 2.1 基本示例 2.2 模板中的解包(unwrapping) 在 模板 里, 会 自动解包 ,可直接写 ,不必 。 在响应式对象中...
学习面试
vue2025-09-23
Vue3 中computed属性依赖总结
Vue3 属性依赖总结 1. 依赖收集机制 的 getter 内部 读取 到哪些响应式变量 ( / ),就会把这些变量收集为依赖。 当这些依赖发生变化时, 会重新计算。 2. 如果只返回常量 始终是 。 修改 或 不会触发 重新计算,因为 getter 内没有访问它们。 3. 如果访问了响应式变量但仍返回常量 初始化时会打印一次。 当 或 改变时,getter 会重新执行(再次打印),因为它们被收集为依赖。 但 依旧始终是 。 4. 总...
学习面试
vue2025-09-17
template标签为什么不可以使用v-show
前言 写 Vue 时,最容易踩的小坑之一,就是给 套一个 。编辑器不报错,页面却“消失”了。很多人以为这是 bug,其实它只是 Vue 的底层设计在默默提醒: 不是真节点,它只是个“草稿纸”。 一、为什么 拒绝 v show 的本质是切换元素的 CSS: 。浏览器要找到真实 DOM 节点,才能给它加样式。而 在运行时会被完全剥离,不会留下任何标签,Vue 找不到节点,也就无处下刀。 举个反面教材: 打开控制台,你会看到 还是生成了,但...
学习面试
vue2025-09-17
Vue 3中watchEffect自动追踪详解
Vue 3 自动追踪详解 一、是什么 是 Vue 3 Composition API 提供的一个响应式副作用(side effect)函数。 它会 立即执行一次回调函数 ,并且在回调函数中访问到的所有响应式数据(ref、reactive、computed 等)都会被 自动追踪 。 当这些依赖的响应式数据发生变化时,回调会重新执行。 简单理解: 能“自动发现”它用到的响应式变量,不需要手动指定依赖。 二、基本用法 特点: 第一次立即执行...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录