Vue组合式 api 的常用知识点

Vue组合式 api 的常用知识点

看很多公司还是需要vue3优先的,最近把vue3的组合式api学习了一下并整理的笔记,大家可以看看自己有多少掌握的。

setup 函数

会在组件的生命周期函数之前执行

没有this,所以在 setup 中是不能拿到组件的其他的信息。但是在setup 外(methods,差值表达式)可以拿到setup 中的数据

可以return 一个对象作为data,和原来的data 方法一样。并且两者可以共存,在组件作用能同时能拿到两个对象里面的数据

两个data和methods 中有与setup 中的重名,那么会优先使用是setup函数中的变量

接收两个参数,props和context,且两个参数的内容都是只读的

steup (props, context) {
    return {
        props,
        context
    }
}

响应式数据API

ref

用来声明一些响应式数据,可以是any 类型的响应式数据,访问和更改数据需要.value

const count = ref(10)
const refObj = ref({
  id: 2,
  key: 1
})

console.log(count.value); // 10 
console.log(refObj); // RefImpl
console.log(refObj.value);  // Proxy {id: 2, key: 1}
console.log(refObj.value.id);  // 2

count.value ++ // 11

在模板中可以直接使用,不需要使用.value

<p>{{ refObj.id }}</p>
<p>{{ refObj.key }}</p>

总结: 声明响应式的数据,返回值不展开,需要.value,深层次递归嵌套的属性,可能导致性能问题

customRef()

创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。ustomRef() 预期接收一个工厂函数作为参数,这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象。

ustomRef() 预期接收一个工厂函数作为参数,这个工厂函数接受 tracktrigger 两个函数作为参数,并返回一个带有 getset 方法的对象。

import { customRef } from 'vue'

export function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}
reactive

用来声明一些响应式对象数据,复杂类型,如果用来声明基本类型,将会警告,因为返回的值是展开的,而基本类型是无法展开

const reactiveNumber = reactive(2)  // value cannot be made reactive: 2

// 部分源码
if (!isObject(target)) {
if ((process.env.NODE_ENV !== 'production')) {
  console.warn(`value cannot be made reactive: ${String(target)}`);
}
return target;
}

深层次的监听数据的变化,所有嵌套的属性改变都会触发视图的更新(响应式)。基于ES6的proxys实现,返回一个proxy 对象。返回的对象和原来的对象是不相等的。

const student = {
id: 1,
name: '阿木'
}
const people  =reactive(student)
student ===  people // false

reactive 声明的对象会自动展开解构,因此不需要使用.value去访问和更改他们的值

people.id //   1
people.name //  '阿木'

注意当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包:

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value

接受一个源对象,并返回proxy对象,更改proxy对象,那么源对象也会改变

// 修改proxy对象 people
people.name = '忘喧'
// 源对象也会改变
console.log(student); // 忘喧

**总结:**声明响应式的数据,返回值展开,不需要.value,深层次递归,会嵌套的属性,可能导致性能问题,返回的proxy 对象是对源对象的引用,浅复制。reactive 是对ref 的解包。

shallowRef + triggerRef

ref类似,只有跟属性才是响应式,不会主动展开,需要用到.value去访问和更新值。

当去改变嵌套的属性时,数据会改变但是视图没有更新

const teacher  = {
  name: '小毅',
  teach: {
    teach: {
      id: 666,
      hobiy: "swimming"
    }
  }
}
//  声明一个只监听跟属性改变是响应式数据
let shallowRefParam = shallowRef(teacher)

// 改变嵌套的属性,数据改变,但是视图没有更新
function changeShallowRefData() {
  shallowRefParam.value.teacher.teach = {
    id: 5555,
    hobiy: "running"
  }
}

嵌套属性发生改变的时候,triggerRef(shallowRefObj)可以使用手动更新视图

function changeShallowRefUI() {
  triggerRef(shallowRefParam)
}

shallowRef 只是浅复制对象,在改变数据的时候,原来的对象也会发生改变

console.log( teacher.teach.teach.hobiy) // running

shallowRef 是特殊的 ref ,多了一个 ShallowRefMarker来标识是否进行深层次的监听

export declare type ShallowRef<T = any> = Ref<T> & {
    [ShallowRefMarker]?: true;
};
shallowReactive

