💫 透过 Vue Macros 了解 Vue3 发展趋势

第一章:什么是Vue Macros ?

Vue Macros 是一个实现了一些尚未被 Vue 官方实现的提案或想法的库。这意味着它将探索更多的Features和语法糖以增强 Vue 的功能。

Vue Macros 的核心开发团队由 Kevin Deng 主导,团队成员还包括 Alex 和 zhiyuanzmj 等人 (Vue Macros) (GitHub),官方团队参与项目贡献并密切合作,可以说是 Vue 的前瞻版本。

比如 在vue3.3/3.4 + 版本加入**defineOptions()defineModel()** 的就最早诞生于Vue Macros

第二章 Vue-macros JSX/TSX 实验性新特性

在这一章节,主要介绍 Vue-macros 的三个实验性新特性:defineRendersetupComponentsetupSFC

DefineRender

defineRender 是一个让我们可以更简洁地定义组件渲染逻辑的特性。它为 JSX/TSX 语法提供了更好的支持,使得编写渲染函数变得更加直观和简洁。

传统的渲染函数定义方式:

ts 复制代码
export default {
  render() {
    return <div>Hello, Vue!</div>;
  },
};

使用 DefineRender 的方式:

ts 复制代码
<script setup lang="tsx">
// JSX passed directly
defineRender(
  <div>
    <span>Hello</span>
  </div>,
)

// Or using render function
defineRender(() => {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  )
})
</script>

SetupComponent

setupComponent 是 Vue Macros 提供的一种实验性功能。该功能通过更简洁的语法,使开发者能够更加专注于业务逻辑而不是配置细节。以下是结合官方文档对 setupComponent 的详细介绍。

基本用法

使用 setupComponent 可以像下面这样定义组件:

ts 复制代码
export const App = defineSetupComponent(() => {
  defineProps<{
    foo: string
  }>()

  defineEmits<{
    (evt: 'change'): void
  }>()

  defineOptions({
    name: 'App',
  })

  return <div />
})

特点:

根据github及官网的总结,大概有以下三个特点(→ 在我看来就是更像React了):

  • 简化语法,专注业务逻辑

    • setupComponent 通过简化组件 setup 函数的定义,使开发者能够更加专注于业务逻辑而不是配置细节。例如,使用 setupComponent 定义组件的方式更加简洁,使得代码更加易读和易维护 (Vue Macros) (Vue Macros)。
  • 实验性功能,集成度高

    • setupComponent 是一种实验性功能,使用时需要谨慎。它与 definePropsdefineEmitsdefineOptions 等功能结合使用,提供了一个统一的接口来定义组件的属性、事件和选项 (Vue Macros) (GitHub)。虽然目前 TypeScript 支持尚未完成,但这种集成度高的设计使得组件定义更加一致和规范。
  • 高效的类型支持

    • setupComponent 支持 TypeScript 类型注解,使开发者能够在开发过程中获得更好的类型检查和自动补全支持。这一特性在开发大型项目时尤为重要,可以有效减少因类型错误导致的 bug (GitHub) (Vue Macros)。不过需要注意的是,当前的 TypeScript 支持还在完善中,部分功能可能存在已知问题 (Vue Macros)。

SetupSFC

setupSFC 是一个专门为单文件组件(SFC)工具。更重要的是在setupSFC 的模式下,无需在包裹 defineSetupComponent 这个函数了,甚至可以直接写在 tsx 文件中,来编写一个 SFC。

安装指路:

ts 复制代码
// vite.config.ts
import VueMacros from 'unplugin-vue-macros/vite'
import Vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    VueMacros({
      plugins: {
        vue: Vue({
          include: [/\.vue$/, /\.setup\.[cm]?[jt]sx?$/],
          //                   ⬆️ 需要添加 setup 模式
        }),
      },
    }),
  ],
})

使用 SetupSFC 的方式:

ts 复制代码
// Foo.setup.tsx
defineProps<{
  foo: string
}>()

defineEmits<{
  (evt: 'change'): void
}>()

export default () => (
  <div>
    <h1>Hello World</h1>
  </div>
)

第三章:效率性新特性ChainCall

在这个章节,主要介绍 Vue-macros 的一个效率性新特性:chainCall。这一特性旨在通过简化代码结构和提高开发效率,进一步增强 Vue 的功能,也可能在后几个版本加入Vue官方版。

官方调用方式

ts 复制代码
export interface Props {
  msg?: string
  labels?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

使用 ChainCall 的方式

ts 复制代码
<script setup lang="ts">
const props = defineProps<{
  foo?: string
  bar?: number[]
  baz?: boolean
}>().withDefaults({
  foo: '111',
  bar: () => [1, 2, 3],
})
</script>

编译后:

ts 复制代码
<script setup lang="ts">
const props = withDefaults(
  defineProps<{
    foo?: string
    bar?: number[]
    baz?: boolean
  }>(),
  {
    foo: '111',
    bar: () => [1, 2, 3],
  },
)
</script>

优势

1. 代码简洁度

使用 chainCall 可以将定义属性和设置默认值的过程简化为一行代码,这比官方方式更加简洁:

