vue的一些基础知识,以及相关实现原理,一直是面试中比较热门的题目,本文梳理了常见的Vue面试题。系列文章将在公众号「前端小苑」首发,可微信扫描文章底部二维码添加。javascript
注意:关于底层实现原理,建议最好仍是参照源码进行学习,有些原理相对比较复杂,很难经过一篇文章去深刻理解,最终可能只是只知其一;不知其二,面试时一深刻提问,就露馅了。
本文主要是对Vue可能的面试点进行梳理,平时仍是要注重知识的积累和储备,才能游刃有余的应对工做和面试,部分题目会给出相关连接供详细学习。css
父子组件通讯:html
props
和 event
、v-model
、 .sync
、 ref
、 $parent
和 $children
前端
非父子组件通讯:vue
$attr
和 $listeners
、 provide
和 inject
、eventbus
、经过根实例$root
访问、vuex
、dispatch
和 brodcast
java
通讯方式属于较基础的面试题,具体的可参考个人文章—— vue 组件通讯看这篇就够了node
Vue提倡单向数据流,即父级 props
的更新会流向子组件,可是反过来则不行。这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解。若是破坏了单向数据流,当应用复杂时,debug 的成本会很是高。git
v-model
是用来在表单控件或者组件上建立双向绑定的,他的本质是 v-bind
和 v-on
的语法糖,在一个组件上使用 v-model
,默认会为组件绑定名为 value
的 prop
和名为 input
的事件。
文章—— vue 组件通讯看这篇就够了 中也有其详细介绍github
Vuex和全局对象主要有两大区别:面试
Vuex中全部的状态更新的惟一途径都是mutation,异步操做经过 Action 来提交 mutation实现,这样使得咱们能够方便地跟踪每个状态的变化,从而让咱们可以实现一些工具帮助咱们更好地了解咱们的应用。
每一个mutation执行完成后都会对应到一个新的状态变动,这样devtools就能够打个快照存下来,而后就能够实现 time-travel 了。若是mutation支持异步操做,就没有办法知道状态是什么时候更新的,没法很好的进行状态的追踪,给调试带来困难。 参考尤大大回答: www.zhihu.com/question/48…
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
<keep-alive>
有本身独立的钩子函数 activated 和 deactivated。
渲染过程:
父组件挂载完成必定是等子组件都挂载完成后,才算是父组件挂载完,因此父组件的mounted在子组件mouted以后
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子组件更新过程:
父组件更新过程:
销毁过程:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
看起来不少好像很难记忆,其实只要理解了,不论是哪一种状况,都必定是父组件等待子组件完成后,才会执行本身对应完成的钩子,就能够很容易记住。
v-if
会在切换过程当中对条件块的事件监听器和子组件进行销毁和重建,若是初始条件是false,则什么都不作,直到条件第一次为true时才开始渲染模块。
v-show
只是基于css进行切换,无论初始条件是什么,都会渲染。
因此,v-if
切换的开销更大,而 v-show
初始化渲染开销更大,在须要频繁切换,或者切换的部分dom很复杂时,使用 v-show
更合适。渲染后不多切换的则使用 v-if
更合适。
computed
计算属性,是依赖其余属性的计算值,而且有缓存,只有当依赖的值变化时才会更新。
watch
是在监听的属性发生变化时,在回调中执行一些逻辑。
因此,computed
适合在模板渲染中,某个值是依赖了其余的响应式对象甚至是计算属性计算而来,而 watch
适合监听某个值的变化去完成一段复杂的业务逻辑。
计算属性是基于他们的响应式依赖进行缓存的,只有在依赖发生变化时,才会计算求值,而使用 methods
,每次都会执行相应的方法。
keep-alive
能够在组件切换时,保存其包裹的组件的状态,使其不被销毁,防止屡次渲染。
其拥有两个独立的生命周期钩子函数 actived 和 deactived,使用 keep-alive
包裹的组件在切换时不会被销毁,而是缓存到内存中并执行 deactived 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
在网站上动态渲染任意 HTML,很容易致使 XSS 攻击。因此只能在可信内容上使用 v-html,且永远不能用于用户提交的内容上。
这部分的面试题,只看答案部分是不够的,最好结合源码来分析,能够有更深的理解。我在以前的文章对某些源码作过度析的,会给出连接。
若是面试被问到这个问题,又描述不清楚,能够直接画出 Vue 官方文档的这个图,对着图来解释效果会更好。
Object.defineProperty
对数据进行劫持,并结合观察者模式实现。 Vue 利用
Object.defineProperty
建立一个
observe
来劫持监听全部的属性,把这些属性所有转为
getter
和
setter
。Vue 中每一个组件实例都会对应一个
watcher
实例,它会在组件渲染的过程当中把使用过的数据属性经过
getter
收集为依赖。以后当依赖项的
setter
触发时,会通知
watcher
,从而使它关联的组件从新渲染。
这道题目也能够问成 “为何vue3.0使用proxy实现响应式?” 其实都是对Object.defineProperty 和 proxy实现响应式的对比。
Object.defineProperty
只能劫持对象的属性,而 Proxy
是直接代理对象Object.defineProperty
只能对属性进行劫持,须要遍历对象的每一个属性。而 Proxy
能够直接代理对象。Object.defineProperty
对新增属性须要手动进行 Observe
, 因为 Object.defineProperty
劫持的是对象的属性,因此新增属性时,须要从新遍历对象,对其新增属性再使用 Object.defineProperty
进行劫持。 也正是由于这个缘由,使用 Vue 给 data
中的数组或对象新增属性时,须要使用 vm.$set
才能保证新增的属性也是响应式的。Proxy
支持13种拦截操做,这是 defineProperty
所不具备的。Proxy
做为新标准,长远来看,JS引擎会继续优化 Proxy
,但 getter
和 setter
基本不会再有针对性优化。Proxy
兼容性差 目前并无一个完整支持 Proxy
全部拦截方法的Polyfill方案更详细的对比,能够查看个人文章 为何Vue3.0再也不使用defineProperty实现数据监听?
Vue 的 Observer
对数组作了单独的处理,对数组的方法进行编译,并赋值给数组属性的 __proto__
属性上,由于原型链的机制,找到对应的方法就不会继续往上找了。编译方法中会对一些会增长索引的方法(push
,unshift
,splice
)进行手动 observe。 具体一样能够参考个人这篇文章 为何Vue3.0再也不使用defineProperty实现数据监听?,里面有详细的源码分析。
Vue 的组件都是可复用的,一个组件建立好后,能够在多个地方复用,而无论复用多少次,组件内的 data
都应该是相互隔离,互不影响的,因此组件每复用一次,data
就应该复用一次,每一处复用组件的 data
改变应该对其余复用组件的数据不影响。
为了实现这样的效果,data
就不能是单纯的对象,而是以一个函数返回值的形式,因此每一个组件实例能够维护独立的数据拷贝,不会相互影响。
能回答清楚这道问题的前提,是清楚 EventLoop 过程。
在下次 DOM 更新循环结束后执行延迟回调,在修改数据以后当即使用 nextTick
来获取更新后的 DOM。
nextTick
对于 micro task 的实现,会先检测是否支持 Promise
,不支持的话,直接指向 macro task,而 macro task 的实现,优先检测是否支持 setImmediate
(高版本IE和Etage支持),不支持的再去检测是否支持 MessageChannel,若是仍不支持,最终降级为 setTimeout
0;
默认的状况,会先以 micro task
方式执行,由于 micro task 能够在一次 tick 中所有执行完毕,在一些有重绘和动画的场景有更好的性能。
可是因为 micro task 优先级较高,在某些状况下,可能会在事件冒泡过程当中触发,致使一些问题(能够参考 Vue 这个 issue:github.com/vuejs/vue/i…),因此有些地方会强制使用 macro task (如 v-on
)。
注意:之因此将
nextTick
的回调函数放入到数组中一次性执行,而不是直接在nextTick
中执行回调函数,是为了保证在同一个tick内屡次执行了nextTcik
,不会开启多个异步任务,而是把这些异步任务都压成一个同步任务,在下一个tick内执行完毕。
vue模板的编译过程分为3个阶段:
此阶段会深度遍历生成的 AST 树,检测它的每一颗子树是否是静态节点,若是是静态节点则它们生成 DOM 永远不须要改变,这对运行时对模板的更新起到极大的优化做用。
const code = generate(ast, options)
复制代码
经过 generate
方法,将ast生成 render
函数。 更多关于 AST,Vue 模板编译原理,以及和 AST 相关的 Babel 工做原理等,我在 掌握了AST,不再怕被问babel,vue编译,Prettier等原理 中作了详细介绍。
清晰回答这道问题,须要先清楚 Vue 的 diff 过程,关于 diff 原理,推荐一篇文章 my.oschina.net/u/3060934/b…
key
是给每一个 vnode
指定的惟一 id
,在同级的 vnode
diff 过程当中,能够根据 key
快速的对比,来判断是否为相同节点,而且利用 key
的惟一性能够生成 map
来更快的获取相应的节点。
另外指定 key
后,就再也不采用“就地复用”策略了,能够保证渲染的准确性。
当 v-for
和 v-if
处于同一个节点时,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每一个 v-for
循环中。若是要遍历的数组很大,而真正要展现的数据不多时,这将形成很大的性能浪费。
这种场景建议使用 computed
,先对数据进行过滤。
全局前置/钩子:beforeEach、beforeResolve、afterEach
路由独享的守卫:beforeEnter
组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
完整的导航解析流程以下:
区别:
hash 模式:
#
后面 hash 值的变化,不会致使浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面。同时经过监听 hashchange 事件能够知道 hash 发生了哪些变化,而后根据 hash 变化来实现更新页面部份内容的操做。
history 模式:
history 模式的实现,主要是 HTML5 标准发布的两个 API,pushState
和 replaceState
,这两个 API 能够在改变 url,可是不会发送请求。这样就能够监听 url 变化来实现更新页面部份内容的操做。
关于 vue-router
部分,推荐文章 juejin.im/post/5b10b4…
本文是系列文章《2020年大厂面试指南》的第一篇文章,后续系列文章更新,请关注个人公众号【前端小苑】。有任何问题也欢迎加我我的微信yu_shihu_进行交流