1. 了解composition API

Options API的弊端:

vue2中使用的时Options API,其一大特点就是在对应的属性中编写对应的功能模块;比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变,也包括生命 周期钩子。

但是这种代码有一个弊端:

当我们实现某一功能时,这个功能对应的代码逻辑会被拆分到各个属性中,阅读困难,当我们处理单个逻辑关注点时,需要不断的跳到相应的代码块中。compositions API 的出现解决了这个大组件逻辑分散的问题。

image-20211111145129396.png

image-20211111144925485.png

2. setup 函数

setup是composition API的入口,该选项在组件被创建之前执行。

1.官方提示:setup内部并未绑定this。

image-20211116112027935.png

2.1 setup 参数

setup主要有两个参数: props 和context。

1.setup 函数中的第一个参数是 props。正如在一个标准组件中所期望的那样,setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。

<script>
import {toRefs} from 'vue'
export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
    //官方提示props不能使用ES6解构,他会影响prop的双向绑定
    //所以要解构的话要使用内置方法toRefs
    const { title } = toRefs(props)
    console.log(title.value)
  }
}
</script>

2.传递给 setup 函数的第二个参数是 context。``context是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对context` 使用 ES6 解构。

context有四个属性:

  1. attrs:所有的非prop的attribute,等同于 $attrs
  2. slots:父组件传递过来的插槽,等同于 $slots
  3. emit:等同于 $emit
  4. expose: 暴露公共 property (函数)

2.2 setup的返回值

setup的返回值可以在模板template中被使用,也就是说我们可以通过setup的返回值来替代data选项

image-20211116145012344.png

2.3 setup响应式API

setup函数中定义的变量是非响应式的。data中的数据之所以时响应式的,是因为Vue内部将数据交给了reactive()方法。

2.3.1 reactive API

注意:reactive API 对传入的类型有限制,只能传入对象或数组类型。另外reactive会对深层refs进行解包,所以我们不用写demo.age.value。
image-20211117115234232.png

2.3.2 Ref API

ref接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value

也就是说因为ref API 内部返回的是一个ref对象我们需要解包使用,template中vue会帮助我们浅层解包(仅一层),所以在其他地方调用时 我们需要通过 xxx.value的形式调用。

另外ref API对传入的数据类型无限制。

image-20211118101534181.png

2.3.3 readonly API

接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。与 reactive 一样,如果任何 property 使用了 ref,当它通过代理访问时,则被自动解包:

作用:当我们需要给其他组件传递数据,又不希望它们修改属性时,可以使用readonly。
image-20211118105324326.png

2.4 reactive 相关API

详情查看官网

2.4.1 isProxy

​ 检查对象是否是由 reactivereadonly 创建的 proxy。

2.4.2 isReactive

​ 检查对象是否是由 reactive创建的响应式代理。如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;

import { reactive, isReactive, readonly } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'John'
    })
    // 从普通对象创建的只读 proxy
    const plain = readonly({
      name: 'Mary'
    })
    console.log(isReactive(plain)) // -> false

    // 从响应式 proxy 创建的只读 proxy
    const stateCopy = readonly(state)
    console.log(isReactive(stateCopy)) // -> true
  }
}
2.4.3 isReadonly

检查对象是否是由 readonly 创建的只读代理。

2.4.4 toRaw

返回 reactivereadonly 代理的原始对象。这是一个“逃生舱”,可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。建议保留对原始对象的持久引用。请谨慎使用。

const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
2.4.5 shallowReactive

创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。

const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 改变 state 本身的性质是响应式的
state.foo++
// ...但是不转换嵌套对象
isReactive(state.nested) // false
state.nested.bar++ // 非响应式
2.4.6 shallowReadonly

创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。

import {  shallowReadonly } from "vue";
export default {
const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 改变 state 本身的 property 将失败
state.foo++
// ...但适用于嵌套对象
isReadonly(state.nested) // false
state.nested.bar++ // 适用
}
2.4.7 toRefs

在setup中,如果我们使用ES6的解构语法,对reactive返回的对象进行解构获取值,那么之后无论是修改结构后的变量,还是修改reactive 返回的state对象,数据都不再是响应式的。

const state = reactive({
	 foo: 1,
     age:20
})
const {foo,age} =state

console.log(isReactive(foo)) //false

所以官方推出toRefs和toRef来解决该问题:

image-20211119112519906.png

2.4.8 toRef

toRef和toRefs功能相同。作用场景不同:toRefs是给每一个属性添加ref,而toRef是给某一个属性添加ref。

image-20211119113419199.png

2.5 ref 相关API

2.5.1 unref

作用场景:当我们需要去获取一个ref引用中的value,就可以使用unref方法。如果参数是一个ref,则返回内部.value值 ,否则返回参数本身

//其实就是下列式子的语法糖
val = isRef(val) ? val.value : val
2.5.2 isRef

作用:判断值是否是一个ref对象。

2.5.3 shallowRef

创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。

const foo = shallowRef({})
// 改变 ref 的值是响应式的
foo.value = {}
// 但是这个值不会被转换。
isReactive(foo.value) // false

image-20211119143554367.png

2.5.4 triggerRef

手动执行与 shallowRef 关联的任何作用 (effect),换句话说就是手动解决shallowRef的副作用。

image-20211119144238322.png

2.5.5 customRef

创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 tracktrigger 函数作为参数,并且应该返回一个带有 getset 的对象。

场景:对双向绑定的数据进行节流或者防抖操作等,如搜索功能 输入的过程不可能一直搜索,应该是多久没操作的情况下才调用搜索提示。

后期项目会贡献代码。

3. computed

composition API中的computed的写法与之前不同:

写法一:接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。

image-20211121210630590.png
写法二:接受一个具有 getset 函数的对象,用来创建可写的 ref 对象。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

4. 数据的监听

在Composition API中,我们可以使用watchEffect和watch来完成响应式数据的侦听。

watchEffect:用于自动收集响应式数据的依赖。

watch:需要手动指定侦听的数据源

4.1 watchEffect

首先watchEffevct传入的函数会被立即执行一次,并且在执行的过程中收集依赖,当收集的依赖发生变化时,watchEffect传入的函数才会再次执行。

image-20211122100938485.png

 setup() {
    const count = ref(0);
    // 立即执行一次
    watchEffect(() => console.log(count.value));
    // -> logs 0

    setTimeout(() => {
      count.value++;
      // -> logs 1
    }, 1000);
    return {};
  },

停止侦听

​ 当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。举个例子:当我们监听点击次数,当超过20次时 ,手动停止侦听。

//watchEffect 有返回值
const stop = watchEffect(() => {
  /* ... */
})

// later 停止侦听只需调用返回值
stop()

watchEffect清除副作用

有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (即完成之前状态已改变了) 。所以侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时
  • 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)

例如:我们需求当数据修改时重新请求接口,但是如果频繁修改数据时,我们需要使用清除副作用来清除上一次请求。

watchEffect(onInvalidate => {
  const token = performAsyncOperation(id.value)
  //副作用函数通常时一个异步函数
  //当副作用即将重新执行 或者 侦听器被停止 时会执行该函数传入的回调函数
  onInvalidate(() => {
    // id has changed or watcher is stopped.
    // invalidate previously pending async operation
    token.cancel()
  })
})

4.2 watchEffect的刷新时机

如下情况:组件会在DOM挂载前更新一次,当DOM挂载后,watchEffect侦听到数据的改变,从而更新第二次。

image-20211122150549138.png
解决方法:

​ 1.watchEffect参数二 {flush:“post”}:

image-20211122151259819.png

image-20211122151338521.png
2.watchPostEffect

image-20211122151533776.png

4.3 watch的使用

watch的API 和vue2中基本一致,用于侦听特定的数据源。

与watchEffect的不同:

  1. 懒执行副作用(第一次不会直接执行)
  2. 更具体的说明当哪些状态发生变化时,触发侦听器的执行;
  3. 能侦听状态变化前后的值

4.4 watch侦听单个数据源

侦听器数据源可以是一个具有返回值的 getter 函数,也可以直接是一个 ref

// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

4.5 watch侦听多个源

侦听器还可以使用数组以同时侦听多个源:

image-20211122162025614.png

4.6 watch 侦听响应式复杂类型

有时候我们要侦听一个响应式数组或者对象,那么可以使用一个getter函数,并且对可响应对象进行解构。

image-20211122163958254.png

5. 生命周期

image-20211123165641024.png

//setup中一个生命周期可以使用多次,方便之后hook的抽取
	setup(){
        onMounted(()=>{
            xxxxx
        }),
        onMounted(()=>{
            xxxxx
        }),
    }

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