作为新特性 Composition API ,在 Vue3 正式发布之前一段时间就发布过了。
距文档介绍, Composition API 是一组低侵入式的、函数式的 API,使得我们能够更灵活地「组合」组件的逻辑。
不仅在 Vue 中,在其他的框架或原生 JS 也可以很好地被使用,下面我们就选取几个比较重要的 Composition API ,通过一些简单的例子来看看如何在其他项目中使用。
注:本文仅列出各个分类下比较重要的 API,想要查看全部可以点击下方链接进行查看:
https://github.com/vuejs/vue-next/tree/master/packages/reactivity
createReactiveObject 函数是 reactive API 的核心,用于创建 reactive 对象 。
在分享 API 之前,我们先看看其核心实现,由于篇幅有限,本文仅展示出理解后的简化版代码,代码如下:
function createReactiveObject( target, // 要监听目标 isReadonly, // 是否只读 baseHandlers, // target 为 Object 或 Array 时的处理器,支持对数据的增删改查 collectionHandlers // target 为 Map/WeakMap 或 Set/WeakSet 时的处理器,支持对数据的增删改查 ) { if (target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) { // 当 target 已经是一个 Proxy 时,直接返回 // 例外情况:在 Proxy 里调用 readonly() return target } // 当前对象已被监听过时,就直接返回被监听的对象 if (existingProxy) { return existingProxy } // 如果是 Object Array Map/WeakMap Set/WeakSet 以外只能监听到值的数据,直接返回 if (targetType === TargetType.INVALID) { return target } // 根据参数类型生成对应的 Proxy 对象,以及添加对应的处理器 const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy }接收一个普通对象然后返回该普通对象的响应式代理。等同于 2.x 的 Vue.observable()
示例如下:
import { reactive, isReactive // 判断是否是 reactive 对象 } from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js' const obj = { nested: { foo: 1 }, array: [{ bar: 2 }] } const value = 10 const observedObj = reactive(obj) const observedValue = reactive(value) console.log(isReactive(observedObj)) // true console.log(isReactive(observedValue)) // true只为某个对象的私有(第一层)属性创建浅层的响应式代理,不会对“属性的属性”做深层次、递归地响应式代理,而只是保留原样。
示例如下:
const obj = { nested: { foo: 1 }, array: [{ bar: 2 }] } const value = 10 const unobservedObj = shallowReactive(obj) const unobservedValue = shallowReactive(value) console.log(isReactive(observedObj)) // false console.log(isReactive(observedValue)) // falsecreateReactiveEffect 是 effect API 的核心,用于创建监听用户自定义的 reactive 对象的函数
在分享 API 之前,我们先看看其核心实现,由于篇幅有限,本文仅展示出理解后的简化版代码,代码如下:
function createReactiveEffect( fn, // 用户创建的 reactive 对象变动执行回调 options = { lazy, // 是否执行用户函数 scheduler, // 收集数据记录 onTrack, // 追踪用户数据的回调 onTrigger, // 追踪变更记录 onStop, // 停止执行 allowRecurse // 是否允许递归 } ) { const effect = function reactiveEffect() { if (!effectStack.includes(effect)) { cleanup(effect) // 清空 effect try { enableTracking() // 往追踪用户数据的栈内添加当前 effect effectStack.push(effect) // 往 effect 栈内添加 effect activeEffect = effect // 将活动 effect 变成当前 effect return fn() // 执行回调 } finally { // 删除当前记录 effectStack.pop() resetTracking() activeEffect = effectStack[effectStack.length - 1] } } } effect.id = uid++ effect._isEffect = true effect.active = true effect.raw = fn effect.deps = [] effect.options = options return effect }effect 函数是 effect API 的核心。以 WeakMap 为数据类型,是一个用于存储用户自定义函数的 订阅者。
示例如下:
let dummy const counter = reactive({ num: 0 }) effect(() => (dummy = counter.num)) console.log(dummy === 0) // true counter.num = 7 console.log(dummy === 7) // trueRefImpl 是 ref API 的核心,用于创建 ref 对象
在分享 API 之前,我们先看看其核心实现,代码如下:
class RefImpl { private _value // 存储当前 ref 对象的值 public __v_isRef = true // 确定是否为 ref 对象的变量 (只读) constructor( private _rawValue, // 用户传入的原始值 public readonly _shallow = false // 当前 ref 对象是否为 shallowRef ) { // convert:如果传入的原始值为对象,则会被 convert 函数转换为 reactive 对象 this._value = _shallow ? _rawValue : convert(_rawValue) } get value() { // 用于追踪用户输入的值变化 // track:effect api 的 track 函数,用于追踪用户行为,当前则是追踪用户的 get 操作 // toRaw:effect api 的 toRaw 函数,将 this 转化为用户输入值 track(toRaw(this), TrackOpTypes.GET, 'value') return this._value } set value(newVal) { if (hasChanged(toRaw(newVal), this._rawValue)) { // 当前 ref 对象有变化时 // _rawValue / _value 变更 // trigger:effect api 的 trigger 函数,根据用户传入的值与操作类型来进行操作,当前则为将用户传入的值添加到值 map 里 this._rawValue = newVal this._value = this._shallow ? newVal : convert(newVal) trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal) } } }接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性 .value。如果传入 ref 的是一个对象,将调用 reactive 方法进行深层响应转换。
示例如下:
const count = ref({ name: '鱼头', type: '帅哥' }) console.log(count.value.type) // 帅哥 count.value.type = '超级大帅哥' console.log(count.value.type) // 超级大帅哥创建一个 ref ,将会追踪它的 .value 更改操作,但是并不会对变更后的 .value 做响应式代理转换(即变更不会调用 reactive)
示例如下:
const __shallowRef = shallowRef({ a: 1 }) let dummy effect(() => { dummy = __shallowRef.value.a }) console.log(dummy) // 1 __shallowRef.value.a = 2 console.log(dummy) // 1 console.log(isReactive(__shallowRef.value)) // falsecustomRef 用于自定义一个 ref,可以显式地控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回一个带有 get 和 set 属性的对象。
示例如下:
let value = 1 let _trigger const custom = customRef((track, trigger) => ({ get() { track() return value }, set(newValue) { value = newValue _trigger = trigger } })) let dummy effect(() => { dummy = custom.value }) console.log(dummy) // 1 custom.value = 2 console.log(dummy) // 1 _trigger() console.log(dummy) // 2triggerRef 用于主动触发 shallowRef
示例如下:
const __shallowRef = shallowRef({ a: 1 }) let dummy effect(() => { dummy = __shallowRef.value.a }) console.log(dummy) // 1 __shallowRef.value.a = 2 console.log(dummy) // 1 console.log(isReactive(__shallowRef.value)) // false triggerRef(__shallowRef) console.log(dummy) // 2ComputedRefImpl 是 ref API 的核心,用于创建 computed 对象
在分享 API 之前,我们先看看其核心实现,由于篇幅有限,本文仅展示出理解后的简化版代码,代码如下:
class ComputedRefImpl { private _value // 当前值 private _dirty = true // 当前值是否发生过变更 public effect // effect 对象 public __v_isRef = true; // 指定为 ref 对象 public [ReactiveFlags.IS_READONLY]: boolean // 是否只读 constructor( getter, // getter private _setter, // setter isReadonly // 是否只读 ) { this.effect = effect(getter, { lazy: true, scheduler: () => { if (!this._dirty) { // 将变更状态变为 true // trigger:effect api 的 trigger 函数,根据用户传入的值与操作类型来进行操作,当前则为将用户传入的值添加到值 map 里 this._dirty = true trigger(toRaw(this), TriggerOpTypes.SET, 'value') } } }) this[ReactiveFlags.IS_READONLY] = isReadonly } get value() { if (this._dirty) { // 返回当前值 // 将变更状态变为 false this._value = this.effect() this._dirty = false } // track:effect api 的 track 函数,用于追踪用户行为,当前则是追踪用户的 get 操作 track(toRaw(this), TrackOpTypes.GET, 'value') return this._value } set value(newValue) { this._setter(newValue) } }传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。
示例如下:
const count1 = ref(1) const plus1 = computed(() => count1.value + 1) console.log(plus1.value) // 2 plus1.value++ // Write operation failed: computed value is readonly const count2 = ref(1) const plus2 = computed({ get: () => count2.value + 1, set: val => { count2.value = val - 1 } }) console.log(plus2.value) // 2 plus2.value = 0 console.log(plus2.value) // 0如果你喜欢探讨技术,或者对本文有任何的意见或建议,非常欢迎加鱼头微信好友一起探讨,当然,鱼头也非常希望能跟你一起聊生活,聊爱好,谈天说地。 鱼头的微信号是:krisChans95 也可以扫码添加好友,备注“csdn”就行(加好友送200M前端面试资料)