美文网首页VUE
Vue3 Composition API初探

Vue3 Composition API初探

作者: Betterthanyougo | 来源:发表于2020-05-29 22:56 被阅读0次

    Composition API

    Composition API字面意思是组合API,它是为了实现基于函数的逻辑复用机制而产生的。

    基本使用

    <script>
        const {
          createApp,
          reactive
    } = Vue;
    // 声明组件 const App = {
    // setup是一个新的组件选项,它是组件内使用Composition API的入口 // 调用时刻是初始化属性确定后,beforeCreate之前
    setup() {
    // 响应化:接收一个对象,返回一个响应式的代理对象 const state = reactive({ count: 0 })
    // 返回对象将和渲染函数上下文合并
            return { state }
          }
    }
        createApp(App).mount('#app')
      </script>
    

    计算属性

    const { computed } = Vue;
    const App = {
        setup() {
            const state = reactive({
                count: 0,
    // computed()返回一个不可变的响应式引用对象
    // 它封装了getter的返回值
    doubleCount: computed(() => state.count * 2)
    
    }) }
    }
    
    

    事件处理

    <div @click="add">count: {{ state.count }}</div>
    const App = {
        setup() {
    // setup中声明一个add函数 function add() {
                state.count++
            }
    // 传入渲染函数上下文
            return { state, add }
        }
    }
    

    侦听器

    const { watch } = Vue;
    const App = {
        setup() {
    // state.count变化cb会执行
    // 常用方式还有watch(()=>state.count, cb) 
    watch(() => {
    console.log('count变了:' + state.count); })
    } }
    

    引用对象:单个原始值响应化

    <div>counter: {{ counter }}</div>
    const { ref } = Vue;
    const App = {
    setup() {
    // 返回响应式的Ref对象
     const counter = ref(1) setTimeout(() => {
    // 要修改对象的value
    counter.value++
    }, 1000);
    // 添加counter
    return { state, add, counter }
    } }
    

    体验逻辑组合

    const { createApp, reactive, onMounted, onUnmounted, toRefs } = Vue;
    // 鼠标位置侦听 function useMouse() {
    // 数据响应化
    const state = reactive({ x: 0, y: 0 })
        const update = e => {
            state.x = e.pageX
            state.y = e.pageY
        }
        onMounted(() => {
            window.addEventListener('mousemove', update)
        })
        onUnmounted(() => {
            window.removeEventListener('mousemove', update)
        })
    // 转换所有key为响应式数据
        return toRefs(state)
    }
    // 事件监测
    function useTime() {
        const state = reactive({ time: new Date() })
        onMounted(() => {
            setInterval(() => {
                state.time = new Date()
    }, 1000) })
        return toRefs(state)
    }
    // 逻辑组合
    const MyComp = {
        template: `
            <div>x: {{ x }} y: {{ y }}</div>
            <p>time: {{time}}</p>
    `, setup() {
    // 使用鼠标逻辑
    const { x, y } = useMouse() // 使用时间逻辑
    const { time } = useTime() // 返回使用
    return { x, y, time }
    } }
    createApp().mount(MyComp, '#app')
    

    Vue3响应式原理剖析

    vue3使用ES6的Proxy特性来解决这些问题。

    function reactive(obj) {
      if (typeof obj !== 'object' && obj != null) {
    return obj }
    // Proxy相当于在对象外层加拦截
    // http://es6.ruanyifeng.com/#docs/proxy const observed = new Proxy(obj, {
    get(target, key, receiver) {
    // Reflect用于执行对象默认操作,更规范、更友好
    // Proxy和Object的方法Reflect都有对应
    // http://es6.ruanyifeng.com/#docs/reflect const res = Reflect.get(target, key, receiver) console.log(`获取${key}:${res}`)
    return res
        },
        set(target, key, value, receiver) {
    const res = Reflect.set(target, key, value, receiver) console.log(`设置${key}:${value}`)
    return res
        },
        deleteProperty(target, key) {
    const res = Reflect.deleteProperty(target, key) console.log(`删除${key}:${res}`)
    return res
    } })
      return observed
    }
    

    测试代码

    const state = reactive({
        foo: 'foo',
    bar: { a: 1 } })
    // 1.获取
    state.foo // ok
    // 2.设置已存在属性
    state.foo = 'fooooooo' // ok // 3.设置不存在属性
    state.dong = 'dong' // ok // 4.删除属性
    delete state.dong // ok
    

    嵌套对象响应式
    测试:嵌套对象不能响应

    // 4.设置嵌套对象属性 
    react.bar.a = 10 // no ok
    

    添加对象类型递归

    const isObject = val => val !== null && typeof val === 'object'
    function reactive(obj) { //判断是否对象
    if (!isObject(obj)) {
    return obj }
      const observed = new Proxy(obj, {
        get(target, key, receiver) {
    // ...
    // 如果是对象需要递归
    return isObject(res) ? reactive(res) : res
    },
    //...
    }
    

    避免重复代理

    const toProxy = new WeakMap() // 形如obj:observed const toRaw = new WeakMap() // 形如observed:obj
    function reactive(obj) {
      //...
    // 查找缓存,避免重复代理 if (toProxy.has(obj)) {
        return toProxy.get(obj)
      }
      if (toRaw.has(obj)) {
        return obj
      }
      const observed = new Proxy(...)
    // 缓存代理结果 toProxy.set(obj, observed) toRaw.set(observed, obj) return observed
    }
    // 测试效果
    console.log(reactive(data) === state) console.log(reactive(state) === state)
    

    依赖收集

    建立响应数据key和更新函数之间的对应关系。
    用法

    // 设置响应函数
    effect(() => console.log(state.foo))
    // 用户修改关联数据会触发响应函数 state.foo = 'xxx'
    

    设计

    实现三个函数:
    effect:将回调函数保存起来备用,立即执行一次回调函数触发它里面一些响应数据的getter track:getter中调用track,把前面存储的回调函数和当前target,key之间建立映射关系 trigger:setter中调用trigger,把target,key对应的响应函数都执行一遍


    image.png

    实现

    设置响应函数,创建effect函数

    const effectStack = []
    // effect任务:执行fn并将其入栈 function effect(fn) {
    const rxEffect = function() { 
    // 1.捕获可能的异常
    try {
    
    
    // 2.入栈,用于后续依赖收集
     effectStack.push(rxEffect) 
    // 3.运行fn,触发依赖收集
     return fn()
    } finally {
    // 4.执行结束,出栈 effectStack.pop()
    } }
    // 默认执行一次响应函数 rxEffect()
    // 返回响应函数
    return rxEffect
    }
    

    依赖收集和触发

    function reactive(obj) {
      // ...
      const observed = new Proxy(obj, {
        get(target, key, receiver) {
    // ...
    // 依赖收集
    track(target, key)
    return isObject(res) ? reactive(res) : res
        },
        set(target, key, value, receiver) {
    // ...
    // 触发响应函数 trigger(target, key) return res
    } })
    }
    // 映射关系表,结构大致如下:
    // {target: {key: [fn1,fn2]}} let targetMap = new WeakMap() function track(target, key) {
    // 从栈中取出响应函数
    const effect = effectStack[effectStack.length - 1] if (effect) {
    // 获取target对应依赖表
    let depsMap = targetMap.get(target) if (!depsMap) {
          depsMap = new Map()
          targetMap.set(target, depsMap)
        }
    // 获取key对应的响应函数集
    let deps = depsMap.get(key) if (!deps) {
          deps = new Set()
          depsMap.set(key, deps)
        }
    // 将响应函数加入到对应集合 
            
    if (!deps.has(effect)) {
          deps.add(effect)
    } }
    }
    // 触发target.key对应响应函数
     function trigger(target, key) {
    // 获取依赖表
    const depsMap = targetMap.get(target) if (depsMap) {
    // 获取响应函数集合
    const deps = depsMap.get(key) if (deps) {
    // 执行所有响应函数
     deps.forEach(effect => {
    effect() })
    } }
    }
    

    相关文章

      网友评论

        本文标题:Vue3 Composition API初探

        本文链接:https://www.haomeiwen.com/subject/lirqzhtx.html