Vue3 官网更新,更了啥?

680 阅读4分钟

不久前,Vue 官网进行了一次大更新,我也被朋友安利又去阅读了一遍。

可以说新版本的官网,不管从阅读体验还是知识讲解上都比原先的更顺滑舒服。

我将其中一些较为明显的改变总结列举出来,与掘友们分享。

风格偏好

新官网文档中最值得称赞的就是提供了一个代码风格的选项。

我们知道,Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API

几乎所有实例,官方都给出了两种风格的写法

image.png

由于我是忠实的组合派,所以本文的代码也是以组合式的写法

互动教程

这次官方推出了一种新的文档风格——互动教程

让读者在阅读文档的同时,可以随时编写代码查看效果。

针对每一个知识点都也都提出了问题需求,让读者在问题的驱导下学习。

image.png

知识全面

这次官方文档的更新,讲的知识更全、更深,解答了我很多 Vue3 方面的疑惑

简单列举几个知识点吧,这些我在旧文档没有找到的内容,在新文档中都进行了具体的讲解。

计算属性

在新文档中,官方将计算属性缓存特性与方法进行了比较,明确指出计算属性仅会在其响应式依赖更新时才重新计算,而方法调用总是会在重渲染发生时再次执行函数。

并解释了一些模糊的现象:下面的计算属性永远不会更新,因为 Date.now() 并不是一个响应式依赖

const now = computed(() => Date.now())

样式绑定

官网详细列举了为元素绑定样式的三种语法:字符串、数组、对象

并说明了在组件上绑定样式会触发的行为——样式透传

关于样式透传的内容在原文档中压根都没提及,我当时研究这部分全靠测试。刚好官网这次更新了,就展开讲讲:

样式透传指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的属性或者 v-on 事件监听器。最常见的例子就是 classstyle 和 id

当组件以单个元素作为根渲染时,这些“遗漏”的属性会被添加到组件的根元素上,与根元素上的属性合并

如果不希望组件继承这些属性,可以设置 inheritAttrs: false

这些透传进来的属性可以在模板的表达式中直接用 $attrs 访问到。

<span>Fallthrough attribute: {{ $attrs }}</span>

或动态绑定到内部元素身上。

<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">click me</button>
</div>

没有参数的 v-bind 会将一个对象的所有属性都作为 attribute 应用到目标元素上。

当组件有多个根节点时,没有自动的属性透传行为。且需要显示地绑定 $attrs,否则会发出一个警告

<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

渲染函数&JSX

虽然 Vue 推荐使用模板语法,但仍有许多人钟赖忠睐于使用渲染函数JSX编写组件。

官方提供了一些渲染函数 / JSX 语法的案例,实现常见的模板功能,如 v-ifv-forv-onv-model、自定义指令/修饰符、渲染/传递插槽 等等。对这部分内容做了详细的讲解。

深入响应式&渲染机制

官方文档对响应式系统渲染过程这两个较底层的内容做了较详细的讲解。

最令我感动的是,终于对源码中变量的含义给出了官方的说明,之前写文章总是纠结于要怎么翻译讲述(泪奔)。

有关响应式的源码解析我已经出过了——万字细说 Vue3 响应式原理

等这个月下旬把视图更新相关的源码解析抛出来。

TS支持

官方介绍了如何在 Vite 和 Vue CLI 中配置 ts,并且推荐了两个官方插件:VolarTypeScript Vue Plugin

官方还介绍了个 Volar Takeover 模式来优化编译器性能,但我没有成功打开,知道如何打开的可以教我一下。

官方也详细介绍了那些常用的(组合式) api,如何定义 ts 类型。

props

<script setup lang="ts">
const props = defineProps({
  foo: { type: String, required: true },
  bar: Number
})

props.foo // string
props.bar // number | undefined
</script>

emits

<script setup lang="ts">
// 运行时
const emit = defineEmits(['change', 'update'])

// 基于类型
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
</script>

ref

// 得到的类型:Ref<string | number>
const year = ref<string | number>('2020')

year.value = 2020 // 成功!

reactive

import { reactive } from 'vue'

