ljianshu / Blog

关注基础知识,打造优质前端博客,公众号[前端工匠]的作者
7.9k stars 1.19k forks source link

Vue 3.2 有哪些新变化? #109

Open ljianshu opened 3 years ago

ljianshu commented 3 years ago

前言

8.10号凌晨,尤雨溪在微博平台官宣 Vue 3.2 版本正式发布: image.png

此版本包含一系列重要的新功能与性能改进,但并不涉及任何重大变更。本文主要介绍一些相对重要 Vue3.2新特性,如需了解更多请查阅官方文档!

新的 SFC 功能

关于单文件组件(SFC,即.vue 文件)的两项功能已经由实验状态正式毕业,现提供稳定版本

1、<script setup>

<script setup>中,我们不必声明export defaultsetup方法,这种写法会自动将所有顶级变量、函数,均会自动暴露给模板(template)使用。我们先来通过一个例子,对比script setup前后写法的不同,直观感受下setup带给我们的便利:

// script setup之前的写法
<template>
  <div>
    <div>浪里行舟</div>
    <Card>{{ message }}</Card>
  </div>
</template>
<script lang="ts">
import { ref, defineComponent } from "vue";
import Card from "./components/Card.vue";

export default defineComponent({
  components: {
    Card,
  },
  setup() {
    const message = ref("vue 3.2 新特性 script setup");
    return { message };
  },
});
</script>
// script setup的写法
<template>
  <div>
    <div>浪里行舟</div>
    <Card>{{message}}</Card>
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import Card from "./components/Card.vue";
const message = ref("vue 3.2 新特性 script setup");
</script>

从上面的例子来看,<script setup>语法省去了组件Card的注册步骤,以及return变量message的语句,使得代码更为精简。关于<script setup>的使用还有些细节和注意事项,我将会在下一篇文章详细介绍。

2、<style> v-bind

挺有趣的一个新特性,通过这个指令,Vue SFC 的 CSS 灵活性将大大提高。该指令适用于<script setup>, 并支持 JavaScript 表达式(必须用引号括起来)。

<script setup>
import { ref } from "vue";
const color = ref("pink");
color.value = "green";
const fontSize = ref("18px");
</script>
<template>
  <h2>浪里行舟</h2>
  <h1>Hello Vue3.2</h1>
  <h2>{{ color }}</h2>
  <button @click="color = 'red'">color red</button>
  <button @click="color = 'yellow'">color yellow</button>
  <button @click="color = 'blue'">color blue</button>
  <button @click="fontSize = '40px'">fontSize 40px</button>
</template>
<style scoped>
h1 {
  color: v-bind(color);
}
h2 {
  font-size: v-bind(fontSize);
}
</style>

1.gif

点击按钮更改color 或者 fontSize的数值,可以看到页面样式也会响应式变化。其原理就是自定义属性将通过内联样式应用于组件的根元素,并在数值更改时进行响应更新。

v-memo

3.2 版本为 Vue 的响应式系统带来了一系列重大性能改进,具体包括:

新版本还提供新的 v-memo 指令,可实现对部分模板树的记忆功能。当v-memo 命中时,不仅允许 Vue 跳过虚拟 DOM 差异、甚至可以完全跳过新 VNode 的创建步骤。虽然这个指令使用频率不高,但它提供了一个逃生舱来在某些情况下(例如处理大型 v-for 列表)获取最大性能。

<div v-for="user of users" :key="user.id" v-memo="[user.name]">
  {{ user.name }}
</div>

这个例子使用v-memo,不会重新创建虚拟元素,并且会重新使用前一个元素,除非v-memo(此处为用户名)的条件发生变化。这可能看起来是一个很小的改进,但如果您渲染大量元素,它实际上是性能的巨大改进。

其实v-memo可以接受一组条件,请看下面的例子:

<div v-for="user of users" :key="user.id" v-memo="[user.name, selectedUserId === user.id]">
  <p :class="{ red: selectedUserId === user.id }">{{ user.name }}</p>
</div>

此时如果user.nameselectedUserId发生变化,div则将更新。

新 ref 语法糖(实验性)

$ref()避免在更新 ref 值时需要使用.value,可以让代码更加精简!请看下面例子:

<template>
  <input type="number" v-model="count"> * 5€
  <h1>{{ total }}</h1>
</template>

