MST

星途 面试题库

面试题:自定义Vue响应式数据结构的实现

假设你需要在Vue项目中使用一种特殊的数据结构(如自定义的树状结构),并且要让它也具备Vue响应式系统的特性,你会如何设计并实现?请从数据劫持、依赖收集、更新触发等方面详细阐述。
44.7万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试
  1. 数据劫持
    • 在Vue中,可以使用Object.defineProperty()方法来实现数据劫持。对于自定义的树状结构,假设树状结构的节点定义如下:
    function TreeNode(value) {
        this.value = value;
        this.children = [];
    }
    
    • 对树状结构的每个节点的属性进行数据劫持:
    function reactiveTreeNode(node) {
        Object.defineProperty(node, 'value', {
            enumerable: true,
            configurable: true,
            get() {
                // 这里可以进行依赖收集相关操作,稍后介绍
                return node._value;
            },
            set(newValue) {
                if (newValue!== node._value) {
                    node._value = newValue;
                    // 这里触发更新,稍后详细介绍
                    triggerUpdate(node);
                }
            }
        });
        Object.defineProperty(node, 'children', {
            enumerable: true,
            configurable: true,
            get() {
                // 依赖收集
                return node._children;
            },
            set(newChildren) {
                if (newChildren!== node._children) {
                    node._children = newChildren;
                    // 触发更新
                    triggerUpdate(node);
                }
            }
        });
        // 递归劫持子节点
        node.children.forEach(child => reactiveTreeNode(child));
        return node;
    }
    
  2. 依赖收集
    • 首先定义一个Dep类来管理依赖:
    class Dep {
        constructor() {
            this.subscribers = new Set();
        }
        depend() {
            if (activeEffect) {
                this.subscribers.add(activeEffect);
            }
        }
        notify() {
            this.subscribers.forEach(effect => effect());
        }
    }
    
    • 在数据劫持的get方法中进行依赖收集:
    let activeEffect;
    function effect(callback) {
        activeEffect = callback;
        callback();
        activeEffect = null;
    }
    function reactiveTreeNode(node) {
        const dep = new Dep();
        Object.defineProperty(node, 'value', {
            enumerable: true,
            configurable: true,
            get() {
                dep.depend();
                return node._value;
            },
            set(newValue) {
                if (newValue!== node._value) {
                    node._value = newValue;
                    dep.notify();
                }
            }
        });
        Object.defineProperty(node, 'children', {
            enumerable: true,
            configurable: true,
            get() {
                dep.depend();
                return node._children;
            },
            set(newChildren) {
                if (newChildren!== node._children) {
                    node._children = newChildren;
                    dep.notify();
                }
            }
        });
        node.children.forEach(child => reactiveTreeNode(child));
        return node;
    }
    
  3. 更新触发
    • 在数据劫持的set方法中,当数据发生变化时,通过Dep类的notify方法通知所有依赖的effect函数重新执行。
    function reactiveTreeNode(node) {
        const dep = new Dep();
        Object.defineProperty(node, 'value', {
            enumerable: true,
            configurable: true,
            get() {
                dep.depend();
                return node._value;
            },
            set(newValue) {
                if (newValue!== node._value) {
                    node._value = newValue;
                    dep.notify();
                }
            }
        });
        Object.defineProperty(node, 'children', {
            enumerable: true,
            configurable: true,
            get() {
                dep.depend();
                return node._children;
            },
            set(newChildren) {
                if (newChildren!== node._children) {
                    node._children = newChildren;
                    dep.notify();
                }
            }
        });
        node.children.forEach(child => reactiveTreeNode(child));
        return node;
    }
    
    • 在Vue组件中使用时,可以通过effect函数包裹需要响应式更新的逻辑:
    <template>
        <div>
            <span>{{ treeNode.value }}</span>
        </div>
    </template>
    
    <script>
    import { reactiveTreeNode, effect } from './reactive.js';
    export default {
        data() {
            return {
                treeNode: reactiveTreeNode(new TreeNode('初始值'))
            };
        },
        mounted() {
            effect(() => {
                // 这里的逻辑会在treeNode.value或treeNode.children变化时重新执行
                this.$forceUpdate();
            });
        }
    };
    </script>
    

这样就实现了自定义树状结构在Vue响应式系统中的特性,通过数据劫持、依赖收集和更新触发来确保数据变化时视图能够正确更新。