组合式API

优点:

关键词:复用、独立封装
同样功能代码集中,可复用;
数据响应系统独立封装使用方便,数据流转清晰,代码可读性强;

使用示例:

<script type="module">
    import { createApp, reactive, onMounted, onUnmounted, toRefs } from './node_modules/vue/dist/vue.esm-browser.js'
    const app = createApp({  // createApp({}):创建APP实例
        // data() {} 必为函数,不接受对象
        setup () {
        // setup() ,composition api 的入口,在beforeCreated\created之间实现,所以相关数据处理均可放置于setup中;
        // setup的参数:(props, context = {attrs, emit, slots})
        // const position = useMousePosition()
        const { x, y } = useMousePosition()
            return { // 返回对象在组件任意位置可以使用
                x,
                y
            }
        }
    })
    console.log(app)

    const position = reactive({ // reactive({}):创建响应式对象
        x: 0,
        y: 0
    })

    app.mount('#app')
</script>

生命周期钩子函数

选项 API 生命周期选项和组合式 API 之间的映射

beforeCreate -> use setup()

created -> use setup()

beforeMount -> onBeforeMount

mounted -> onMounted

beforeUpdate -> onBeforeUpdate

updated -> onUpdated

beforeUnmount -> onBeforeUnmount

unmounted -> onUnmounted(destroyed => unmounted)

errorCaptured -> onErrorCaptured

renderTracked -> onRenderTracked (render函数调用触发调用,包含首次触发)

renderTriggered -> onRenderTriggered (render函数调用触发调用,不包含首次触发)

三个响应式函数简述:reactive-ref-toRefs

关键词:收集依赖track、触发依赖trigger、effect定义依赖、发布-订阅者模式;

思想:

同vue2自定义事件一样是“发布-订阅者模式”(watch为“观察者模式”),定义全局变量targetMap( new WeakMap() )作为调度中心,结构为:

WeakMap {
	target: Map{
		key: Set [effect1(), ......, effectn()]
	}
}

targetMap:new WeakMap(), key目标对象,value -> depsMap;弱引用,失去引用后可以销毁
depsMap:new Map(), key目标对象的属性名称,value -> dep;
dep:new Set(), value -> effect函数

reactive

对target进行代理,属性的下一层在获取get时再代理处理;

  • 接收一个参数,判断是否为对象
  • 创建proxy对象handler,设置get、set、deleteProperty;get时调用track收集依赖,set时调用trigger触发依赖;
  • 返回proxy对象

ref

可把基本类型数据转为响应式对象;const count = ref(0) // 返回一个对象,{value: 0}

  • 参数为基本类型值的话,内部会创建一个只有value属性的对象,改对象value属性具有getter(收集依赖)和setter(触发更新)参数为对象,内部调用reactive;
ref() // undefined
ref([]) // 空数组
  • 参数为对象,内部调用reactive;
ref和reactive对比:
reactiveref
参数对象其他基本类型
返回响应式对象(不可解构){value: ‘xx’}的响应式对象,操作数据’.value’
重新赋值对象丢失响应式保持响应式

toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的ref。代理对象被解构的时候,相当于定义对应key值变量来接收对象的key值;而基本类型赋值,就是在内存中再复制一份,所以此处被解构的x\y是两个基本变量,与代理对象无关

  • 内部创建一个新的对象
  • 遍历传入的代理对象的所有属性,值都转为响应式对象;
  • 挂载到新的对象上,然后返回新的对象

简单实现:

effect定义时触发track收集依赖;

const isObject = val => typeof val === 'object' && val !== null
const convert = val => isObject(val) ? reactive(val): val
const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (target, key) => hasOwnProperty.call(target, key)
let activeEffect = null
let targetWeakMap = new WeakMap()

/*
    1、判断是否是对象,不是的话直接返回
    2、创建一个handler对象,返回一个proxy对象new Proxy(target, handler)
*/
export function reactive (target) {  
    if(!isObject(target)) return target
    const handler = {
        get: function(target, key, receiver){
            // 1、收集依赖;
            // 2、返回Reflect映射后的数据,某属性可能是对象,对其进行下一层处理,即获取普通value或reactive后的value;
            track(target, key)
            const result = Reflect.get(target, key, receiver)
            return convert(result)
        },
        set: function(target, key, value, receiver){
            // 1、value对比是否改变,无改变直接返回,改变进行下一步
            // 2、调整value,通知依赖
            const oldV = Reflect.get(target, key, receiver); // getter this指向receiver
            if(oldV === value) return true;
            const res = Reflect.set(target, key, value, receiver)
            trigger(target, key)
            return res
        },
        deleteProperty: function(target, key){
            const hadKey = hasOwn(target, key)
            const result = Reflect.deleteProperty(target, key) // true:属性存在删除成功 + 属性不存在;false:属性unconfigurable,如Object.freeze({foo: 1})处理过的属性
            if (hadKey && result) {
              // 触发更新
              trigger(target, key)
            }
            return result
        }
    }

    return new Proxy(target, handler)
}

