【导读】本文总结了前端面试中经常问到的vue高频面试题,仅供参考。
beforeCreate是newVue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。
created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。
beforeMount发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
mounted在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
Vue3.x改用Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?
(很简单啊)
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理,这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?
我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。
MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。
使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
v-model本质就是一个语法糖,可以看成是value+input方法的语法糖。可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性。
简单来说,diff算法有以下过程
在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。(实际的实现可以结合Vue3.x源码看。)
该算法中还运用了动态规划的思想求解最长递归子序列。
父子组件通信
父->子props,子->父$on、$emit
获取父子组件实例$parent、$children
Ref获取实例的方式调用组件的属性或者方法
Provide、inject官方不推荐使用,但是写组件库时很常用
兄弟组件通信
EventBus`实现跨组件通信`Vue.prototype.$bus=newVueVuex跨级组件通信
Vuex$attrs、$listeners9.Vue的路由实现,hash路由和history路由实现原理说一下`location.hash`的值实际就是URL中`#`后面的东西。history实际采用了HTML5中提供的API来实现,主要有history.pushState()和history.replaceState()。
当条件不成立时,v-if不会渲染DOM元素,v-show操作的是样式(display),切换当前DOM的显示和隐藏。
keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
常用的两个属性include/exclude,允许组件有条件的进行缓存。
两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态。
keep-alive的中还运用了LRU(LeastRecentlyUsed)算法。
在下次DOM更新循环结束之后执行延迟回调。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。
SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端。
SSR有着更好的SEO、并且首屏加载速度更快等优点。不过它也有一些缺点,比如我们的开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境。还有就是服务器会有更大的负载需求。
一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。
当组件实例触发生命周期函数beforeCreate后,它会做一系列事情,其中就包括对computed的处理。
它会遍历computed配置中的所有属性,为每一个属性创建一个Watcher对象,并传入一个函数,该函数的本质其实就是computed配置中的getter,这样一来,getter运行过程中就会收集依赖
但是和渲染函数不同,为计算属性创建的Watcher不会立即执行,因为要考虑到该计算属性是否会被渲染函数使用,如果没有使用,就不会得到执行。因此,在创建Watcher的时候,它使用了lazy配置,lazy配置可以让Watcher不会立即执行。
收到lazy的影响,Watcher内部会保存两个关键属性来实现缓存,一个是value,一个是dirty
value属性用于保存Watcher运行的结果,受lazy的影响,该值在最开始是undefined
dirty属性用于指示当前的value是否已经过时了,即是否为脏值,受lazy的影响,该值在最开始是true
Watcher创建好后,vue会使用代理模式,将计算属性挂载到组件实例中
当读取计算属性时,vue检查其对应的Watcher是否是脏值,如果是,则运行函数,计算依赖,并得到对应的值,保存在Watcher的value中,然后设置dirty为false,然后返回。
如果dirty为false,则直接返回watcher的value
巧妙的是,在依赖收集时,被依赖的数据不仅会收集到计算属性的Watcher,还会收集到组件的Watcher
当计算属性的依赖变化时,会先触发计算属性的Watcher执行,此时,它只需设置dirty为true即可,不做任何处理。
由于依赖同时会收集到组件的Watcher,因此组件会重新渲染,而重新渲染时又读取到了计算属性,由于计算属性目前已为dirty,因此会重新运行getter进行运算
而对于计算属性的setter,则极其简单,当设置计算属性时,直接运行setter即可。
在使用vue的时候,我们有两种方式来创建我们的HTML页面,第一种情况,也是大多情况下,我们会使用模板template的方式,因为这更易读易懂也是官方推荐的方法;第二种情况是使用render函数来生成HTML,它比template更接近最终结果。
complier的主要作用是解析模板,生成渲染模板的render,而render的作用主要是为了生成VNode
complier主要分为3大块:
React和Vue有许多相似之处,它们都有:
建议面试前通读一遍该篇文档,然后进行适当的总结。
区别:
watch的参数:
computed缓存原理:
conputed本质是一个惰性的观察者;当计算数据存在于data或者props里时会被警告;
vue初次运行会对computed属性做初始化处理(initComputed),初始化的时候会对每一个computed属性用watcher包装起来,这里面会生成一个dirty属性值为true;然后执行defineComputed函数来计算,计算之后会将dirty值变为false,这里会根据dirty值来判断是否需要重新计算;如果属性依赖的数据发生变化,computed的watcher会把dirty变为true,这样就会重新计算computed属性的值。
在vue中修饰符可以分为3类:
事件修饰符
在事件处理程序中调用event.preventDefault或event.stopPropagation方法是非常常见的需求。尽管可以在methods中轻松实现这点,但更好的方式是:methods只有纯粹的数据逻辑,而不是去处理DOM事件细节。
为了解决这个问题,vue为v-on提供了事件修饰符。通过由点.表示的指令后缀来调用修饰符。
常见的事件修饰符如下:
按键修饰符
除了事件修饰符以外,在vue中还提供了有鼠标修饰符,键值修饰符,系统修饰符等功能。
表单修饰符
vue同样也为表单控件也提供了修饰符,常见的有.lazy、.number和.trim。
编码阶段
SEO优化
打包优化
用户体验
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
优化首屏加载可以从这几个方面开始:
key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
其实不只是vue,react中在执行列表渲染时也会要求给每个组件添加上key这个属性。
要解释key的作用,不得不先介绍一下虚拟DOM的Diff算法了。
我们知道,vue和react都实现了一套虚拟DOM,使我们可以不直接操作DOM元素,只操作数据便可以重新渲染页面。而隐藏在背后的原理便是其高效的Diff算法。
vue和react的虚拟DOM的Diff算法大致相同,其核心有以下两点:
基于以上这两点,使得虚拟DOM的Diff算法的复杂度从O(n^3)降到了O(n)。
当页面的数据发生变化时,Diff算法只会比较同一层级的节点:
当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程默认情况下也是遵循以上原则。
比如一下这个情况:
我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:
即把C更新成F,D更新成C,E更新成D,最后再插入E
是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
ref的作用是被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的$refs对象上。其特点是:
所以常见的使用场景有:
接口请求可以放在钩子函数created、beforeMount、mounted中进行调用,因为在这三个钩子函数中,data已经创建,可以将服务端端返回的数据进行赋值。
但是推荐在created钩子函数中调用异步请求,因为在created钩子函数中调用异步请求有以下优点:
另外作者也在找工作,欢迎公司有HC的同学内推,base地:深圳、广州或长沙。