和 shallowRef类似,只有跟属性才会响应式,不会主动展开,需要用到.value去访问和更新值。嵌套属性更改不会触发视图的更新。

const user = {
  id: 1,
  name: '阿木'
}
const shallowReactiveParam = shallowReactive(user);
shallowReactiveParam.value.name // 阿木

只是对源对象浅复制,更新的时候和影响源对象

**注意:**没有 triggerReactive

toRef

基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然

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

const fooRef = toRef(state, 'foo')

// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2

// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3
toRefs

将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef创建的.

toRefs 在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用 toRef

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // ...基于状态的操作逻辑

  // 在返回时都转为 ref
  return toRefs(state)
}

// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()
toRaw()

toRaw() 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象。

const foo = {}
const reactiveFoo = reactive(foo)

console.log(toRaw(reactiveFoo) === foo) // true
markRaw

将一个对象标记为不可被转为代理。返回该对象本身。

isRef

有时候会不知道是不是需要.value去访问一个ref的值,可以使用这个判断是不是ref

if(isRef(refData)){
	return	refData.value
}else {
		refData
}
unref

如果参数是 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 计算的一个语法糖。

function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x)
  // unwrapped 现在保证为 number 类型
}
readonly

接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。

只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。

要避免深层级的转换行为,请使用 shallowReadonly 作替代。

shallowReadonly

和 readonly() 不同,这里没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。

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

// 更改状态自身的属性会失败
state.foo++

// ...但可以更改下层嵌套对象
isReadonly(state.nested) // false

// 这是可以通过的
state.nested.bar++
isProxy

检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

isReactive

检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

isReadonly

检查一个对象是否是由 readonly() 或 shallowReadonly() 创建的代理。

isShallow

检查一个对象是不是由 shallowRef或shallowReactive,shallowReadonly创建的代理

const aa = shallowReactive({ a: 'aa' })
const bb = shallowRef('bb')
const cc = shallowReadonly({ cc: 'cc' })

console.log(isShallow(aa)); // true
console.log(isShallow(bb)); // true
console.log(isShallow(cc));  // true
computed

j模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护,可以computed

// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})

计算属性有缓存,计算属性值会基于其响应式依赖被缓存

可以写的计算属性

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // 注意:我们这里使用的是解构赋值语法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
watchEffect

监听依赖项发生改变,就执行副作用

const count = ref(0)

const stop = watchEffect(() => console.log(count.value))
// -> 输出 0

count.value++
// -> 输出 1

// 当不再需要此侦听器时:
stop()

第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。

interface WatchEffectOptions {
  flush?: 'pre'(侦听器将在组件渲染之前执行) | 'post' (将会使侦听器延迟到组件渲染之后再执行)| 'sync'(在响应式依赖发生改变时立即触发侦听器) // 默认:'pre'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}
watchPostEffect()

watchEffect() 使用 flush: ‘post’ 选项时的别名

watchSyncEffect()

watchSyncEffect()

watch()

第一个参数是侦听器的。这个来源可以是以下几种:

  • 一个函数,返回一个值
  • 一个 ref
  • 一个响应式对象
  • …或是由以上类型的值组成的数组
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
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))
})

// 处理掉当前作用域内的所有 effect
scope.stop()
getCurrentScope()

如果有的话,返回当前活跃的 effect 作用域。

当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。

生命周期

onBeforeMount()

注册一个钩子,在组件被挂载之前被调用。

onMounted()

注册一个回调函数,在组件挂载完成后执行。

组件在以下情况下被视为已挂载:

  • 其所有同步子组件都已经被挂载 (不包含异步组件或 `` 树内的组件)。
  • 其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时,才可以保证组件 DOM 树也在文档中。
onBeforeUpdate()

注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在这个钩子中更改状态也是安全的。

onUpdated()

注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。

父组件的更新钩子将在其子组件的更新钩子之后调用

// 性能问题

这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的,如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。

onUpdated(() => {
  // 文本内容应该与当前的 `count.value` 一致
  console.log(document.getElementById('count').textContent)
})
</script>

不要这个这个钩子中更改状态,会死循环

onBeforeUnmount()

注册一个钩子,在组件实例被卸载之前调用。

当这个钩子被调用时,组件实例依然还保有全部的功能。

onUnmounted

