• web前端面试高频考点——Vue3.x(Composition API的逻辑复用、Proxy实现响应式)


    系列文章目录



    一、Composition API 如何实现逻辑复用

    • 抽离逻辑代码到一个函数
    • 函数命名约定为 useXxx 格式(React Hooks 也是)
    • 在 setup 中引用 useXxx 函数

    useMousePosition.js 文件

    • 鼠标移动事件,显示鼠标的位置
    • 写在 js 文件中,可供逻辑复用
    import { ref, onMounted, onUnmounted} from 'vue'
    
    function useMousePosition() {
        // 初始化坐标
        const x = ref(0)
        const y = ref(0)
    
        // 更新坐标
        function update(e) {
            x.value = e.pageX
            y.value = e.pageY
        }
    
        // 挂载:添加鼠标移动事件
        onMounted(() => {
            console.log('useMousePosition mounted');
            window.addEventListener('mousemove', update)
        })
    
        // 销毁:删除鼠标移动事件
        onUnmounted(() => {
            console.log('useMousePosition unMounted');
            window.removeEventListener('mousemove', update)
        })
    
        return {
            x,
            y
        }
    }
    
    export default useMousePosition
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    App.vue 父组件

    • 点击按钮,进行组件的创建 / 销毁
    <template>
      <MousePosition v-if="flag" />
      <button @click="changeFlagHandler">change flag</button>
    </template>
    
    <script>
    import MousePosition from "./components/index.vue";
    export default {
      data() {
        return {
          flag: true,
        };
      },
      methods: {
        // 实现组件的创建/销毁
        changeFlagHandler() {
          this.flag = !this.flag;
        },
      },
      components: { MousePosition },
    };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    index.vue 子组件

    • 解构出函数中定义的 x 和 y
    <template>
      <p>mouse position {{ x }} {{ y }}</p>
    </template>
    
    <script>
    import useMousePosition from "./useMousePosition";
    
    export default {
      name: "MousePosition",
      setup() {
        // 解构 x 和 y
        const { x, y } = useMousePosition();
    
        return {
          x,
          y,
        };
      },
    };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    CompositionAPI复用

    二、Vue3 如何实现响应式

    1、Object.defineProperty 的缺点

    • 深度监听需要一次性递归(层级很深的话会影响性能)
    • 无法监听新增属性 / 删除属性(Vue.set Vue.delete)
    • 无法原生监听数组,需要特殊处理

    2、Proxy 实现响应式

    • target:就是定义的对象 data
    • key:获取的键
    • val:获取的值
    • receiver:是 proxyData

    示例:对象通过 Proxy 实现响应式测试

    const data = {
        name: '杂货铺',
        age: 20
    }
    
    const proxyData = new Proxy(data, {
    	// 监听获取
        get(target, key, receiver) {
            const result = Reflect.get(target, key, receiver)
            console.log('get', key);
            return result // 返回结果
        },
        
        // 监听设置
        set(target, key, val, receiver) {
            const result = Reflect.set(target, key, val, receiver)
            console.log('set', key, val)
            console.log('result', result); // true
            return result // 是否设置成功
        },
        
        // 监听删除
        deleteProperty(target, key) {
            const result = Reflect.deleteProperty(target, key)
            console.log('delete property', key);
            console.log('result', result); // true
            return result // 是否删除成功
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述

    示例:数组通过 Proxy 实现响应式测试

    const data = ['a', 'b', 'c']
    
    const proxyData = new Proxy(data, {
        get(target, key, receiver) {
            // 只处理本身(非原型的)属性
            const ownKeys = Reflect.ownKeys(target) // 获取对象的键
            if (ownKeys.includes(key)) {
                console.log('get', key);  // 监听
            }
            const result = Reflect.get(target, key, receiver)
            console.log('get', key);
            return result // 返回结果
        },
        set(target, key, val, receiver) {
            // 重复的数据,不处理
            const oldVal = target[key]
            if(val === oldVal) {
                return true
            }
            const result = Reflect.set(target, key, val, receiver)
            console.log('set', key, val)
            console.log('result', result); // true
            return result // 是否设置成功
        },
        deleteProperty(target, key) {
            const result = Reflect.deleteProperty(target, key)
            console.log('delete property', key);
            console.log('result', result); // true
            return result // 是否删除成功
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述

    3、Reflect 作用

    • 和 Proxy 能力一一对应
    • 规范化、标准化、函数式
    • 替代掉 Object 上的工具函数
      在这里插入图片描述

    4、Proxy 实现响应式

    • 深度监听,性能更好(用到的时候再监听)
    • 可监听 新增 / 删除 属性
    • 可监听数组变化
    • Proxy 能规避 Object.defineProperty 的问题
    • Proxy 无法兼容所有浏览器,无法 polyfill(用于实现浏览器并不支持原生 API 的代码)

    示例:使用 Proxy 实现响应式

    • 深度监听不是一次性监听完,而是用到的时候才监听
    // 创建响应式
    function reactive(target = {}) {
        if (typeof target !== 'object' || target == null) {
            // 不是对象或数组
            return target
        }
    
        // 代理配置
        const proxyConf = {
            get(target, key, receiver) {
                // 只处理本身(非原型的)属性
                const ownKeys = Reflect.ownKeys(target)
                if (ownKeys.includes(key)) {
                    console.log('get', key); // 监听
                }
                const result = Reflect.get(target, key, receiver)
    
                // 深度监听
                // 性能如何提升的? 什么时候 get 到,什么时候去做响应式
                return reactive(result) // 返回结果
            },
            set(target, key, val, receiver) {
                // 重复的数据,不处理
                const oldVal = target[key]
                if (val === oldVal) {
                    return true
                }
    
                // 监听是已有的键还是新增的键
                const ownKeys = Reflect.ownKeys(target)
                if (ownKeys.includes(key)) {
                    console.log('已有的 key', key);
                } else {
                    console.log('新增的 key', key);
                }
    
                const result = Reflect.set(target, key, val, receiver)
                console.log('set', key, val)
                return result // 是否设置成功
            },
            deleteProperty(target, key) {
                const result = Reflect.deleteProperty(target, key)
                console.log('delete property', key);
                console.log('result', result); // tru            return result // 是否删除成功
            }
        }
    
        // 生成代理对象
        const observed = new Proxy(target, proxyConf)
        return observed
    }
    
    // 测试数据
    const data = {
        name: '杂货铺',
        age: 21,
        info: {
            city: 'beijing'
        }
    }
    
    const proxyData = reactive(data)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    不积跬步无以至千里 不积小流无以成江海

    点个关注不迷路,持续更新中…

  • 相关阅读:
    OPENCV进行图像修复
    访问者模式的一个使用案例——文档格式转换
    Python学习(7)--获取windows本地网卡和地址信息(2)
    js:Lodash一个JavaScript 实用工具库
    怎么在相册里去水印?三种方法教你去除
    Google Earth Engine(GEE)——GHSL:全球人类住区层,建成网格 1975-1990-2000-2015 (P2016) 数据集
    如何办理软件企业认定证书
    Eyeshot 2022.3 Fem Released Crack
    基于模糊PID控制器的水温控制系统仿真
    使用stm32cubemx实现GD32 DAC信号输出和ADC信号采集
  • 原文地址:https://blog.csdn.net/qq_45902692/article/details/126681278