💫 透过 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与模板语法的共存状态。但无论未来如何发展,持续关注行业动向还是非常重要的~

参考链接

相关推荐
Qrun1 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp1 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.2 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl4 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫5 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友5 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理7 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻7 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
mapbar_front8 小时前
在职场生存中如何做个不好惹的人
前端
牧杉-惊蛰8 小时前
纯flex布局来写瀑布流
前端·javascript·css