写这篇文章的原因是想好好把vue复习一下,然后整理一下我认为常见很重要的面试题,也方便我自己以后查阅,我觉得这些基本上是Vue里面相关的所有重要知识点了,相信会对现在找工作的小伙伴们很大帮助!
问题大纲
大家可以先看一下这个提纲思考下,看看自己还有哪些不清楚的地方,查漏补缺一下~~~
- 谈一下你对
MVVM
原理的理解 - 请说一下响应式数据的原理
- 为何
Vue
采用异步渲染 vue
优点Proxy
与Object.defineProperty()
的对比- 如何比较
React
和Vue
Vue
中相同逻辑如何抽离nextTick
实现原理diff
算法的时间复杂度- 简述
Vue
中diff
算法原理 Vue
中v-if
和v-show
的区别Vue
每个生命周期什么时候被调用- 异步请求适合在哪个生命周期调用
- 第一次页面加载会触发哪几个钩子
- 为什么
v-for
和v-if
不能连用 Vue
如何进行组件间的通信- 什么时候需要使用到
beforeDestroy
- 谈谈你对作用域插槽的理解
- 谈谈你对
keep-alive
的了解 v-for
中为什么要用key
- 组件中的
data
为什么是函数 computed
和watch
有什么区别vue
几种常用的指令vue
常用的修饰符Vue
的渲染过程vue
中的模板编译原理Vue
中v-html
会导致哪些问题- 原生
dom
的绑定 Watch
中的deep:true
是如何实现的action
和mutation
区别vue-router
有哪几种导航钩子- 实现
hash
路由和history
路由 $route
和$router
的区别Vuex
的理解及使用场景- 你有对
Vue
项目进行哪些优化 Vue3.0
你知道有哪些改进- 为什么要使用异步组件?
谈一下你对MVVM原理的理解
M - Model,Model
代表数据模型,也可以在Model
中定义数据修改和操作的业务逻辑
V - View,View
代表 UI
组件,它负责将数据模型转化为 UI
展现出来
VM - ViewModel,ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View
和 Model
的对象,连接 Model
和 View
【重点】:一定要理解Vue
的MVVM
原理,面试必问!
请说一下响应式数据的原理
采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
默认Vue在初始化数据时,会给data
中的属性使用Object.defineProperty
重新定义所有属性,当页面到对应属性时,会进行依赖收集(收集当前组件中的watcher
)如果属性发生变化会通知相关依赖进行更新操作
为什么Vue采用异步渲染
因为如果不采用异步更新,那么每次更新数据都会对当前租金按进行重新渲染,所以为了性能考虑,Vue
会在本轮数据更新后,再去异步更新数据
Vue优点
- 轻量级框架
只关注视图层,是一个构建数据的视图集合,大小只有几十kbVue.js
通过简洁的API提供高效的数据绑定和灵活的组件系统 - 简单易学
国人开发,中文文档,不存在语言障碍,易于理解和学习 - 双向数据绑定
通过MVVM
思想实现数据的双向绑定,让开发者不用再操作dom
对象,有更多的时间去思考业务逻辑 - 组件化
Vue.js
通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component
)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个参数叫做组件的属性),然后再分别写好各种组件的实现(填坑),然后整个应用就算做完了 - 虚拟DOM
把最终的DOM
操作计算出来并优化,由于这个DOM
操作属于预处理操作,并没有真实的操作DOM
,所以叫做虚拟DOM
。最后在计算完毕才真正将DOM
操作提交,将DOM
操作变化反映到DOM
树上 - 视图,数据,结构分离
使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作 - 运行速度更快
像比较与react
而言,同样都是操作虚拟dom
,就性能而言,vue
存在很大的优势
Proxy与Object.defineProperty()的对比
Proxy的优点:
- 可以直接监听对象而非属性,并返回一个新对象
- 可以直接监听数值的变化
- 可以劫持整个对象,并返回一个新对象
Proxy的缺点:
Proxy
是es6
提供的新特性,兼容性不好,所以导致Vue3一致没有正式发布让开发者使用
Object.defineProperty的优点:
- 兼容性好,支持IE9
- IE9以下的版本不兼容
Object.defineProperty的缺点:
- 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应
- 只能劫持对象的属性,我们需要对每个对象的每个属性进行遍历
如何比较React和Vue
监听数据变化的实现原理不同:
Vue
通过getter/sette
r以及一些函数,能精确知道数据变化React
默认是通过比较引用的方式(diff
)进行的,React
不精确监听数据变化
数据流不同:
Vue2.0
可以通过props
实现双向绑定,用vuex
单向数据流的状态管理框架React
不支持双向绑定,提倡单项数据流,Redux
单向数据流的状态管理框架
组件通信的区别:
- Vue三种组件通信方法:
父组件通过props
向子组件传递数据或回调
子组件通过事件event
向父组件发送数据或回调
通过provide/inject
实现父组件向子组件传入数据,可跨层级 - React三种组件通信方法:
父组件通过props
向子组件传递数据React
不支持子组件像父组件发送数据,而使用的是回调函数
通过context
实现父组件向子组件传入数据, 可跨层级
模板渲染方式不同:
- 表面上来看:
React
通过JSX
渲染模板Vue
通过HTML
进行渲染 - 深层上来看:
React
是通过原生JS
实现模板中常见语法,如:插件,条件,循环Vue
是与组件JS
分离的单独模板,通过指令实现,如:v-if
模板中使用的数据:
React
里模板中使用的数据可以直接import
的组件在render
中调用Vue
里模板中使用的数据必须要在this
上进行中转,还要import
一个组件,还要在components
中声明
渲染过程不同:
Vue
不需要渲染整个组件树React
状态改变时,全部子组件重新渲染
框架本质不同:
Vue
本质是MVVM
框架,由MVC
发展而来React
是前端组件化框架,由后端组件化发展而来
Vuex和Redux的区别:
Vuex
可以使用dispatch
、commit
提交更新Redux
只能用dispatch
提交更新
组合不同功能方式不同:
Vue
组合不同功能方式是通过mixin
,可以帮我定义的模板进行编译、声明的props
接收到数据….React
组合不同功能方式是通过HoC
(高阶组件),本质是高阶函数
Vue中相同逻辑如何抽离
Vue.mixin
用法给组件每个生命周期,函数等都混入一些公共逻辑
nextTick实现原理
nextTick
方法主要是使用了宏任务和微任务,定义一个异步方法,多次调用nextTick
会将方法存在队列中,通过这个异步方法清空当前队列。所以这个nextTick
方法就是异步方法
diff算法的时间复杂度
两个数的完全的diff
算法是一个时间复杂度为o(n3)
, Vue
进行了优化O(n3)
复杂度的问题转换成O(n)
复杂度的问题(只比较同级不考虑跨级问题)在前端当中,你很少会跨级层级地移动Dom
元素,所以Virtual Dom
只会对同一个层级地元素进行对比
简述Vue中diff算法原理
- 先同级比较,在比较子节点
- 先判断一方有儿子一方没儿子的情况
- 比较都有儿子的情况
- 递归比较子节点
(大家要是不懂也可以看下这篇文章)对Dom-Diff的理解
Vue中v-if和v-show的区别
v-show
是css
切换,v-if
是完整的销毁和重新创建
使用 频繁切换时用v-show
,运行时较少改变时用v-if
v-if=‘false’
v-if
是条件渲染,当false
的时候不会渲染
Vue每个生命周期什么时候被调用
beforeCreate
在实例初始化之后,数据观测(data observer
)之前被调用created
实例已经创建完成之后被调用,在这一步,完成已完成以下的配置:数据观测(data observer
),属性和方法的运算,watch/event
事件回调,这里没有$el
beforeMount
在挂载开始之前被调用:相关的render
函数首次被调用mounted el
被创建的vm.$el
替换,并挂载到实例上去之后调用该钩子beforeUpdate
数据更新时调用,发生虚拟DOM
重新渲染和补丁之前updated
由于数据更改导致的虚拟DOM
重新渲染和打补丁,在这之后调用该钩子beforeDestory
实例销毁之前调用,在这一步,实例仍然完全可用destroyed
Vue
实例销毁后调用,调用后,Vu
实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用
异步请求适合在哪个生命周期调用
异步请求是在mounted
生命周期中调用的,而实际上也可以在created
生命周期中调用
第一次页面加载会触发哪几个钩子
beforeCreate, created, beforeMount, mounted
为什么v-for和v-if不能连用
当 v-for
和 v-if
处于同一个节点时,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-for
循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费。 这种场景建议使用 computed
,先对数据进行过滤
Vue如何进行组件间的通信
父子通信
父向子传递数据通过props
子向父传递是通过$emit
、event
子实例访问父实例通过$parent
父实例访问子实例通过$children
$attrs
用父组件传递数据给子组件或孙组件 (包含了父作用域中不作为 prop
被识别 (且获取) 的特性绑定 (class
和 style
除外))
listeners用父组件传递数据给子组件或孙组件 包含了父作用域中的 (不含 .native
修饰器的)v-on
事件监听器
祖先组件通过provider
提供变量给子孙组件
子孙组件通过inject
注入变量给祖先组件
ref
用来访问组件实例
兄弟通信
vuex
用来作为兄弟之间和跨级之间的通信
跨级通信
vuex
用来作为兄弟之间和跨级之间的通信
$attrs
用父组件传递数据给子组件或孙组件 (包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class
和 style
除外))
listeners用父组件传递数据给子组件或孙组件 包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器
祖先组件通过provider
提供变量给子孙组件
子孙组件通过inject
注入变量给祖先组件
(大家要是还不清楚可以看下这篇文章)
彻底理解Vue组件间通信(6种方式)-完整版
什么时候需要使用beforeDestroy
- 可能在当前页面中使用了
$on
方法,那需要在组件销毁前解绑 - 清除自己定义的定时器
- 解绑事件的绑定
srcoll
mousemove
....
谈谈你对作用域插槽的理解
单个插槽:
当子组件模板只有一个没有属性的插槽时, 父组件传入的整个内容片段将插入到插槽所在的 DOM
位置, 并替换掉插槽标签本身
命名插槽:
solt
元素可以用一个特殊的特性name
来进一步配置如何分发内容。 多个插槽可以有不同的名字。 这样可以将父组件模板中 slot
位置, 和子组件slot
元素产生关联,便于插槽内容对应传递
作用域插槽:
可以访问组件内部数据的可复用插槽(reusable slot)
在父级中,具有特殊特性 slot-scope
的<template>
元素必须存在, 表示它是作用域插槽的模板。slot-scope
的值将被用作一个临时变量名, 此变量接收从子组件传递过来的 prop
对象
谈谈你对keep-alive的了解
keep-alive
是 Vue
内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 。它拥有两个独立的生命周期钩子函数 actived
和 deactived
,使用 keep-alive
包裹的组件在切换时不会被销毁,而是缓存到内存中并执行 deactived
钩子函数,命中缓存渲染后会执行actived
钩子函数
v-for为什么要用key
key
是为Vue
中的vnode
标记的唯一id
,通过这个key
,我们的diff
操作可以 更准确、更快速
准确:
如果不加key
,那么vue
会选择复用节点(Vue
的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug
快速:
key
的唯一性可以被Map
数据结构充分利用
组件中的data为什么是函数
因为组件是用来复用的,JS
里对象是引用关系,这样作用域没有隔离,而new
Vue
的实例,是不会被复用的,因此不存在引用对象问题
computed和watch有什么区别
计算属性是基于他们的响应式依赖进行缓存的,只有在依赖发生变化时,才会计算求值,而使用 methods
,每次都会执行相应的方法
vue几种常用的指令
v-for
、 v-if
、v-bind
、v-on
、v-show
、v-else
vue常用的修饰符
.prevent
: 提交事件不再重载页面.stop
: 阻止单击事件冒泡.self
: 当事件发生在该元素本身而不是子元素的时候会触发.capture
: 事件侦听,事件发生的时候会调用
Vue的渲染过程
1、把模板编译为render
函数
2、实例进行挂载, 根据根节点render
函数的调用,递归的生成虚拟dom
3、对比虚拟dom
,渲染到真实dom
4、组件内部data
发生变化,组件和子组件引用data
作为props
重新调用render
函数,生成虚拟dom
, 返回到步骤3
Vue中的模板编译原理
- 将
template
转化成render
函数
Vue中v-html会导致哪些问题
- 可能会导致
xss
攻击 v-html
会替换掉标签内部的子元素
原生dom绑定
vue
在创建真是dom
时会调用createElm
,默认会调用invokeCreateHooks
会遍历当前平台下相对的属性处理代码,其中就有updateDOMListeners
方法,内部会传入add
方法
vue
中绑定事件是直接绑定给真实dom
元素的
组件绑定事件是通过vue
中自定义的$on
方法来实现的
Watch中的deep:true是如何实现的
当用户指定了watch
中的deep
属性为true
时,如果当前监控的值是数组类型,会对对象中的每一项进行求值,此时会将当前watcher
存入到对应属性的依赖中,这样数组中的对象发生变化时也会通知数据更新
action和mutation区别
mutation
是同步更新数据(内部会进行是否为异步方式更新数据的检测)action
异步操作,可以获取数据后调用mutation
提交最终数据
vue-router有哪几种导航钩子
有3种
- 全局导航钩子:
router.beforeEach(to,from,next)
,作用:跳转前进行判断拦截 - 组件内的钩子
- 单独路由独享组件
实现hash路由和history路由
- onhashchange
- history.pushState
$route
和$router
的区别
router
为VueRouter
的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象 经常用的跳转链接就可以用this.$router.push
,和router-link
跳转一样
$route为当前router跳转对象里面可以获取name
、path
、query
、params
等
Vuex的理解及使用场景
Vuex
是一个专为 Vue
应用程序开发的状态管理模式。每一个 Vuex
应用的核心就是 store(仓库)
Vuex
主要包括以下几个核心模块:
State
:定义了应用的状态数据
Getter
:在 store
中定义“getter
”(可以认为是 store
的计算属性),就像计算属性一样,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
Mutation
:是唯一更改 store
中状态的方法,且必须是同步函数
Action
:用于提交 mutation
,而不是直接变更状态,可以包含任意异步操作
Module
:允许将单一的 Store
拆分为多个 store
且同时保存在单一的状态树中
你有对 Vue 项目进行哪些优化?
代码层面的优化:
v-if
和v-show
区分使用场景computed
和watch
区分使用场景v-for
遍历必须为item
添加key
,且避免同时使用v-if
- 长列表性能优化
- 事件的销毁
- 图片资源懒加载
- 路由懒加载
- 第三方插件的按需引入
- 优化无限列表性能
- 服务端渲染
SSR or
预渲染
Webpack 层面的优化:
Webpack
对图片进行压缩- 减少
ES6
转为ES5
的冗余代码 - 提取公共代码
- 模板预编译
- 提取组件的
CSS
- 优化
SourceMap
- 构建结果输出分析
- Vue 项目的编译优化
基础的 Web
技术的优化:
1.开启gzip
压缩
2.浏览器缓存
3.CDN
的使用
4.使用 Chrome Performance
查找性能瓶颈
Vue3.0你知道有哪些改进
Vue3
采用了TS
来编写- 支持
Composition API
Vue3
中响应式数据原理改成proxy
vdom
的对比算法更新,只更新vdom
的绑定了动态数据的部分
为什么使用异步组件
在大型应用中,功能不停地累加后,核心页面已经不堪重负,访问速度愈来愈慢。为了解决这个问题我们需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块,从而提高页面加载速度
最后
我认为的vue比较重要的就这么多了吧,要是大家有更好的欢迎在评论区补充,最后祝大家找到自己满意的工作~~~
关注公众号即可加我好友,我拉你进 前端交流群,大家一起共同交流和进步❤️❤️❤️