共计 14026 个字符,预计需要花费 36 分钟才能阅读完成。
又快到年底了,有很多小伙伴可能会抉择跳槽或者升职或者考核,尤其重要的是面试,在面试过程中面试官总会问一些基础性的内容来考查咱们的专业知识的把握水平,再决定是否录用。
前言
当初对于前端来说,问的最多的可能是 JavaScript
根底,除此之外问的更多的就是目前的支流框架相干的一些知识点。目前支流的框架 Vue、React、Angular
,国内的互联网状况来讲,目前还是Vue
居多。
就目前来看越来越多的面试,和之前的面试不太一样了,两三年前问的更多的是对于支流框架的利用,目前更多的是对于底层的一些问题更多一些。针对这些 Vue
相干面试题做一下总结并记录。心愿能够帮到更多的小伙伴。
根底面试题
根底面试题即 Vue
利用和应用方面的面试题,这一部分大多数还是面向于高级工程师,相对来说要简略一些。
问:Vue 生命周期钩子都有哪些?
答:Vue
生命周期一共有 10
个:
- beforeCreate – 在实例初始化之前调用,在此生命周期中是不存在
this
的,因为Vue
实例还没有创立。 - created – 实例初始化之后调用
- beforeMount – 在挂载开始之前被调用,如果是在服务器端渲染时不被调用,因为元素还没有渲染,在此申明周期无奈获取到
DOM
元素。 - mounted – 在挂载后被调用,是能够获取元素,并进行操作
- beforeUpdate – 视图层数据更新前调用,在虚构
DOM
更新之前,能够获取到DOM
j 节点,然而此节点为更新之前的DOM
节点 - updated – 视图层数据更新后调用,此处获取到的
DOM
节点是更新之后DOM
- beforeDestroy – 实例销毁之前调用,在被销毁的组件中进行调用
- destroyed – 实例销毁后调用
beforeDestroy
和 destroyed
生命周期阶段能够在此阶段,进行 `Vuex
数据重置,勾销事件监听等操作,晋升性能。
以上是比拟罕用的生命周期钩子函数,还有两个比拟非凡的生命周期钩子,别离是 activated
和deactivated
,这两个生命周期只有在组件上应用了 keep-alive
之后才会被执行。
- activated – 实例被激活时应用,用于反复激活一个实例的时候,该生命周期于会在
mounted
之后执行。 - deactivated – 实例没有被激活时
问:父组件和子组件生命周期钩子函数执行程序?
答:
Vue
的父组件和子组件生命周期钩子函数执行程序能够归类为以下 4 局部:
加载渲染过程
- 父 beforeCreate
- 父 created
- 父 beforeMount
- 子 beforeCreate
- 子 created
- 子 beforeMount
- 子 mounted
- 父 mounted
子组件更新过程
- 父 beforeUpdate
- 子 beforeUpdate
- 子 updated
- 父 updated
父组件更新过程
- 父 beforeUpdate
- 父 updated
销毁过程
- 父 beforeDestroy
- 子 beforeDestroy
- 子 destroyed
- 父 destroyed
问:在哪个生命周期内调用异步申请?
答:
能够在钩子函数 created
、beforeMount
、mounted
中进行调用,因为在这三个钩子函数中,data
曾经创立,能够将服务端端返回的数据进行赋值。举荐在 created 钩子函数中调用异步申请,因为在 created
钩子函数中调用异步申请,因为能更快获取到服务端数据,缩小页面 loading
工夫,ssr
不反对 beforeMount
、mounted
钩子函数,所以放在 created
中有助于一致性。
问:父组件能够监听到子组件的生命周期吗?
答:
能够的,有两种办法能够能够做到,第一种则是应用 $emit
自定义事件进行事件监听,当子组件执行到某个生命周期时用 $emit
告诉即可。第二种应用 @hook: 生命周期名称
,@hook
办法不仅仅是能够监听 mounted
,其它的生命周期事件,例如:created
,updated
等都能够监听。
问:keep-alive 是做什么的?
答:keep-alive
是 Vue
内置的一个组件,能够使被蕴含的组件保留状态,防止从新渲染,其有以下个性:
- 个别联合路由和动静组件一起应用,用于缓存组件
- 提供
include
和exclude
属性,两者都反对字符串或正则表达式,include
示意只有名称匹配的组件会被缓存,exclude
示意任何名称匹配的组件都不会被缓存,其中exclude
的优先级比include
高 - 对应两个钩子函数
activated
和deactivated
,当组件被激活时,触发钩子函数activated
,当组件被移除时,触发钩子函数deactivated
。
问:组件之间的传值通信形式有哪些?
答:组件间通信形式有 7 种:
1. props/emit
父组件传入属性,子组件通过 props
接管,能够通过这种办法获取到父组件传入的参数。而子组件则是能够通过 $emit('工夫名', 参数)
像外裸露一个自定义事件,在父组件中的属性监听事件,同时也能获取子组件传进去的参数,应用 v-on: 事件名称 =func
或者 @事件名称 =func
的形式监听子组件的自定义事件。
还能够通过 prop
接管函数为参数,子组件调用该函数,子组件执行函数时,能够传入对应的实参,在父组件接管时以形参的模式接管,执行对应的逻辑,能够达到通信的目标。
2. EventBus
EventBus
又称为事件总线,EventBus
来作为沟通桥梁能够使任意两个组件之间通信,就像是所有组件共用雷同的事件核心,能够向该核心注册发送事件或接管事件。
如何应用 EventBus
,应用new Vue
的形式取得 Vue
实例,本质上 EventBus
是一个不具备 DOM
的组件,它具备的仅仅只是它实例办法而已,因而它十分的轻便。
能够把获取到 Vue
实例挂载到 Vue.prototype
中也能够寄存到一个独自的 js
文件中导出,应用哪种取决于业务的具体情况,个别举荐应用第二种办法。
应用 EventBus.$emit("事件名称", 参数);
的形式进行事件公布。应用 EventBus.$on("事件名称", func)
的办法对事件进行订阅。func
的参数则是对应事件传递过去的参数。
EventBus
的原理是把注册的事件存起来,等触发事件时再调用。定义一个类去处理事件。
3. Vuex 状态治理
Vuex
是 Vue
的外围插件、用于任意组件的任意通信,需注意刷新后有可能 Vuex
数据会失落。
创立全局惟一的状态治理仓库 store
,有同步mutations
、异步actions
的形式去治理数据,有缓存数据 getters
,还能分成各个模块modules
易于保护
4. ($parent|$root)/$children
这种通信形式举荐应用,这样会使两个组件之间强耦合在一起,对于当前的保护和拓展来说不不是特地的敌对。
通过 ($parent|$root)/$children
获取到对应的组件实例,调用组件外部的办法以达到通信的成果。
5. $ref
通过 $ref
援用的形式获取子节点,罕用于父组件调用子组件的办法,在 $refs
来存储以后所有设置了 ref
属性的组件的实例,在实例中能够调用到组件外部的办法。
6. attrs/listeners
$attrs
能够获取父组件传进来但没有通过 props
接管的属性,能够应用 v-bind="$attrs"
的模式持续向子组件传递参数。
$listeners
会开展父组件的所有监听的事件,一个页面中有两个组件的点击事件触发办法是一样的。能够应用 v-on="$listeners"
把事件持续向子组件传递事件。
7. provide/inject
父组件中通过 provider
来提供变量,而后在子组件中通过 inject
来注入变量。只有在父组件中调用了,那么在这个父组件失效的生命周期内,所有的子组件都能够调用 inject
来注入父组件中的值。
问:watch 和 computed 有什么区别?
答:
computed
是计算属性在应用时和 data
对象中的数据属性是相似的,watch
是用于监听某一个属性的变动的,computed
善于的是一个数据受多个数据影响,然而 watch
是用于监听某一个属性的变动的。
computed
反对缓存只有依赖数据产生扭转,才会从新进行计算,computed
基于它们的响应式依赖进行缓存的,也就是基于 data
中申明过或者父组件传递的 props
中的数据通过计算失去的值,watch
则不反对缓存,数据变,间接会触发相应的操作。watch
的函数接管两个参数,第一个参数是最新的值;第二个参数是输出之前的值;
computed
不反对异步,computed
内有异步操作时有效,无奈监听数据的变动,watch
反对异步。
computed
在应用时,computed
属性值是函数,那么默认会走 get
办法,函数的返回值就是属性的属性值,在 computed
中的,属性都有一个 get
和一个 set
办法,当数据变动时,调用 set
办法。watch
在应用时,监听数据必须是 data
中申明过或者父组件传递过去的 props
中的数据,当数据变动时,触发其余操作,函数有两个参数。immediate
(组件加载立刻触发回调函数执行),deep
(深度监听)为了发现对象外部值的变动,简单类型的数据时应用。
computed
外部就是依据 Object.definedProperty()
实现的,computed
具备缓存性能,依赖的值不发生变化,就不会从新计算。watch
是监控值的变动,值发生变化时会执行对应的回调函数,computed
和 watch
都是基于 Watcher
类来执行的。computed
缓存性能依附一个变量 dirty
,示意值是不是脏的默认是 true
,取值后是false
,再次取值时dirty
还是 false
间接将还是上一次的取值返回。
问:为什么 data 必须是一个工厂函数而不能是一个对象?
答:
因为 Vue
组件复用准则,因为 data
是对象为援用类型,当一个组件的数据扭转后另一个组件状态会受到影响,然而通过工厂函数的模式返回对象就不会导致这样的问题,因为每个工厂函数所返回的对象都是一个全新的对象绝对独立。
问:Vue 中 style scoped 有作用?
答:
在 vue
文件中的 style
标签上,有一个非凡的属性:scoped
。当一个 style
标签领有 scoped
属性时,它的 CSS 款式就只能作用于以后的组件,也就是说,该款式只能实用于以后组件元素。通过该属性,能够使得组件之间的款式不相互净化。scoped
会在 DOM
构造及 css
款式上加上唯一性的标记 data-v-something
属性,即 CSS
带属性选择器,以此实现相似作用域的抉择形式,从而达到款式私有化,不净化全局的作用。scoped
应用尽管不便然而咱们须要慎用,因为在咱们须要批改公共组件(三方库或者我的项目定制的组件)的款式的时候,scoped 往往会造成更多的艰难,须要减少额定的复杂度。应用 >>>
能够穿透 scoped
属性,批改其余第三方组件的款式。应用 sass
或less
的款式穿透/deep/
。
问:v-show 与 v -if 有什么区别?
答:
v-if
是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建,v-if
是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。v-show
就简略得多——不论初始条件是什么,元素总是会被渲染,并且只是简略地基于 CSS
的display
属性进行切换。所以,v-if
实用于在运行时很少扭转条件,不须要频繁切换条件的场景,v-show
则实用于须要十分频繁切换条件的场景。
问:当给 data 中的对象或数组数据产生变更数据没有视图没有响应是什么起因?
答:
因为在 Vue
在初始化时,会对 data
中的数据应用 object.defineproperty
对数据进行挟持,通过 get/set
实现数据响应的操作,因为增加的属性,在 Vue
在初始化时该属性不存在于 data
中,所以该属性没有挟持到无奈执行 get/set
。Vue
提供过 this.$set
办法能够对新减少的数据挟持实现数据相应。
问:SPA 和 SSR 有什么区别
答:
SPA
利用是当初最罕用的开发模式即单页面利用,页面整体式 javaScript
渲染进去的,称之为客户端渲染 CSR
。SPA
渲染过程由客户端拜访 URL
发送申请到服务端,返回 HTML
构造 (然而SPA
的返回的 HTML
构造是十分的小的,只有一个根本的构造)。客户端接管到返回后果之后,在客户端开始渲染 HTML
,渲染时执行对应javaScript
最初通过 JavaScript
渲染成 HTML
,渲染实现之后再次向服务端发送数据申请,留神这里时数据申请,服务端返回json
格局数据。客户端接收数据,而后实现最终渲染。
SSR
渲染流程是这样的,客户端发送 URL
申请到服务端,服务端读取对应的 URL
的模板信息,在服务端做出 HTML
和数据的渲染,渲染实现之后返回 HTML
构造,客户端这时拿到的之后首屏页面的 HTML
构造。所以用户在浏览首屏的时候速度会很快,因为客户端不须要再次发送 ajax
申请。并不是做了 SSR
咱们的页面就不属于 SPA
利用了,它依然是一个独立的 SPA
利用。
SSR
是处于 CSR
与SPA
利用之间的一个折中的计划,在渲染首屏的时候在服务端做出了渲染,留神仅仅是首屏,其余页面还是须要在客户端渲染的,在服务端接管到申请之后并且渲染出首屏页面,会携带着残余的路由信息预留给客户端去渲染其余路由的页面。
SSR 长处:
- 更好的 SEO,搜索引擎爬虫爬取工具能够间接查看齐全渲染的页面
- 更宽的内容达到工夫(
time-to-content
),当权申请页面的时候,服务端渲染完数据之后,把渲染好的页面间接发送给浏览器,并进行渲染。浏览器只须要解析html
不须要去解析javaScript
。
SSR 毛病:
- 开发条件受限,
Vue
组件的某些生命周期钩子函数不能应用 - 开发环境基于
Node.js
- 会造成服务端更多的负载。在
Node.js
中渲染残缺的应用程序,显然会比仅仅提供动态文件server
更加占用CPU
资源,因而如果你在意料在高流量下应用,请筹备响应的服务负载,并理智的采纳缓存策略
问:vue-router 有几种模式?
答:
vue-router
公共三种模式:hash
、history
和abstract
。
hash 模式:
hash
值是url
中#
及前面的局部hash
值扭转不会引起页面刷新hash
值的扭转会触发hashchange
事件
history 模式:
- 利用
history.pushState
来实现url
跳转而无需刷新页面 - 须要后盾配置反对,如果
URL
匹配不到任何动态资源,服务器应该返回利用依赖的index.html
页面
abstract 模式:
- 实用于所有
JavaScript
环境,例如服务器端应用Node.js
- 没有浏览器
API
,路由器将主动被强制进入此模式 - 这个历史记录的次要目标是解决
SSR
, 通过调用router.push
或router.replace
将该地位替换为启动地位
问:$route 和 $router 的区别?
答:
$router
通过 Vue.use(VueRouter)
和Vue
构造函数失去一个 router
的实例对象,这个对象中是一个全局的对象,他蕴含了所有的路由,蕴含了许多要害的路由信息,还有路由的跳转办法,钩子函数等。
$route
$route
是一个跳转的路由对象,每一个路由都会有一个 $route
对象,是一个部分的对象,能够获取对应的 name
,path
,params
,query
等。路由对象的属性是只读的,外面的属性是 immutable
(不可变) 的,不过能够应用 watch
监测路由的变动。
问:vue-router 守卫都有哪些?
答:
vue-router
守卫主分为三类守卫别离是:全局守卫、路由守卫和组件守卫
全局守卫
全局守卫字面量了解就是定义在全局钩子函数,当路由触发跳转操作时则会触发对应的钩子函数,全局守卫有三种:
- beforeEach:它在每次导航路由跳转前触发
- beforeResolve:所有
组件内守卫
和异步路由组件
被解析之后触发 - afterEach:路由跳转实现后触发
beforeEach
全局前置守卫接管三个参数:
- to: 行将要进入的指标路由对象
- from: 以后导航正要来到的路由对象
- next: 肯定要调用该办法不然会阻塞路由
重点说一下 next
参数,next
参数是一个函数,能够不增加,然而一旦增加,则必须调用一次,否则路由跳转等会进行。next
参数只有在 beforeEach
和beforeResolve
两个路由守卫中存在,afterEach
因为曾经跳转到了指标路由则没有提供 next
办法。如果间接执行 next
函数则会间接接入 to
所对应的指标路由中。next(false)
则会终端以后路由当行,返回 form
路由对应的路由中。在 next
中传入跳转计划 (如:next('/') 或者 next({path:'/'})
),则会跳转到对应的地址中。next(error)
则导航会终止,且该谬误会被传递给 router.onError()
注册过的回调。beforeEach
能够定义多个,会依据创立顺序调用,在所有守卫实现之前导航始终处于期待中。
路由守卫
路由守卫只有一个 beforeEnter
,须要在路由配置上定义beforeEnter
守卫,此守卫只在进入路由时触发,在 beforeEach
之后紧随执行,不会在 params
、query
或hash
扭转时触发,beforeEnter
路由守卫的参数是 to
、from
、next
,同beforeEach
一样。
组件守卫
组件守卫则是须要定义在组件中的,组件守卫石油三种:
- beforeRouteEnter:路由进入组件之前调用,该钩子在全局守卫
beforeEach
和路由守卫beforeEnter
之后,全局beforeResolve
和全局afterEach
之前调用,该守卫内拜访不到组件的实例,也就是this
为undefined
,也就是他在beforeCreate
生命周期前触发 - beforeRouteUpdate:在以后路由扭转,然而该组件被复用时调用,举例来说,对于一个带有动静参数的门路
/foo/:id
,在/foo/1
和/foo/2
之间跳转的时候,因为会渲染同样的Foo
组件,因而组件实例会被复用。而这个钩子就会在这个状况下被调用。能够拜访到组件实例this
- beforeRouteLeave:导航来到该组件的对应路由时调用,能够拜访组件实例
this
问:vue-router 如何做权限管制?
答:
vue-router
权限管制最罕用的办法是在 beforeEach
须要跳转的路由进行管制,如果没有权限则无奈跳转到路由,个别状况下在路由配置的 meta
中增加权限管制字段,当路由产生跳转时,会触发 beforeEach
全局路由守卫,在该守卫中对权限进行甄别即可实现路由权限管制。首次之外还有一种办法,则是应用 addRoutes
办法,动静的向 vue-router
中增加路由配置,在增加配置之前只在路由中注册具备权限的路由信息即可。
问:vue-router 传参有几种形式?
答:
vue-router
传参一共有三种形式:
param
param
传参须要在配置路由信息的是时候,在路由中注明须要接管的参数 (如:url/:id
),其中的id
则是 param
须要传递的参数,须要留神的是当以这种模式传递参数的时候,如果没有传递参数是无奈正确的匹配到路由的。能够在定义参数前增加 ?
则代表该参数可有可无,无论是否传递参数都能正确的匹配到对应的路由。参数前面能够增加正则 如:url/?:id(\\d+)
,以这种模式增加参数能够限度参数的格局。
当应用 param
的时候,在配置路由信息的时增加 {props:true}
配置项,则传入的 param
参数,在页面的中的 props
中接管到该参数,除了以这种形式接管以外,还能够应用 this.$route.params
中获取到参数。传递 param
参数无论应用 router-link
还是编程式导航,如果是传入的字符串模式,则能够在对应的路由地址前面间接拼接参数即可。如果是以对象的模式,在对象中增加 params
字段,外部对应的是对应的 param
参数即可。
query
query
和 param
传参只是有稍微的不同,query
传参不须要在路由配置中定义有哪些参数,query
是可有可无的不会影响到路由地址的匹配。query
的传递参数应用 query
对象或者在地址前面以 ?
的模式进行拼接。接管的时候则是在 this.$route.query
中获取到传递过去的参数。
注:无论是应用 param 还是 query 进行路由传参,当页面刷新的时候,都不会导致参数失落,因为其参数是间接寄存于路由地址中
meta
集体不太倡议应用 meta
这种模式传参,尽管通过操作也是能够实现传参的目标,然而当页面刷新的时候会导致参数的失落,然而,这种传参是隐式传参,用户是无奈得悉的。
应用 meta
传参能够在路由跳转之前,在跳转页面通过 this.$route
获取到以后路由的对象,在路由对象的 meta
中存入对应的参数。而后应用对应的办法进行路由跳转。当跳转实现之后,在指标页面应用 beforeRouteEnter
组件钩子函数,对参数进行接管,尽管 beforeRouteEnter
无法访问到 this
,然而在第三个参数即next
中能够传入一个函数作为参数,这个参数中能够拜访到以后组件的实例,在之后在 beforeRouteEnter
的第二个参数 form
中对象中能够读取到 meta
中存储的数据实现传参。
你对 Vuex 是如何了解的?
答:
Vuex
是为 Vue
提供的状态管理模式,集中式存贮治理利用的所有组件的状态,并以相应的规定保障状态以一种可预测的形式发生变化。中有五大外围属性:
- state:
state
次要用于存储数据和存储状态,在根实例中注册store
当前,用this.$store.state
来拜访到state
中所存储的数据。存放数据形式为响应式,vue
组件从store
中读取数据,如数据发生变化,组件也会对应的更新。 - mutation:更改
Vuex
的store
中的状态的惟一办法是提交mutation
。 - action:该办法与
mutation
相似,惟一不同的是在action
所执行的是异步办法。 - getter:能够认为是
store
的计算属性,它的返回值会依据它的依赖被缓存起来,且只有当它的依赖值产生了扭转才会被从新计算。 - module:将
store
宰割成模块,每个模块都具备state
、mutation
、action
、getter
甚至是嵌套子模块。
当组件进行数据批改的时候咱们须要调用 dispatch
来触发 actions
外面的办法。actions
外面的每个办法中都会有一个 commit
办法,当办法执行的时候会通过 commit
来触发 mutations
外面的办法进行数据的批改。mutations
外面的每个函数都会有一个 state
参数,这样就能够在 mutations
外面进行 state
的数据批改,当数据批改结束后,会传导给页面。页面的数据也会产生扭转。
因为传参的办法对于多层嵌套的组件将会十分繁琐,并且对于兄弟组件间的状态传递尽管也能够通过其余的办法进行参数传递,可能仍会感到有力。咱们常常会采纳父子组件间接援用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式十分软弱,通常会导致代码无奈保护。所以咱们须要把组件的共享状态抽取进去,以一个全局单例模式治理。在这种模式下,咱们的组件树形成了一个微小的 视图
,不论在树的哪个地位,任何组件都能获取状态或者触发行为!另外,通过定义和隔离状态治理中的各种概念并强制恪守肯定的规定,咱们的代码将会变得更结构化且易保护。
底层面试题
这一部分面试题相比下面要绝对难一些,可能间接回升了一个等级,可能须要对 Vue
相干 API
有足够的理解,并且浏览过相干源码的下能力更好的了解。
问:Vue 的两个外围是什么?
答:别离是:数据驱动和组件零碎
数据驱动
Vue
数据观测原理在技术实现上,利用的是 ES5
的Object.defineProperty
和存储器属性:getter
和 setter
可称为基于依赖收集的观测机制,这些 getter
和setter
对用户来说是不可见的,然而在外部它们让 Vue
可能追踪依赖,在 property
被拜访和批改时告诉变更。外围是 VM
,即ViewModel
,保证数据和视图的一致性。每一个指令都会有一个对应的用来观测数据的对象,叫做watcher
,每个组件实例都对应一个watcher
实例,它会在组件渲染的过程中把 接触
过的数据 property
记录为依赖。getter
的时候咱们会收集依赖,依赖收集就是订阅数据变动 watcher
的收集,依赖收集的目标是当响应式数据发生变化时,可能告诉相应的订阅者去解决相干的逻辑。setter
的时候会触发依赖更新,之后当依赖项的 setter
触发时,会告诉watcher
,从而使它关联的组件从新渲染。
组件零碎
组件零碎是 Vue
的另一个重要概念,因为它是一种形象,容许咱们应用小型、独立和通常可复用的组件构建大型利用。认真想想,简直任意类型的利用界面都能够形象为一个组件树,Vue
更心愿构建的页面是由各个组件形成而非html
,当开发我的项目时把整个应用程序划分为组件,以使开发更易治理与保护。
组件零碎中蕴含了几个很重要的局部:
- template:模板申明了数据和最终展示给用户的 DOM 之间的映射关系。
- data:一个组件的初始数据状态。对于可复用的组件来说,这通常是公有的状态
- props:组件之间通过参数来进行数据的传递和共享
- methods:对数据的改变操作个别都在组件的办法内进行
- lifecycle hooks:一个组件会触发多个生命周期钩子函数
- assets:Vue.js 当中将用户自定义的指令、过滤器、组件等统称为资源
问:如何了解 MVVM?
答:
MVVM
次要由三个局部组成:Model
、View
、ViewModel
- Model:代表数据模型,也能够在 Model 中定义数据批改和业务逻辑
- View:代表 UI 组件,它负责将数据模型转化成 UI 展示进去
- ViewModel:代表同步 View 和 Model 的对象
在 MVVM
中View
和 Model
没有间接的关系,全副通过 ViewModel
进行交互。ViewModel
负责把 Model
的数据同步到 View
显示进去,还负责把 View
的批改同步回 Model
。MVVM
实质就是基于操作数据来操作视图进而操作 DOM
,借助MVVM
无需间接操作 DOM
,开发者只需实现蕴含申明绑定的视图模板,编写ViewModel
中有业务,使得 View
齐全实现自动化。
问:Vue 初始化做了什么?
答:
Vue
在初始化时会接管用户所传入的 options
即所须要的参数,之后则会创立 Vue
的实例,并且为该实例定义一个惟一的 _uid
的标识,用于辨别 Vue
的实例,把 Vue
实例标记成 Vue
防止被观察者观测到。
实现实例创立的初始化工作之后,须要对用户选项和零碎默认的选项进行合并,首先解决的的是组件的配置内容,把传入的 option
与其构造函数自身进行合并,这里蚕蛹的策略是默认配置和传入配置的配置进行合并。如果是外部组件(子组件)实例化,且动静的 options
合并会很慢,须要对 options
的一些非凡参数进行解决。如果是根组件,将全局配置选项合并到根组件的配置上,其实就是一个选项合并。
接下来则是初始化外围局部,首先初始化 Vue
实例生命周期相干的属性,定义了比方:root
、parent
、children
、refs
,接着初始化自定义组件事件的监听,若存在父监听事件,则增加到该实例上,而后初始化 render
渲染所需的 slots、渲染函数等。其实就两件事
- 插槽的解决、
- $createElm 也就是 render 函数中的 h 的申明
接下来则是执行 beforeCreate
钩子函数,在这里就能看出一个组件在创立前和后别离做了哪些初始化。
执行完 beforeCreate
钩子函数之后,初始化注入数据,隔代传参时先 inject
。作为一个组件,在要给后辈组件提供数据之前,须要先把祖辈传下来的数据注入进来。对props
、methods
、data
、computed
、watch
进行初始化,包含响应式的解决,在把祖辈传下来的数据注入进来当前再初始化provide
。
最初调用 created
钩子函数,初始化实现,能够执行挂载了,挂载到对应 DOM
元素上。如果组件构造函数设置了 el
选项,会主动挂载,所以就不必再手动调用 $mount
去挂载。
问:如何了解 Vue 的响应式数据?
答:
Vue
中实现了一个 definedReactive
办法,办法外部借用 Object.definedProperty()
给每一个属性都增加了 get/set
的属性。definedReactive
只能监控到最外层的对象,对于内层的对象须要递归劫持数据。数组则是重写的 7 个 push
、pop
、shift
、unshift
、reverse
、sort
、splice
来给数组做数据拦挡,因为这几个办法会扭转原数组。对象新增或者删除的属性无奈被 set
监听到只有对象自身存在的属性批改才会被劫持。或应用 Vue
提供的 $set()
实现监听,在 defineReactive
中存有一个 Dep
类,这个用来收集渲染的 Watcher
,Watcher
的次要工作则应用用来更新视图。
问:Vue 如何进行依赖收集?
答:
Dep
是一个用来负责收集 Watcher
的类,Watcher
是一个封装了渲染视图逻辑的类,用于派发更新的。须要留神的是 Watcher
是不能间接更新视图的还须要联合 Vnode
通过 patch()
中的 diff
算法才能够生成真正的DOM
。
然而每一个属性都有本人的 dep
属性,来寄存依赖的 Watcher
,属性发生变化后会告诉Watcher
去更新。在用户获取 (getter
) 数据时 Vue
给每一个属性都增加了dep
属性来 (collect as Dependency
) 收集 Watcher
。在用户setting
设置属性值时 dep.notify()
告诉收集的 Watcher
从新渲染。Dep
依赖收集类其和 Watcher
类是多对多双向存储的关系。每一个属性都能够有多个 Watcher
类,因为属性可能在不同的组件中被应用。同时一个 Watcher
类也能够对应多个属性。
每一个属性能够有多个依赖,比方这个属性可能应用在 computed
中,watch
中,自身的 data
属性中。这些依赖都是应用响应式数据的 Dep
来收集的。Watcher
是依赖就像一个中介,可能被 Dep
收集也可能被 Dep
告诉更新。
问:Vue 是如何编译模板的?
答:
编译是把咱们写的 .vue
文件里的 template
标签蕴含的 html
标签变为 render
函数,能够这么了解 template
模板是对 render
的封装,template
在 vue
源码外面会变转化为 render
函数:
- 将 template 模板字符串转换成 ast 语法树(parser 解析器),这里应用了大量的正则来匹配标签的名称,属性,文本等。
- 对 AST 进行动态节点 static 标记,次要用来做虚构 DOM 的渲染优化(optimize 优化器),这里会遍历出所有的子节点也做动态标记
- 应用 AST 语法树 从新生成 render 函数代码字符串 code。
问:Vue 生命周期钩子实现原理?
答:
Vue
中的生命周期钩子只是一个回调函数,在创立组件实例化的过程中会调用对应的钩子执行。应用 Vue.mixin({})
混入的钩子或生命周期中定义了多个函数,Vue
外部会调用 mergeHook()
对钩子进行合并放入到队列中顺次执行。
问:Vue.mixin 应用场景和原理?
答:
Vue.mixin
次要用于抽离一个公共的业务逻辑实现复用。其外部执行时会调用 mergeOptions()
办法采纳策略模式针对不同的属性合并。混入的数据和组件的数据有抵触就采纳组件自身的。Vue.mixin({})
存在一些缺点,导致混入的属性名和组件属性名产生命名抵触,数据依赖的起源问题。
问:$nextTick 实现原理?
答:
vm.$nextTick(cb)
是一个异步的办法为了兼容性做了很多降级解决顺次有 promise.then,MutationObserver
,setImmediate
,setTimeout
。在数据批改后不会马上更新视图,而是通过set
办法 notify
告诉 Watcher
更新,将须要更新的 Watcher
放入到一个异步队列中,nexTick
的回调函数就放在 Watcher
的前面,期待主线程中同步代码执行借宿而后顺次清空队列中,所以 vm.nextTick(callback)
是在 dom
更新完结后执行的。
问:vue-router 实现原理?
答:vue-router
最罕用的模式有两种别离是 hash
模式和 history
模式:
hash 模式
hash
模式是 vue-router
的默认路由模式,它的标记是在域名之后带有一个 #
,通过window.location.hash
获取到以后 url
的hash
。·hash·模式下通过 hashchange
办法能够监听 url
中hash
的变动。hash
模式的特点是兼容性更好,并且 hash
的变动会在浏览器的 history
中减少一条记录,能够实现浏览器的后退和后退性能。
history 模式
history
模式是另一种前端路由模式,它基于 HTML5
的history
对象,通过 location.pathname
获取到以后 url 的路由地址。history
模式下,通过 pushState
和replaceState
办法能够批改 url
地址,联合 popstate
办法监听 url
中路由的变动。
问:vuex 实现原理?
答:
Vuex
在初始化时,在全局存储了 Vue
的实例,在 install
函数中,首先会判断是否曾经调用了 Vue.use(Vuex)
,而后调用applyMixin
办法进行初始化的一些操作,applyMixin
办法只做了一件事件,就是将所有的实例上挂载一个 $store
对象,在应用 vuex
的时候,会将 store
挂载在根组件之上。在第一次调用 vuexInit
函数时,options.store
就是根选项的 store
,因而会判断其类型是不是function
,若是则执行函数并将后果赋值给根实例的$store
中,否则间接赋值。
Vuex
的 state
状态是响应式,是借助 vue
的data
是响应式,将 state
存入 vue
实例组件的 data
中,Vuex
的 getters
则是借助 vue
的计算属性 computed
实现数据实时监听。
结束语
以上面试题是笔者在面试过程中面试官们问到的做了一些调研学习和整顿,可能会有些许的谬误,大家能够在上面评论指出谬误,大家独特学习提高。如果有哪些方面没有笼罩到的中央大家也能够评论通知我,前面回补齐。