2021年8月5日发布了Vue3.2.0的版本,目前最新版本是3.2.23版本,3.2.x版本最大的亮点是,提升了响应式性能。
新的编译器(compiler),跳过了很多 运行时(runtime) 检查,并且修改并优化了Composition API的响应式,提升了响应式性能:
ref
API 的读效率提升了260%
,写效率提升了50%
- 依赖收集的效率提升了
40%
, - 减少了约
17%
的内存使用。
让我们看一下一些小功能
v-memo
记住一个模板的子树。元素和组件上都可以使用。该指令接收一个固定长度的数组作为依赖值进行记忆比对。如果数组中的每个值都和上次渲染的时候相同,则整个该子树的更新会被跳过。例如:
<ul v-for="member of members" :key="member.id" v-memo="[member.username]">
<li>{{ member.username }}</li>
</ul>
复制代码
当组件重新渲染的时候,如果 member.username
的值维持不变,那么对这个 <ul>
以及它的所有子节点的更新都将被跳过。事实上,即使是虚拟 DOM 的 VNode 创建也将被跳过,因为子树的记忆副本可以被重用。听起来是不是很酷?
正确地声明记忆数组是很重要的,否则某些事实上需要被应用的更新也可能会被跳过。带有空依赖数组的 v-memo
(v-memo="[]"
) 在功能上等效于 v-once
。
v-memo
仅供性能敏感场景的针对性优化,会用到的场景应该很少。渲染 v-for
长列表 (长度大于 1000) 可能是它最有用的场景:
<ul v-for="member of members" :key="member.id"
v-memo="[member.name, member.id == selectedUser]">
<li>
<span :class="{ selected: selectedUser == member.id }">{{ user.name }}</span>
</li>
</ul>
复制代码
v-demo
数组中也可以接收 条件判断语句,上面的代码演示中,当组件的 selectedUser
状态发生变化时,即使绝大多数 member
都没有发生任何变化,大量的 VNode 仍将被创建,此处使用的 v-memo
本质上代表着“仅在 member 从未选中变为选中时更新它,反之亦然”
在 v-for
中使用 v-memo
时,确保它们被用在了同一个元素上。 v-memo
在 v-for
内部是无效的。
effectScope API
通常情况,响应式副作用绑定到挂载的 vue 实例,当组件被卸载时,依赖项会自动清理,不需要手动修改。但是,当我们在组件外使用或者编写一个独立的包时,这会变得非常麻烦。当在单独的文件中,我们该如何停止computed & watch的响应式依赖呢?新的API-----Effect Scope API出现了,专门用来解决这个问题
使用effectScope API
,创建一个 effect 作用域对象,以捕获在其内部创建的响应式 effect (例如计算属性 computed
或侦听器watch,watchEffect
),使得这些 effect 可以一起被处理。
effectScope是一个函数,调用effectScope函数会返回一个对象,其中包含了run(一个函数)和stop(一个函数);
import { effectScope, watchEffect, computed, ref, watch } from 'vue'
export default {
setup () {
const scope = effectScope()
const counter = ref(0)
//2秒修改一次
setInterval(() => {
counter.value++
}, 2000)
//创建一个 effect 作用域对象
scope.run(() => {
const doubled = computed(() => counter.value * 2)
watch(doubled, () => console.log('doubled:', doubled.value))
watchEffect(() => console.log('Count: ', counter.value))
})
scope.run(() => {
watchEffect(() => console.log(`counter: ${counter.value}`))
})
// 处理该作用域内的所有 effect
// scope.stop()
}
}
复制代码
如果没有调用scope.stop()
,浏览器一直会输出结果
当调用了stop之后,浏览器只会输出一次。
效果:
v-bind
缩写::
或 .
(当使用 .prop
修饰符时)
修饰符:
.prop
: 将一个绑定强制设置为一个 DOM property.attr
:将一个绑定强制设置为一个 DOM attribute,新的快捷方式:^
动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
在绑定 class
或 style
attribute 时,支持其它类型的值,如数组或对象。可以通过下面的教程链接查看详情。
在绑定 prop 时,prop 必须在子组件中声明。可以用修饰符指定不同的绑定类型。
没有参数时,可以绑定到一个包含键值对的对象。注意此时 class
和 style
绑定不支持数组和对象。
<!-- 绑定 attribute -->
<img v-bind:src="imageSrc" />
<!-- 动态 attribute 名 -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc" />
<!-- 动态 attribute 名缩写 -->
<button :[key]="value"></button>
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName" />
<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]"></div>
<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
<!-- 绑定一个全是 attribute 的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- prop 绑定。"prop" 必须在 my-component 声明 -->
<my-component :prop="someThing"></my-component>
<!-- 将父组件的 props 一起传给子组件 -->
<child-component v-bind="$props"></child-component>
<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>
复制代码
同样,v-bind也可以用到style标签中,看代码:
<script setup>
import { ref } from 'vue';
const color = ref('blue')
const changeColorToRed = () => {
color.value = 'red'
}
</script>
<template>
<p class="colored">{{ color }} colored text!</p>
<button @click="changeColorToRed">
Change color to red! (once and for all)
</button>
</template>
<style scoped>
.colored {
color: v-bind(color);
}
</style>
复制代码
Async Setup
重要:vue 3.0中,生命周期钩子不能在await
。下面是正确的做法
async setup() {
onCreated(() => console.log('Server side created, let\'s get data.'));
const data = await fetchData();
onMounted(() => console.log(`We have the data -{$data.metaInformation}`)); // can now be called
}
复制代码
Script Setup
<script setup>
是一个语法糖
<script setup lang="ts">
import { ref } from 'vue';
const welcome = ref('Hello Tailiang') as string | undefined;
const count = ref(0) as number;
const increaseCount = () => {
count.value++;
}
increaseCount()
</script>
<template>
{{ welcome }} I have a counter on my website!
Counter: {{ counter }}
</template>
复制代码