  • 官方调用方式 :需要显式调用 withDefaults 并传递 defineProps 结果,显得冗长。
  • ChainCall 方式 :通过链式调用,将 withDefaultsdefineProps 合并,减少代码量。

2. 可读性提高

链式调用的方式使代码结构更加直观,易于理解和维护:

  • 官方调用方式:两步调用可能会让初学者感到困惑,需要额外的注释来解释。
  • ChainCall 方式:一步到位,清晰明了。

3. 类型安全

chainCall 完全支持 TypeScript,可以在编译时检查类型,确保代码的可靠性:

  • 官方调用方式:需要在两个函数调用间传递类型信息,可能导致类型丢失或错误。
  • ChainCall 方式:在链式调用中保持类型信息的一致性,避免类型错误。

第四章:vue v3.4 / v3.3 和新特性

defineModel()

这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。组件 v-model 指南中也讨论了示例用法。

在底层,这个宏声明了一个 model prop 和一个相应的值更新事件。如果第一个参数是一个字符串字面量,它将被用作 prop 名称;否则,prop 名称将默认为 "modelValue"。在这两种情况下,你都可以再传递一个额外的对象,它可以包含 prop 的选项和 model ref 的值转换选项。

ts 复制代码
// 声明 "modelValue" prop,由父组件通过 v-model 使用
const model = defineModel()
// 或者:声明带选项的 "modelValue" prop
const model = defineModel({ type: String })

// 在被修改时,触发 "update:modelValue" 事件
model.value = "hello"

// 声明 "count" prop,由父组件通过 v-model:count 使用
const count = defineModel("count")

// 或者:声明带选项的 "count" prop
const count = defineModel("count", { type: Number, default: 0 })

function inc() {
  // 在被修改时,触发 "update:count" 事件
  count.value++
}

defineModel() 宏简化了组件中的双向数据绑定,使代码更加简洁高效。在之前的版本中,实现双向绑定需要定义传入数据的 props (modelValue) 并发出事件 (update:modelValue) 来更新父组件,这种方式既冗长又容易出错。

可以通过这个例子对比:

在 Vue 3.4 之前,实现双向数据绑定通常需要以下步骤:

ts 复制代码
<!-- 父组件 -->
<template>
  <ChildComponent v-model="username" />
</template>

<script setup>
import { ref } from 'vue';

const username = ref('');
</script>

<!-- 子组件 -->
<template>
  <input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({
  modelValue: String
});

const emit = defineEmits(['update:modelValue']);
</script>

使用 defineModel() 的简化版本

ts 复制代码
<!-- 父组件 -->
<template>
  <ChildComponent v-model="username" />
</template>

<script setup>
import { ref } from 'vue';

const username = ref('');
</script>

<!-- 子组件 -->
<template>
  <input type="text" v-model="model" />
</template>

<script setup>
import { defineModel } from 'vue';

const model = defineModel('modelValue', { required: true });
</script>

defineOptions()

以前的方式:混合使用 <script><script setup>

defineOptions() 在 Vue 3.3 中被引入,目的是为了解决在使用 <script setup> 时定义组件选项(如 nameinheritAttrs 等)的繁琐问题。以前,这些选项只能在常规的 <script> 块中定义,这导致了在使用 <script setup> 时的割裂体验

在 Vue 3.3 之前,如果你想使用 <script setup> 并定义组件的选项(如 nameinheritAttrs),你必须在常规的 <script> 块中进行定义。这导致了在同一个文件中混合使用两种脚本标签,造成代码分裂和管理上的不便。

ts 复制代码
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<!-- 常规的 <script> 块 -->
<script>
export default {
  name: 'ExampleComponent',
  inheritAttrs: false,
};
</script>

<!-- <script setup> 块 -->
<script setup>
const message = 'Hello, Vue 3!';
</script>

在这个示例中,我们需要使用两个 <script> 块来分别定义组件选项和组件的逻辑,这使得代码显得分裂且不直观。

defineOptions() 的新方式

在 Vue 3.3 中,引入了 defineOptions(),使得我们可以在 <script setup> 中直接定义组件的选项,从而避免了这种分裂:

ts 复制代码
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<!-- 统一的 <script setup> 块 -->
<script setup>
defineOptions({
  name: 'ExampleComponent',
  inheritAttrs: false
});

const message = 'Hello, Vue 3.3!';
</script>

这个宏可以用来直接在 <script setup> 中声明组件选项,而不必使用单独的 <script> 块:

ts 复制代码
<script setup>
defineOptions({
  inheritAttrs: false,
  customOptions: {
    /* ... */
  }
})
</script>
  • 仅支持 Vue 3.3+。
  • 这是一个宏定义,选项将会被提升到模块作用域中,无法访问 <script setup> 中不是字面常数的局部变量。

Vue 3.4 的 JSX/TSX 相关更新

Vue 3.4 在 JSX/TSX 使用方面进行了显著更新,主要包括:

