vue核心面试题--从源码说

1-为什么要在列表中绑定key,有什么作用

    组件在更新阶段会对两次vnode做比较,以确定走patchVnode还是create-updateParentPlaceholder-destory过程

vue核心面试题--从源码说_第1张图片

    节点对比的条件之一就是key值是否等价

vue核心面试题--从源码说_第2张图片

    这意味着,在不带key值的情况下(即两次均为undefined),vue有一定概率(如list列表)会将两次vnode判断为sameVnode走patchVnode复用dom节点

    那么,假设当前的需求是选项卡形式,每一个选项卡下都有一个list列表,我们在tab1下的列表1通过点击绑定了class使其高亮,那么当选项卡切换到tab2后,列表1仍然是高亮状态

2-聊聊vue的双向数据绑定

    所谓双向绑定,即数据驱动视图更新,同时视图更新又能change到数据变化

    数据——>视图

    vue的组件创建会经过init过程,这将执行initstate函数对data和props做一次处理,它将遍历data的key执行observe,这是一次递归defineReactive的过程。即对每一个key通过Object.defineProperties设置拦截,在get时向dep收集watcher,在set时通过dep notify到watcher执行render update

    视图——数据

    这表现在v-model指令中,我们在input、textarea中输入的文本能驱动数据的更新。这是因为vue在模板编译阶段将v-model解析成events+props挂载到ast节点上,在patch过程中将会调用updateListeners通过addeventListeners向input添加input事件,当input触发时将调用事件更新props对应的值,以达到视图更新的目的

3-聊聊vuex和redux的异同点

    两者都是响应式编程的状态管理方案,都很好的解决了嵌套组件的数据传递问题,也都是在全局定义store,通过dispatch向reducer派发action以更新store。相比较redux来说,vuex提供了直接连接reducer的方案,即commit

4-在vue中,子组件为何不可以修改父组件传递的props,如果修改了,vue是如何监控到并报警告的?

    组件在init过程中会执行initstate函数,这包括了对props的处理

vue核心面试题--从源码说_第3张图片

        可以看到,vue通过重写Object.properties.set方法,当尝试对props修改时发出警告。这么做是为了保证数据的单项流,避免出错

5-为什么vue中的mutations中不能有异步操作

    因为异步操作是有副作用的,它的固定输入不会有固定产出,这将使得程序状态变得不可预测

6-vue2.x中的Object.definedProperty为什么会在vue3中被Proxy替代

    Object.definedProperty只能劫持对象的属性,而对于某一个属性的嵌套则无法劫持,但是Proxy可以,另外就是Proxy提供的可选择api更丰富

7-Vue组件生命周期

    一个组件的创建,要执行init-mount-reder-patch流程。在init过程中,首先通过callhook调用beforeCreate,接着才是initState,这意味着beforeCreate时组件的状态是不可用的,但是可以向vuex或vue-router那样通过Vue.mixin混入一些插件能力,因为此时的组件尚是"干净的",避免了对组件造成可能性的干扰;同样的,由于created是在initState之后,故此时的状态是可用的,因此我们可以在此向后台发起请求获取数据并绑定给data.key;在mount过程中将首先执行beforeMount,此时的render函数以及被编译但尚未执行,故无法进行dom操作;在new Watcher的过程中将会执行到render和update,走patch流程,故mounted时,dom已被创建,因此可以通过ref指令添加dom引用;当我们通过点击事件触发data更新时,将会由dep通知到watcher执行run走render update,此时将会执行beforeUpdate,由于此时的dom还未更新,因此我们可以copy一份旧的dom以尝试做一次回退操作;最后当组件卸载时将会触发beforeDestory和destoryed,这将递归销毁组件。由于vue的模板解析只会对template中的事件进行处理,因此我们通过ref操作dom添加的事件需要手动进行销毁

    最后是与keep-alive相关的activited和deactivited,标识组件的激活和冻结状态

8-vue在v-for时给每个元素绑定事件需要使用事件代理吗

    不需要!

    vue对dom的事件监听是通过dom.addEventListener实现的,对组件的事件处理是通过事件总线完成的

9-vue是如何对数组做响应式处理的?为什么通过下标的方式不行(假设数组为[1,2,3])

    vue会遍历data的每一个key调用observe,当为数组时,实例化observe

vue核心面试题--从源码说_第4张图片

  即调用

vue核心面试题--从源码说_第5张图片

    可以看到这会进入新一轮的observe

vue核心面试题--从源码说_第6张图片

    对于我们的数组[1,2,3,4]而言,每一个key是基本类型,因此vue直接return,并未对数组的每一个key做响应式处理

    那么数组的响应式又是如何做到的呢,在observe实例化过程中,针对数组调用了protoAugment或copyAugment方法,这将我们数组的原型链指向了arrayMethods对象,该对象通过methodsToPatch进行了重写

vue核心面试题--从源码说_第7张图片
vue核心面试题--从源码说_第8张图片

    因此,当我们对数组执行push/splice操作时能触发更新

10-为什么data应该是一个函数

    在init过程中有对data做处理,当为函数时会执行getData通过call语法将this指向当前实例,它将返回一个独一无二的引用类型

    当组件中存在子组件时,在render过程中将会通过Vue.extend构建子组件构造器,在这一过程中将会对我们的配置项进行合并,如果data不是一个函数,vue会提示警告

vue核心面试题--从源码说_第9张图片

    这是因为,当组件被作为公共组件使用时,对象形式的data会被共享,这意味着,在a处的修改会影响b处的展示

11-nextTick的实现

    当组件更新时会通过dep.notify到watcher的update,这将watcher push进队列

vue核心面试题--从源码说_第10张图片

    并手动调用nextTick,传入flushSchedulerQueue

vue核心面试题--从源码说_第11张图片

    而这实际上是利用了异步api,将我们的回调队列延后到主线之后执行

vue核心面试题--从源码说_第12张图片

12-computed是如何实现的

    computed watcher会对依赖的变化做判断,只有依赖变化时才会重新计算。我们知道当data中的数据被修改时会触发watcher的update,这将会把dirty置为true

vue核心面试题--从源码说_第13张图片

    那么当在render过程中访问到computed.key时将会触发computedGetter,这将触发computed重新求值

vue核心面试题--从源码说_第14张图片

    并在计算后重新置为false



本人能力有限,如有错误,欢迎指正哟~~

你可能感兴趣的:(vue核心面试题--从源码说)