注册一个回调函数,在组件实例被卸载之后调用。

一个组件在以下情况下被视为已卸载:

  • 其所有子组件都已经被卸载。
  • 所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止
onErrorCaptured

注册一个钩子,在捕获了后代组件传递的错误时调用

onRenderTracked

注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。

这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用

onRenderTriggered

注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。

这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用

onActivated

注册一个回调函数,若组件实例是 `` 缓存树的一部分,当组件被插入到 DOM 中时调用。

onDeactivated

注册一个回调函数,若组件实例是 `` 缓存树的一部分,当组件从 DOM 中被移除时调用。

onServerPrefetch

注册一个异步函数,在组件实例在服务器上被渲染之前调用

果这个钩子返回了一个 Promise,服务端渲染会在渲染该组件前等待该 Promise 完成。

这个钩子仅会在服务端渲染中执行,可以用于执行一些仅存在于服务端的数据抓取过程。

组件通信传递

provide

提供一个值,可以被后代组件注入

provide() 必须在组件的 setup() 阶段同步调用

// 提供静态值
provide('foo', 'bar')

// 提供响应式的值
const count = ref(0)
provide('count', count)
inject

注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值

// 注入值的默认方式
const foo = inject('foo')

// 注入响应式的值
const count = inject('count')

// 通过 Symbol 类型的 key 注入
const foo2 = inject(fooSymbol)

// 注入一个值,若为空则使用提供的默认值
const bar = inject('foo', 'default value')

// 注入一个值,若为空则使用提供的工厂函数
const baz = inject('foo', () => new Map())

// 注入时为了表明提供的默认值是个函数,需要传入第三个参数
const fn = inject('function', () => {}, false)
defineProps

定义接受的父组件的props

//父组件调用子组件
<ChildrenCom :count="count"  @chage="changeCount" />

// 子组件
// 接受父组件传入的props
const props = defineProps(['name', 'count'])

// 使用,可以直接使用
<h2>{{ count }}</h2>

// props只读,不能直接在自组件中修改props 
function changeCount() {
    props.count = 20  // Set operation on key "count" failed: target is readonly
}

// 可以使emits 修改props的值
defineEmits

接受组件组件的传入的函数

// 父组件调用children
<ChildrenCom :count="count"  @chage="changeCount"   />
 
// 子组件使用defineEmits
const emits = defineEmits(['chage'])

// 触发父组件传入的修改count的事件
function emitsChangeCount() {
  emits('chage')
}

// 也可以传入参数
function emitsChangeCount(value) {
  emits('chage',value)
}
useAttrs

接受没有在props中声明的方法或者属性

//父组件调用子组件
<ChildrenCom :count="count"  @chage="changeCount"  age="1" :chageClick="changeCount" />

// 子组件
// 接受父组件传入的props
const props = defineProps(['name', 'count'])

const attrs = useAttrs() 
console.log('attrs', attrs);  // chageClick  age

attrs 只读

defineExpose

使用 的组件是**默认关闭**的——即通过模板引用或者 `$parent` 链获取到的组件的公开实例,**不会**暴露任何在 中声明的绑定。

// 子组件
const name = ref('忘喧')
const age = ref(10)
const changeAge = () => {
  age.value = age.value + 1
}
defineExpose({
  name,
  age
})

// 父组件
const childInstance = ref()

function getChildInstance() {
  console.log(childInstance.value.name) // 忘喧
}

<ChildrenCom ref="childInstance" />
inheritAttrs

认情况下,父组件传递的,但没有被子组件解析为 props 的 attributes 绑定会被“透传”。这意味着当我们有一个单根节点的子组件时,这些绑定会被作为一个常规的 HTML attribute 应用在子组件的根节点元素上。当你编写的组件想要在一个目标元素或其他组件外面包一层时,可能并不期望这样的行为。我们可以通过设置 inheritAttrsfalse 来禁用这个默认行为。这些 attributes 可以通过 $attrs 这个实例属性来访问,

并且可以通过 v-bind 来显式绑定在一个非根节点的元素上。

在一个组件的

透传 attribute

useSlots
const slots = useSlots()

参考:https://cn.vuejs.org/api/composition-api-setup.html#setup-context

你可能感兴趣的:(JavaScript,vue.js,javascript,前端,前端框架)