/*
    考虑到ref操作value不是很便利,常再次封装处理一层value
    1、ref过的响应式对象ret
    2、target是对象 -> reactive;其他,返回一个 对象 r,操作对象的value值
*/
export function ref(raw){
    if(isObject(raw) && raw.__is_ref) return
    let value = convert(raw) // 如果是对象,reactive
    const r = {
        __is_ref: true,
        get value() {
            track(r, 'value') // track 的 target 为 r,返回的对象;
            return value;
        },
        set value(newV) {
            if(newV === value) return true;
            raw = newV;
            value = convert(raw) // 对raw赋值,然后重新转换raw,得到的值再复制给value
            trigger(r, 'value')
            return true;
        }
    }
    return r;
}

/*
实现一个reactive之后的proxy(数组、obj)的每个属性都变成响应式,借由ref(任何类型)
params: proxy
return: 属性处理过响应式的obj
*/
export function toRefs(proxy){
    const ret = proxy instanceof Array ? new Array(proxy.length): {}

    for(let key in proxy){
        ret[key] = toProxyRef(proxy, key)
    }

    return ret
}

export function toProxyRef(proxy, key){
    const r = {
        __is_ref: true,
        get value() {
            return proxy[key];
        },
        set value(newV) {
            proxy[key] = newV
            return true;
        }
    }
    return r;
}

export function computed(getter){
    const result = ref()
    effect(() => result.value = getter())
    return result
}

// effect生效之后,删除属性,再赋值属性,effect依旧有效;即deleteProperty并不会删除依赖
export function effect(callback) {
    activeEffect = callback
    callback() // 执行回调,会访问get响应式对象属性,track
    activeEffect = null
}

export function track(target, key){
    /*
    1、没有依赖可收集,直接return;
    2、收集依赖 weakMap - Map - set;无对应key创建对应下一级变量类型并赋值,有则到set收集
    */
    if(!activeEffect) return
    let depsMap = targetWeakMap.get(target)
    if(!depsMap) targetWeakMap.set(target, (depsMap = new Map()))
    let deps = depsMap.get(key)
    if(!deps) depsMap.set(key, (deps = new Set()))
    deps.add(activeEffect)
}

export function trigger(target, key){
    /*
    1、查找target\key的依赖是否存在,存在则循环触发,不存在则return
    */
    let depsMap = targetWeakMap.get(target)
    if(!depsMap) return
    let deps = depsMap.get(key)
    if(!deps) return
    deps.forEach(effect => {
        effect()
    });
}
Proxy、Reflect使用
  1. Reflect,反射,用来获取或设置对象中的成员;如果Reflect和Object有相同方法,建议使用Reflect;
  2. Proxy第二个参数对象中set、deleteProperty中需返回布尔类型的值,严格模式下,如果不返回值,默认返回undefined即false会出现Type Error异常;
  3. Proxy和Reflect的receiver参数
    Proxy中的receiver:Proxy或者继承Proxy的对象
    Reflect中的receiver:如果target对象中设置了getter,getter中的this指向receiver
    vue3中receiver都会传递,为保证this获取值正确;

vue3其他响应式组件:

computed

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。
简化模板的代码,缓存计算的结果,当数据变化再重新计算;

const activeCount = computed(() => {
    return todos.filter(item => !item.completed).length
})
console.log('activeCount.value ---> ', activeCount.value);  // activeCount.value --->  2
watch

监听器:监听响应式数据变化,执行回调
参数:(data, handler, {deep, immediate})
返回值:取消监听的函数

第一个参数调整:侦听器数据源可以是一个具有返回值的 getter 函数,也可以直接是一个 ref;数组包含多个源

watchEffect

watch的简化版本,也用来监视数据的变化
接收一个函数作为参数,监听函数内响应式数据的变化;无第二个回调函数参数;
返回值:取消监听的函数

Logo

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

更多推荐