面试题答案
一键面试- 数据劫持:
- 在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; }
- 在Vue中,可以使用
- 依赖收集:
- 首先定义一个
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; }
- 首先定义一个
- 更新触发:
- 在数据劫持的
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响应式系统中的特性,通过数据劫持、依赖收集和更新触发来确保数据变化时视图能够正确更新。