面试题答案
一键面试Vue中v - model指令双向数据绑定底层实现机制
- 数据劫持
- Vue利用Object.defineProperty()方法来实现数据劫持。通过这个方法,可以定义对象属性的getter和setter函数。
- 例如,假设有一个数据对象
data
:
const data = { message: 'Hello' }; Object.defineProperty(data, 'message', { get() { console.log('获取message的值'); return this._message; }, set(newValue) { console.log('设置message的值为', newValue); this._message = newValue; } });
- 在Vue中,当组件实例创建时,会遍历data对象的所有属性,并使用Object.defineProperty()将它们转换为“响应式”数据。当数据被访问(getter被调用)时,可以进行依赖收集(将当前正在使用该数据的地方记录下来);当数据被修改(setter被调用)时,就可以通知依赖该数据的地方进行更新。
- 发布订阅模式
- 依赖收集:在数据劫持的getter中,会进行依赖收集。每个数据属性都有一个Dep(依赖)对象,它负责收集依赖该数据的Watcher(订阅者)。当数据的getter被触发时,Dep会将当前正在使用该数据的Watcher添加到自己的订阅者列表中。
- 通知更新:当数据的setter被触发时,Dep会遍历自己的订阅者列表,通知所有Watcher进行更新。Watcher接收到通知后,会执行相应的更新操作,例如重新渲染组件视图。
- 例如,有一个Watcher监听某个数据属性:
class Watcher { constructor(vm, expOrFn, cb) { this.vm = vm; this.cb = cb; // 这里假设expOrFn是一个获取数据的函数 this.getter = parsePath(expOrFn); this.value = this.get(); } get() { Dep.target = this; const value = this.getter.call(this.vm, this.vm); Dep.target = null; return value; } update() { const oldValue = this.value; this.value = this.get(); this.cb.call(this.vm, this.value, oldValue); } } function parsePath(path) { if (/[.$]/.test(path)) { const segments = path.split(/[.$]/); return function (obj) { for (let i = 0; i < segments.length; i++) { if (!obj) return; obj = obj[segments[i]]; } return obj; }; } return function (obj) { return obj && obj[path]; }; }
- 这里Watcher在
get
方法中会触发数据的getter,从而进行依赖收集,当数据更新时,update
方法会被调用进行视图更新。
在不同表单元素上的具体实现差异
- input元素
- 文本输入框(type = "text"):v - model绑定文本输入框的值时,会在input元素的
input
事件中获取最新的输入值,并通过数据劫持的setter更新数据。同时,在组件初始化时,会根据数据的初始值设置input元素的value
属性。 - 示例代码:
<template> <input type="text" v - model="message"> </template> <script> export default { data() { return { message: '' }; } }; </script>
- 当用户输入文本时,
input
事件触发,新值通过数据劫持更新message
,同时message
的变化又会通过数据绑定更新输入框的value
属性。
- 文本输入框(type = "text"):v - model绑定文本输入框的值时,会在input元素的
- select元素
- v - model绑定select元素的值时,会根据数据的初始值设置select元素的
value
属性。当select元素的change
事件触发时,会获取选中项的值,并通过数据劫持更新数据。 - 示例代码:
<template> <select v - model="selectedValue"> <option value="option1">选项1</option> <option value="option2">选项2</option> </select> </template> <script> export default { data() { return { selectedValue: 'option1' }; } }; </script>
- 当用户选择不同的选项时,
change
事件触发,新的选中值通过数据劫持更新selectedValue
,同时selectedValue
的变化又会通过数据绑定更新select元素的value
属性,确保选中项的一致性。
- v - model绑定select元素的值时,会根据数据的初始值设置select元素的