最近也是在学习一些Vue的源码知识,然后将这些常见的面试题做一下记录和分享
然后我也会每天更新一些面试题,如果有回答的不对的地方欢迎大家指出,共同进步
这些面试题的回答我也只是抛砖引玉;
因为有些题的回答如果做非常相信的回答单独写一篇文章都是足够的,所以我只是将我的理解记的知识点记录下来;给大家分享一下,并且提供一个目标和方向;
1. 响应式数据的理解
- 核心点是考察对象和数组的值发生变化如何劫持到;
- 对象的数据劫持主要是通过Object.definepropety给对象中的每一项属性进行观测(只会观测已经存在的属性);通过observer()方法来递归对象中的每一项
- 数组的数据劫持在原型上采用切片编程的思想重写了可以改变原数组的7个方法;当用户在调用这些方法的时候会触发数据劫持;
- 数组和对象每一个属性和对象都有一个dep来记录watcher。当数据取值的时候会把对应的dep记录对应的渲染watcher,当数据从新设置值的时候,会触发渲染watcher来更新视图
2. Vue如何检测数组的变化
- 循环options.data中的每一项,判断如果是数组??梢匀檬橥ü?strong>proto找到自己从新定义的原型上;
- 重写了可以改变原数组的7个方法(push,shift,pop,unshift,sort,splice,reverse);在用户调用这7个方法的时候可以通过原型链走到我们定义的7个方法中。然后检测数组的变化;
- 如果是push、unshift、splice的话则需要对新增的值继续进行观测如果是对象或者数组则递归观测
- 因为性能原因所以数组没有通过definePropoty来进行每一项的观测,所以如果直接改变数组的length或者通过索引来修改数组中的值是无法动态响应的;所以需要$set来实现;
3. Vue中模板编译原理?
- 首先先去判断options中用户是否传入了render函数,如果有就直接用用户传入的render函数,如果没有在看有没有传入template,如果render和template都没有,则获取$el下的outerHTML来渲染ast树;
- 静态节点标记,优化不需要改变的标签
- 把获取到的html字符串通过正则方法来逐步截取解析生成render函数;(这一步vue源码里有大量的判断和字符串拼接);
- 通过render函数来生成DOM结构然后插入到父节点中;
- 模板引擎的实现原理就是new Function + with来进行实现的
4. 生命周期钩子是如何实现的?
- 生命周期钩子实质是回调函数,通过发布订阅模式在初始化的时候把全局定义的钩子函数和实例上的钩子函数订阅好。
- 在创建组件实例的过程中通过callHook方法来调用不同的钩子函数;
- 钩子函数采用数组的方式存储。created: [created1, created2, created3...],然后循环执行;
5. Vue.mixin的使用场景和原理?
- 原理:Vue.mixin的作用就是抽离公共的业务逻辑,原理类似“对象的继承”;
- 当组件初始化的时候,通过mergerOptions把全局的一些方法和组件的方法混合到一起;这样在子级中可以使用全局或者父级的方法或属性;
- 采用策略模式针对不同的属性进行合并;如果混入的数据和本身发生冲突,采用就近原则,先采用组件本身的属性;
- 场景: 用来混合全局的方法或者Vue中内部的钩子函数;
- data, watch, computed, 钩子函数等;
6. nextTick在哪使用,原理是?
- 原理:当批量更改数据或者DOM结构之后,为了不频繁渲染页面(我理解类似文档碎片),采用批处理;
- nextTick中的回调是在下次 DOM 更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
- 通过异步方法来获取更新后的DOM结构;先判断当前浏览器是否支持promise。如果不支持promise 就是用MutationObserver();如果不支持就用setImmediate;如果都不支持的话,就用setTimeout;
- 使用:在手动更新了数据或者操作DOM之后想要立即获取更新后的DOM结构或者让视图立即更新显示最新状态
7. Vue为什么需要虚拟DOM?
- 虚拟DOM本质也是通过JS来描述真实DOM,是对真实DOM的抽象;
- 通过虚拟DOM的比对从差异(DOM diff)然后更新出真实DOM渲染可以减少对真实DOM的操作。
- 虚拟DOM不依赖真实平台环境从而也可以实现跨平台。
- 因为操作虚拟DOM的代价要比操作真实DOM的代价要小。虚拟DOM只是对象,通过修改对象然后更新真实的DOM结构可以节省性能;
8. Vue中diff的原理?
- vue的diff是同级比较,不考虑跨级比较的情况,内部采用深度递归+双指针的方式
- 先比较是否是相同节点。如果不同直接替换;
- 节点相同比较属性。
- 比较文本,替换文本;
- 比较子元素,判断子元素的在新老元素的有无,如果都有则去diff算法去比较新节点和老节点的子元素;
- 子节点的比较采用了双指针的方法,在用新虚拟DOM和老虚拟DOM进行比对的时候先采用了头头比,尾尾比,头尾比,尾头比;
- 并且在对比之前还判断是tag标签和key是否相同;
- 如果key不同的情况下就插入或者直接更新;
9. 既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?
- 响应式数据变化,Vue确实可以在数据发生变化时,响应式系统可以立刻得知。但是如果给每个属性都添加watcher用于更新的话,会产生大量的watcher从而降低性能。而且粒度过细也会导致更新不精准的问题,所以vue采用了组件级的watcher配合diff来检测差异。
10. Vue 中 computed 和 watch 的区别?
- 相同点: 他俩都是通过watcher来更新渲染,也就是基于Watcher来实现的;
- 不同点:首先computed是可以缓存数据的,内部通过dirty来检测所依赖的值是否发生了变化,如果没有发生变化则不会更细;watch是不可以缓存数据的,会在立即执行,将老值保存下来,然后当监听的值再次更新的时候调用对应的回调函数返回新值和老值;
- watch就是一个事件监听,当监听的数据变化的时候就会触发watcher,然后更新。并且watch监听的数据在渲染Watcher执行的会先执行一次,拿到数据。然后监听变化的时候还能拿到新的数据,然后返回新老数据;
- computed内部有是通过Object.definePropety监听里面依赖的数据,只有当里面的数据变化之后才会触发渲染Watecher, 并且有一个dirty属性来标注是否缓存