前言
- 响应式工具解读
- 响应式API:进阶解读
- vue3中自定义指令与vue2有哪些不同
- 特殊属性
1、响应式工具
1-1. toRef()
描述
基于一个响应式对象,解构出一个可读写的属性,并且与源属性所关联sou
语法
// sourceObject 源对象<响应式对象>
// sourceAttribute 源属性<string>
const ref = toRef(sourceObject, sourceAttribute)
例子
const state = reactive({ foo: 1, bar: 3 })
const fooRef = toRef(state, 'foo')
// 修改 toRef 生成的属性会更新源属性
fooRef.value ++
console.log(state.foo) // 2
// 更改源属性也会更新 ref
state.foo ++
console.log(fooRef.value) // 3
1-2. toRefs()
描述
toRefs复制关联整个响应式对象将其转成普通对象,使其可以解构/展开并且不会失去响应式
语法
const { foo, bar } = toRefs(souceObject)
例子
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2
})
// ...基于状态的操作逻辑
// 在返回时都转为 ref
return toRefs(state)
}
// 可以结构不会失去响应式
const { foo, bar } = useFeatureX()
2、响应式API:进阶
2-1. shallowRef()
描述
ref()的浅层转换,只会将第一层转为响应式
例子
const peopleObj = shallowRef({ name: '张三' })
watchEffect(() => {
console.log("name", peopleObj.value.name)
})
// name 张三
peopleObj.value.name = '我被修改了' // 不会触发更新,是一个副作用
// name 我被修改了
peopleObj.value = { name: '我被修改了' } // 会触发更新
2-2. triggerRef()
描述
强制更新浅层ref的副作用,通常在对浅引用的内部值进行深度变更后使用。
例子
const shallow = shallowRef({
greet: 'Hello, world'
})
// 触发该副作用第一次应该会打印 "Hello, world"
watchEffect(() => {
console.log(shallow.value.greet)
})
// 这次变更不应触发副作用,因为这个 ref 是浅层的
shallow.value.greet = 'Hello, universe'
// triggerRef会强制执行shallowRef修改所产生的副作用
// 打印 "Hello, universe",
triggerRef(shallow)
2-3. shallowReactive()
描述
和 reactive() 不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。
例子
const state = shallowReactive({
foo: 1,
nested: {
bar: 2
}
})
// 更改状态自身的属性是响应式的
state.foo++
// ...但下层嵌套对象不会被转为响应式
isReactive(state.nested) // false
// 不是响应式的
state.nested.bar++
2-4. shallowReadonly()
描述
和 readonly() 不同,这里没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。
例子
const state = shallowReadonly({
foo: 1,
nested: {
bar: 2
}
})
// 更改状态自身的属性会失败
state.foo++
// ...但可以更改下层嵌套对象
isReadonly(state.nested) // false
// 这是可以通过的
state.nested.bar++
2-5. effectScope()
描述
创建一个 effect 作用域,可以捕获到作用域内所有的响应式副作用
例子
const scope = effectScope()
scope.run(() => {
const doubled = computed(() => counter.value * 2)
watch(doubled, () => console.log(doubled.value))
watchEffect(() => console.log('Count: ', doubled.value))
onScopeDispose(() => {
console.log('scope 被停止了!')
})
})
// 处理掉当前作用域内的所有 effect
scope.stop()
getCurrentScope() // 返回 effectScope()
2-6. getCurrentScope()
描述
返回存在的 effect作用域 或 undefined
例子
2-7. onScopeDispose()
描述
在当前活跃的 effect 作用域上注册一个处理回调函数。当相关的 effect 作用域被注销是会调用这个回调函数
例子
3、自定义指令
3-1. v-if & v-for
不同于 vue2,在 vue3 中 v-if 优先级比 v-for 优先级高,不过还是不推荐一起使用
3-2. v-memo 3.2+
描述
缓存子组件,如果每一个值与最后一次渲染相同,整个子树的更新将被跳过。
<div v-memo="[valueA, valueB]">
...
</div>
当组件重新渲染,如果 valueA 和 valueB 都保持不变,这个 <div> 及其子项的所有更新都将被跳过。
v-memo 传入空依赖数组 (v-memo="[]") 将与 v-once 效果相同。
4、组件
4-1. 内置元素 <components>
<components>可以搭配 is 实现动态组件
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
<--! is 接受一个 string | Component 类型,会动态渲染对应组件-->
<component :is="Math.random() > 0.5 ? Foo : Bar" />
</template>
5、特殊属性
5-1. key
vue3 的 key 与 vue2 的区别
-
vue3中key可以被绑定在template容器上,这在vue2中是不被允许的<template v-for="todo in todos" :key="todo.name"> <li>{{ todo.name }}</li> </template>
-
vue3会自动为条件分支生成唯一的key,在vue2中需要手动添加<--! Vue2.x --> <div v-if="loginType === 'username'" key="a">A</div> <div v-else key="b">B</div> <--! Vue3 --> <div v-if="loginType === 'username'">A</div> <div v-else>B</div>
5-2. is
用于绑定动态组件
- 当
is被绑定在普通 html 标签时,会当作普通属性存在 - 在 components 使用时,保留
vue2的功能