Vue3|Composition_API (全)总结
为了能够使用Composition API, 我们需要有一个可以实际使用它的地方。在vue组件中,我们将此位置称为setup
setup函数
setup函数是在组件创建之前执行的,setup函数中的props一旦被解析,就将作为Composition API 的入口
需要注意的是: 我们不能在setup函数中使用this, 因为它不会找到组件实例。 原因是 虽然组件实例是在执行setup之前就会被创建出来了,但是
setup函数被调用之前,data,computed,methods等都没有被解析,所以它们无法在setup中被获取。
setup接收俩个参数(props,context)
props
作为setup函数中的第一个参数,props是响应式的,当传入新的prop时,它将会被更新
其实就是父组件传递过来的属性会被放到props对象中
- 对于定义props的类型,和Vue2一样,在props选项对象中定义
- 在template中的写法依然是不变的
- 因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用。
1 |
|
!! 由于 props
是响应式的, 所以我们不能用 解构,它会消除props的响应性。
如果需要结构props,那么需要用到 toRefs
函数来完成操作。
1 |
|
context
作为setup函数的第二个参数, context是一个普通的JavaScript对象,它暴露组件的三个property
- **attrs ** :所有的非prop的attribute
- slots :父组件传递过来的插槽
- emit : 当我们组件内部需要发出事件时用到emit, 和vue2中 this.$emit用法一致
!!由于context只是一个普通的JavaScript对象,所以它不是响应式的,这就意味着我们可以安全地对 context
使用 解构
1 |
|
setup函数的返回值
由于setup是一个函数,那么它就会有返回值
如果setup的返回值是一个对象,那么该对象的property就可以在模板中访问到。
注意的是:从setup中返回的 refs
在模板中是会自动浅解包的,所以不必在模板中用 .value
有关响应性的API
reactive
返回对象的响应式副本
如果想在setup中定义的数据是具有响应式的,那么就可以使用 reactive
1 |
|
为什么会通过 reactive
就可以变成响应式的呢?
- 因为当我们使用reactvie函数处理我们的数据之后,数据再次被使用时会进行以依赖收集
- 当数据发生变化时,所有收集到的依赖都是进行对象的响应式的操作
- 事实上,在vue2中,我们编写的 data选项,也是在内部交给了
reactive
函数将其变成了响应式对象的
注意的俩点:
reactive
将解包所有深层的 refs,同时维持着 ref 的响应性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const count = ref(0)
const obj = reactive({ count })
// ref 会被解包
console.log(count.value === obj.value) // true
// 会更新到 'obj.count'
count.value++
console.log(count.value) // 1
console.log(obj.count) // 1
// 也会更新到 ref 'count'
obj.count++
console.log(count.value) // 2
console.log(obj,value) // 2
当把
ref
分配给reactive
时,将会自动解包1
2
3
4
5const count = ref(1)
const obj = reactive({ })
obj.count = count
console.log(obj.count) // 1
console.log(obj.count === count.value) // true
但是由于 reactive
对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组
如果我们传入的是一个基本的数据类型(String,Number,Boolean)则会报警告.
这时,我们可以使用另一个API, ref
ref
接受一个内部值并返回一个响应式且可变的 ref 对象。 ref对象具有指向内部值的单个property (.vlaue)
ref 会返回一个可变的响应式对象,该对象作为一个响应式的引用(reference) 维护着它内部的值。
内部的值是在 ref的 vlaue属性中被维护的
readonly
我们通过
reactive
或者ref
可以获取到一个响应式的对象,但是某些情况下, 我们传入给其他地方(组件) 的这个响应式对象希望在另外一个地方(组件)被使用,但是不能被修改,这个时候就可以用 readonly 了
readnoly 会返回原生对象的只读代理 原理在于: 利用 proxy 中的set,在set方法中将其劫持,并且设置该值不能修改
关于reactive 判断 的API
isProxy
检查对象是否由
reactive
或readonly
创建的 proxy
1 |
|
isReactive
检查对象是否由
reactive
创建处理的相应手机代理
但如果代理是由 readonly
创建的,但包裹了由 reactive
创建 另一个代理,也会返回 true
1 |
|
isReadOnly
检查对象是否由
readonly
创建的只读代理
toRaw
返回
reactive 或 readonly 代理的原始对象
(不建议保留对原始对象的持久化引用,谨慎使用)
shallowReactive
shallow(浅层)
创建一个响应式代理,他跟踪其自身property的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)
类似于浅拷贝,只把第一层转为响应式了,深层还是原始对象
shallowReadonly
创建一个 proxy , 使其自身的 property 为只读, 但不执行嵌套对象的深度只读转换
就是说第一层是只读的,但是深层还是可读,可写的
toRefs
由于我们使用ES6的解构语法对 reactive 返回的对象进行解构赋值,那么解构后的数据是不具有响应式的
而使用 toRefs 可以将 reative 返回的对象中的属性都转成 ref 这样我们再次解构出来的数据都是 ref的。
1 |
|
toRef
如果我们希望转换一个 reactive 对象中的属性为 ref ,那么可以使用 toRef 的方法
1 |
|
ref其他的API
unref
如果想要获取一个ref引用中的value,可以通过 unref 方法:
如果参数是一个 ref, 则返回内部值,否则返回参数本事
其实是一个语法糖:
val = isRef(val) ? val.value : val
1
2
3
4
5
```ts
function useFoo(x: number | Ref<number>) {
const unwrapped = unref(x) // unwrapped 现在一定是数字类型
}
isRef
判断值是否是一个ref对象
shallowRef
创建一个浅层的ref对象
1 |
|
triggerRef
手动触发和 shallowRef 相关联的副作用
1 |
|
customRef
创建一个自定义的 ref ,并对其依赖项跟踪和更新触发进行显示控制
- 它需要一个工厂函数,该函数接受 track 和 trigger 函数 作为参数
- 应该返回一个带有 get 和 set 的对象
用 customRef 做的一个防抖案例
1 |
|
1 |
|
computed
该API 的方法 和 vue2 的一样, 只不过写的地方是在setup函数中了, 但还需注意的点是 computed 返回的值是一个 ref
- 方式一: 接收一个 getter 函数,并为 getter 函数返回的值,返回一个不变的 ref 对象
1 |
|
- 方式二 : 接收一个具有 get 和 set 的对象,返回一个可变的(可读写的)ref对象
1 |
|
侦听数据的变化 (watch watchEffect)
在 Composition API 中, 我们可以使用 watchEffect 和 watch 来完成响应式数据的侦听
- watch 需要手动指定侦听的数据源
- watchEffect 用于自动收集响应式数据的依赖
watchEffect
当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候就可以用 watchEffect
来看一个案例:
1 |
|
- 通过以上代码,首先 watchEffect 传入的函数会立即被执行一次,并且在执行的过程中会收集依赖
- (为什么需要立即执行一次的原因就是需要去收集依赖)
- 其次,只有收集的依赖发生变化时,watchEffect 传入的函数才会被执行
watchEffect 的停止侦听
- 如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取 watchEffect 的返回值函数, 调用它即可
看一个案例:
1 |
|
watchEffect 清除副作用
用途: 比如在开发中我们需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候,我们停止了侦听器,或者侦听器侦听函数被再次执行了,这时我们需要把上一次的网络请求 取消掉, 就可以用到该方法了
看了一个案例:
1 |
|
在上述代码中,我们给 watchEffect 传入的函数被回调时,可以获取到一个参数:onInvalidate (该参数是一个函数),可以在这个参数中,执行一些清除工作。
watchEffect 的执行时间(刷新时机)
在默认情况下 , 组件的更新会在 watchEffect (副作用函数) 之前执行
1 |
|
那么,当我们在 watchEffect (副作用函数) 获取元素时, 第一次执行是肯定是个null, 是不可以的。只有当DOM挂载完毕后,才会给 title 赋新的值,watchEffect (副作用函数)才会再次执行, 打印出对应的元素
我们希望第一次就打印出该元素的话,这时候需要给 watchEffect 传入第二个参数, 该参数是一个 对象,对象中的 flush 可取三个值 : 'pre' (默认的) , 'post' , 'async' (不建议使用)
1 |
|
watch
watch API 的功能和 vue 2 的 option API 中的 watch 功能一样, 默认情况下, watch 只有当被侦听的源发送改变时才会去回调
与 watchEffect 比较,不同的是:
- watch 是 懒执行副作用
- 更具体地说明了什么状态下应该触发侦听器重新运行
- 可以访问到侦听状态变化前后的值
侦听单个数据源
想要侦听单个数据源的话, 有俩种方法: 传入 getter 函数 或 ref 对象
1 |
|
侦听多个数据源
方法: 传入数组
1 |
|
侦听响应式对象
就是侦听 reactive 对象
1 |
|
想要深度侦听嵌套对象或数组时,需要 deep 设为 true,
1 |
|
但是会发现 新的值和旧的值是一样的。这时候为了完全侦听,需要使用深拷贝了
其他API
生命周期函数
em…. 去看文档吧,
Provide / Inject
功能和之前一样,
我们可以通过 provide 来提供数据
- 通过 provide 方法来定义每个 property
- 传入俩个参数: name(提供的属性名称), value(提供的属性值)
我们可以通过 jnject 来注入需要的内容
- 要 inject 的 property 的 name
- 默认值 (可选)
1 |
|
h函数
vue在生成真实DOM之前,会将我们的节点转换成VNode,而VNode组合起来会形成一颗树结构,即 虚拟DOM
在 template 中的 html 是使用渲染函数生成的对应的VNode
如果我们想要利用 JavaScript 来编写 createVNode 函数,生成对应的VNode 那么就可以使用 h() 函数
h() 函数是一个用于常见 VNode 的一个函数, 其实更准确的命名是 createVNode() 函数,但为了简便 vue 将其简化为 h() 函数
h() 函数接收三个参数: (标签,属性,后代)
1 |
|
注意:如果没有props,可以将 children 作为第二个参数传入, 但是会产生歧义,所以一般会将 null 作为第二个参数传入,将 children 作为第三个参数传入
h函数的基本使用
- 可以在 render 函数选项中使用
- 可以在 setup 函数选项中使用
1 |
|
这样写代码,不仅慢而且阅读性一般, 所以 推荐使用 jsx, 语法和 react一样, 这里不细说了。可看文档
自定义指令
在 Vue 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
自定义指令分为俩种:
- 自定义局部指令: 组件中通过 directives 选项,只能在当前组件中使用。
- 自定义全局指令: app的 directive 方法, 可以在任意组件中被使用。
指令的生命周期
一个指令定义的对象,Vue提供了如下的几个钩子函数:
- created:在绑定元素的 attribute 或事件监听器被应用之前调用;
- beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用;
- mounted:在绑定元素的父组件被挂载后调用;
- beforeUpdate:在更新包含组件的 VNode 之前调用;
- updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用;
- beforeUnmount:在卸载绑定元素的父组件之前调用;
- unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次
这几个钩子函数中可传入四个参数:el
、binding
、vnode
和 prevNnode
案例:时间格式化的指令
Teleport
瞬移组件, 可以将该组件转移到其他dom元素上。
通常用于 封装模态框、土司之类的,将它放在Body元素上和 div#app 元素平级
俩个属性
- to: 指定将其中的内容移动到的目标元素,可以使用选择器
- disabled: 是否禁用 teleport 的功能
1 |
|
Vue插件
通常情况下,我们向Vue全局去添加一些功能时,会采用插件的模式
俩种编写方式
- 对象类型
- 一个 对象,但是必须包含一个
install
的函数,该函数会在安装插件时执行
- 一个 对象,但是必须包含一个
- 函数类型
- 一个 function,这个函数会在 安装插件时自动执行
1 |
|