interface Book {
  title: string
  year?: number
}

const book: Book = reactive({ title: 'Vue 3 指引' })

computed

const double = computed<number>(() => {
  // 若返回值不是 number 类型则会报错
})

定义类型的方式不止一种,详情请到官网查看,还有为事件、依赖注入、模板引用等内容。

响应性语法糖

官方也对一些最新的响应性语法糖进行了介绍

$ref

$ref() 是一个编译时的宏命令,能帮我们省去繁琐的 .value

let count = $ref(0)

console.log(count)

function increment() {
  count++
}

$ref() 不是一个真实的的方法,而是用作 Vue 编译器的标记,表明最终的 count 变量需要是一个响应式变量。在 Vue 编译后的代码如下:

import { ref } from 'vue'

let count = ref(0)

console.log(count.value)

function increment() {
  count.value++
}

每一个会返回 ref 的响应式 API 都有一个相对应的、以 $ 为前缀的宏函数。包括以下这些 API:

$() 解构

$() 也是一个宏,具有 toRef 的功能

import { reactive } from 'vue'

const obj = reactive({ x:1, y:2 })

const { x, y } = $(useMouse())

console.log(x, y)

编译输出为:

import { reactive, toRef } from 'vue'

const obj = reactive({ x:1, y:2 })

const __temp = obj,
  x = toRef(__temp, 'x'),
  y = toRef(__temp, 'y')

console.log(x.value, y.value)

props 解构

现在的 <script setup> 中对 defineProps 宏的使用有两个痛点:

  1. 和 .value 类似,为了保持响应性,你始终需要以 props.x 的方式访问这些 prop。这意味着你不能够解构 defineProps 的返回值,因为得到的变量将不是响应式的、也不会更新。
  2. 当使用基于类型的 props 的声明时,无法很方便地声明这些 prop 的默认值。为此官方提供了 withDefaults() 这个 API,但使用起来仍然很笨拙。

所以官方提供了响应性语法糖,我们就也可以在使用 defineProps 时像响应式变量一样进行解构了:

interface Props {
  msg: string
  count?: number
  foo?: string
}

const {
  msg,
  // 默认值正常可用
  count = 1,
  // 解构时命别名也可用
  // 这里我们就将 `props.foo` 命别名为 `bar`
  foo: bar
} = defineProps<Props>()

watchEffect(() => {
  // 会在 props 变化时打印
  console.log(msg, count, bar)
})

上面的代码将被编译成下面这样的运行时声明:

export default {
  props: {
    msg: { type: String, required: true },
    count: { type: Number, default: 1 },
    foo: String
  },
  setup(props) {
    watchEffect(() => {
      console.log(props.msg, props.count, props.foo)
    })
  }
}

$$()

$$() 是为了解决 $ref 声明的响应式对象自动解包的问题。又是我想们希望传递的是响应式对象,而不是其值,就可以用 $$() 包裹

let x = $ref(0)

let y = x  // y 是普通变量

上面的语句将被翻译为:

let x = ref(0)

let y = x.value

我们的 y 需要的是一个真正的 ref, 而不是 ref 内的值。

我们可以使用 $$(),保持 y 的响应性。

let x = $ref(0)

let y = $$(x)

编译后将符合我们的预期

let x = ref(0)

let y = x

显示启用

响应性语法糖目前是一个实验性功能,默认是禁用的,需要显式选择使用。具体设计在最终定稿前仍可能发生变化。

官方详解介绍了如何在 Vite vue-cli webpack+vue-loader 三种打包环境下如何启用响应性语法糖,以及需要的版本配置。

API 介绍

官方将上百个 API 进行了分类,分为 全局 API | 组合式 API | 选项式 API | 单文件组件 API | 进阶 API 以及指令组件等内置内容

每个 API 都有非常细致的讲解,标注了其 ts 类型,大部分都提供了示例,帮助读者学习理解。

结语

以上,就是我为掘友们整理的 Vue 官网的更新内容。

较以前确实有非常大的改善,不管你是 Vue 的初学者,还是早已熟练使用的老鸟,都应该去看一看。