Vue面试题结合源码
- 请说一下
vue
响应式数据的理解Vue
中如何检测数组变化?Vue
中如何进行依赖收集?- 如何理解
Vue
中的模板编译原理Vue
生命周期钩子是如何实现的Vue
的生命周期方法有哪些?一般在哪一步发送请求及其原因Vue.mixin
的使用场景和原理Vue
组件data为什么必须是个函数?nextTick
在哪里使用?使用原理?computed
和watch
的区别Vue.set
方法是如何实现的Vue
为什么需要虚拟domVue
中diff
算法原理- 既然
Vue
通过数据劫持可以精准探测数据变化,为什么还需要虚拟dom进行diff
检测差异- 请说明
Vue
中key的作用和其原理,谈谈你对它的理解- 谈谈对
Vue
组件化的理解
请说一下 vue
响应式数据的理解
可以监控一个数据的修改和获取操作。针对对象格式会给每个对象的属性进行劫持。其使用了Object.difineProperty方法。
关于vue的响应式原理我是有书写过的。内部就是在递归的劫持引用类型的属性,在属性也是引用类型的情况下会再次拦截。
回答该问题:
- 需要先找到基本的问题在哪
- 源码层面回答
- 使用的时候可能会伴随什么问题(就是踩坑)
源码层面:
- 先走 InitData方法
- observe
- defineReactive方法(内部对所有属性进行了重写,肯定是有性能消耗的)
- 在3中递归的观测引用类型的数据,给对象增加getter和setter
我们在使用vue的时候,如果data数据层级过深,需要考虑优化。比如:
-
数据不是响应式的,没必要放到data中
-
属性取值的时候,尽量避免多次取值
for(let i = 0; i < 100; i++){ this.count++ } // 优化 只会取值一次 let count = this.count for(let i = 0; i < 100; i++){ count++ }
-
如果有些对象是放到data中的,但是不需要是响应式的,可以考虑采用Object.freeze()冻结对象。冻结的对象Vue是不会进行观测的(一般这种对象都是会在模板中使用的数据)
源码分析:
new Vue的时候,来到了核心方法 _init。在该方法的 initState 方法用来初始化所有的状态信息。
初始化data
来到initData,该方法就是拿到data数据,然后进行判断,最后观测数据。
function initData (vm: Component) {
let data = vm.$options.data
// 顺便把 data也放在 _data属性上
data = vm._data = typeof data === 'function'
? getData(data, vm) // data.call(vm)
: data || {}
// 不是计划的对象 data不是对象 或者data函数返回值不是对象
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
// props中有该属性
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) { // 属性不要以 _ 和 $ 开头
// 代理 vm._data
// 访问 vm.name 代理到 vm._data.name
proxy(vm, `_data`, key)
}
}
// observe data 观测数据 标记整数根数据 new Vue
observe(data, true /* asRootData */)
}
在observe方法中,就是创建Observer观测者
在这里就给引用类型创建观察者对象,如果该对象被观测过,直接返回观察过的observer对象.
观测的对象可能是数组,也可能是对象。
如果是一个对象(不是数组对象),就把对象的每个属性变成响应式的数据。那么此时就会来到定义响应式数据的核心方法:defineReactive
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
// 把对象的的每个 key 都变成响应式的
defineReactive(obj, keys[i])
}
}
defineReactive方法,Vue是给我们导出了的,在Vue.utils下,有该工具函数。
虽然vue内部是基本是没有使用过shallow参数进行引用类型数据的浅层观测的,但是把方法暴露给我们用户了,我们使用的时候是可以浅层观测对象的。
可以自己尝试debugger看起响应式的过程。