<script setup>
  let count = $ref(0)
  let total = $computed(() => count * 5)
</script>

⚠️注意:这还是一个实验性特性,所以请谨慎使用,因为它将来可能会发生变化。该提案还引入了其他新的语法糖,包括$computed()$fromRefs()$raw()

Expose API

Vue 3.2 添加了一个新的 Expose API 来定义组件公开的内容。Expose API 的设想是提供一个像 expose({ ...publicMembers }) 这样的组合式 API,这样组件的作者就可以在 setup() 中使用该 API 来精细设定公开暴露给其他组件的内容。

下例中,该组件只能公开其toggle函数,而不能公开其collapsed变量。

export default defineComponent({
  setup(props, { expose }) {
    const collapsed = ref(true)
    const toggle = () => {
      collapsed.value = !collapsed.value;
    }
    // only expose `toggle` to the parent component
    expose({ toggle })
    return { collapsed, toggle }
  }
})

请注意,所有$实例属性都会自动公开,因此使用Collapse的组件可以访问$props$slots以及其他。<script setup>通过调用defineExpose()函数使用时也可以这样做。

当你在封装组件时,如果嫌ref 中暴露的内容过多,不妨用 Expose API 来约束一下输出吧!

Effect Scope API

Vue 3.2版本引入了新的 Effect scope API,用于创建一个effect Scope对象,该对象可以捕获在其中创建的反应性效果(例如computedwatchers),以便可以将这些效果放在一起并轻松处理它们。它可以更轻松地在组件上下文之外使用 Vue 的响应式 API,同时也在组件之内解锁了多种高级用例。Effect scope 是一种高级 API,主要供库作者使用。

我们知道watch, watchEffect,computed等都是绑定到一个特定的组件实例上的,在组件销毁的时候会被 Vue 自动销毁。这可确保应用程序没有内存泄漏。但是如果你想在组件之外使用这些函数,例如在你正在编写的库中,你需要手动处理它们,请看下例:

import { ref, computed, stop, watchEffect } from 'vue';

const quantity = ref(0);
const price = ref(10);
const total = computed(() => quantity.value * price.value);
const stopWatch = watchEffect(() => console.log(`total changed to ${total.value}`));

let effectsToStop = [];
effectsToStop.push(() => stop(total));
effectsToStop.push(stopWatch);
const stopAll = () => {
  effectsToStop.forEach(f => f())
  effectsToStop = []
};
// calling `stopAll()` disposes of all effects

.prop 和 .attr 修饰符

v-bind 默认绑定到 DOM 节点的 attribute 上,使用.prop修饰符后,设置的自定义属性不会在渲染后的 HTML 标签里显示,而.attr修饰符则刚好相反!

.prop修饰符用途:

<input id="input" type="foo" value="11" :data.prop="inputData"></input>// 渲染后HTML标签结构<input id="input" type="foo" value="11"></input>

看了它的用途就知道,如果你不想你的属性显示在html标签里面,就用.prop修饰符吧!

另外这两个修饰符有简写的语法:

<a :title.prop="firstTabTooltip" :aria-selected.attr="isFirstTabSelected">First tab</a><!-- 简写 --><a .title="firstTabTooltip" ^aria-selected="isFirstTabSelected">First tab</a>

Web 组件

Vue 3.2 引入了新的 defineCustomElement 方法,可以使用 Vue 组件 API 轻松创建原生自定义元素:

import { defineCustomElement } from 'vue'const MyVueElement = defineCustomElement({  // 常规 Vue 组件选项})// 注册自定义元素。// 注册完成后,此页面上的所有 `<my-vue-element>` 标签// 都将将升级。customElements.define('my-vue-element', MyVueElement)

此 API 允许开发者们创建由 Vue 驱动的 UI 组件库。这些库可以支持任何框架选项,甚至能够在无框架情况下正常使用。

总结

以上诸多特性,最让我感兴趣的是setup script,此语法使单个文件组件更简单!只需要给 script 标签添加一个 setup 属性,那么整个 script 就直接会变成setup函数,所有顶级变量、函数,均会自动暴露给模板使用(无需再一个个 return了),开发效率将大大的提高!

以至于连尤大也在微博上呼吁大家:“如果你能用Vue3却还在用 Options API,现在有了< script setup>没有理由不换 Composition API了”

参考资料