  1. 全局 JSX 命名空间的移除 :为了避免与 React 的全局命名空间冲突,Vue 不再默认注册全局 JSX 命名空间。开发者需要在 tsconfig.json 中明确指定 jsxImportSourcevue (Vue.js Blog) (Vue.js)。
  2. 更高效的编译和性能 :Vue 3.4 引入了新的模板解析器,显著提升了 SFC(单文件组件)的编译性能,这也包括对 JSX 的处理 (Vue.js Blog)。
  3. 定义模型的稳定性defineModel 宏在 3.4 中变得稳定,使得使用 v-model 的组件实现更加简洁 (Vue.js Blog)。

JSX/TSX 使用指南

对于使用 JSX/TSX 的开发者,Vue 提供了详细的配置指南:

  1. JSX 支持配置create-vue 和 Vue CLI 都提供了预配置 JSX 支持的选项。手动配置时,可以参考 @vue/babel-plugin-jsx 的文档 (Vue.js)。
  2. 类型推断和配置 :确保在 tsconfig.json 中设置 "jsx": "preserve",并在需要的文件顶部添加 /* @jsxImportSource vue */ 注释,以启用 Vue 的 JSX 类型定义 (Vue.js)。

第五章:总结 & 对 Vue3 发展趋势的影响

Vue3 的发展趋势:JSX/TSX 是未来吗 🤔️?

随着 Vue3 的不断发展,特别是在 3.3 和 3.4 版本的更新中,JSX 和 TSX 的使用已经变得越来越普遍。这两种语法为开发者提供了更灵活、更高效的方式来编写 Vue 组件。Vue 3.4 尤其在性能和开发体验上做了大量优化,显著提升了模板解析器和响应性系统的效率,也对 JSX/TSX 这种写法支持度更高。

现在的模板语法与 JSX/TSX 互有优劣

模板语法:

  1. 简单易懂:模板语法直观且易于上手,开箱即用,可以直观地描述UI结构,减少了JavaScript代码的混杂。
  2. 快速开发:由于其直观的语法和框架内置的功能,模板语法适用于快速原型开发
  3. 丰富的工具链支持:Vue 提供了强大的模板编译和优化工具,可以在编译时进行很多优化。

JSX/TSX :

  1. 强大的灵活性:JSX/TSX允许在JavaScript中直接嵌入HTML样式的语法,提供了极大的灵活性来构建动态UI ,但由于JSX/TSX的高度灵活性,可能导致代码组织不当,增加维护难度 。
  2. 更好的类型支持:TSX 结合 TypeScript 使用,可以在编译时进行类型检查,减少运行时错误,虽然模板语法也可以使用TypeScript,但由于其设计上的限制,静态分析和类型检查的效果可能不如TSX来得全面和高效 。

总结 & 未来趋势

根据 Vue 3.4Vue Macros的更新,可以看出 Vue 官方和社区都在大力支持和优化 JSX/TSX 的使用。比如3.4 全局 JSX 命名空间的移除是为了避免与 React 的命名空间冲突,使得 Vue 和 React 可以在同一个项目中共存,表示 Vue 在逐渐走向与 JSX/TSX 更加兼容和优化的方向。新的模板解析器和优化的响应性系统也使得使用 JSX/TSX 编写的组件在性能上得到了很大提升。

但是Vue未来真的会全面拥抱JSX/TSX吗?我个人看法其实是不会🤔️,虽然Vue 3.4 的更新已经为 JSX/TSX 提供了更好的支持,但总体上Vue官方对于JSX/TSX的进一步适配更新还是保持克制的,并且JSX/TSX本身也存在 学习曲线陡峭/复杂度增加/依赖构建工具 的问题,这些问题与 Vue 的开箱即用理念有所不同,所以个人还是更倾向于 Vue 长期会保持JSX/TSX与模板语法的共存状态。但无论未来如何发展,持续关注行业动向还是非常重要的~

参考链接

相关推荐
.生产的驴8 分钟前
SpringBoot 消息队列RabbitMQ 消息确认机制确保消息发送成功和失败 生产者确认
java·javascript·spring boot·后端·rabbitmq·负载均衡·java-rabbitmq
布瑞泽的童话22 分钟前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
白鹭凡34 分钟前
react 甘特图之旅
前端·react.js·甘特图
2401_8628867838 分钟前
蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,游卡,oppo,康冠科技,途游游戏,埃科光电25秋招内推
前端·c++·python·算法·游戏
书中自有妍如玉1 小时前
layui时间选择器选择周 日月季度年
前端·javascript·layui
Riesenzahn1 小时前
canvas生成图片有没有跨域问题?如果有如何解决?
前端·javascript
f8979070701 小时前
layui 可以使点击图片放大
前端·javascript·layui
小贵子的博客1 小时前
ElementUI 用span-method实现循环el-table组件的合并行功能
javascript·vue.js·elementui
明似水1 小时前
掌握 Flutter 中的 `Overlay` 和 `OverlayEntry`:弹窗管理的艺术
javascript·flutter