面试题总结

149 阅读5分钟

父子组件生命周期执行顺序

父beforeCreate ->父created ->父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

1.当父组件执行完beforeMount挂载开始后,会依次执行子组件中的钩子,直到全部子组件mounted挂载到实例上,父组件才会进入mounted钩子

2.子级触发事件,会先触发父级beforeUpdate钩子,再去触发子级beforeUpdate钩子,下面又是先执行子级updated钩子,后执行父级updated钩子

Vue2和vue3的区别

一、 v ue2 和v ue3 双向数据绑定原理的区别?****

Vue2的双向数据绑定是利用ES5的一个API Object.definePropert()然后利用里面的 getter 和 setter 来实现双向数据绑定的,Vue3发生了改变,使用proxy替换Object.defineProerty,使用Proxy的优势:

1、 可直接监听数组类型的数据变化****

2、 性能的提升****

3、 监听的目标为对象本身,不需要想Object .defineProperty 一样遍历每个属性,有一定的性能提升****

4、 可直接实现对象属性的新增/删除****

二、 根节点不同****

Vue3在组件中支持多个根节点

Vue2

image.png

Vue3

image.png

 CompositionAPI(组合api)

Vue2和Vue3最大的区别就是,Vue3使用了Composition API(组合API);

在Vue2中是使用的Options API,这种写法不方便我们的阅读和交流,逻辑过于分散

Vue2

image.png

Vue3 :这样代码会更加简洁和整洁

image.png


proxy 相对 Object.defineProperty 优点有哪些?

proxy 和 Object.defineProperty 都是来实现响应式数据的。

vue3 使用 proxy 来代替 vue2 的 Object.defineProperty 效率更高,值得学习。

1> vue2 利用 Object.defineProperty 来劫持 data 数据的 getter 和 setter 操作,使得 data 在被访问或赋值时,动态更新绑定的 template 模板。而 Object.defineProperty 必须遍历所有的预值才能劫持每一个属性,这一缺点正好能够被 proxy 解决。

proxy 相比 Object.defineProperty 优点分别为:

代码的执行效果更快。proxy 可以直接监听对象而不是它的属性。proxy 可以直接监听数组的每个元素的变化。proxy 不需要初始化的时候遍历所有属性,如果有多层嵌套的话,只访问某个属性的时候,proxy 能够快速访问到,而 Object.defineProperty 还需要遍历所有属性,然后逐级向下访问。proxy 返回的是一个新对象,可以直接操作新对象而达到目标。而 Object.defineProperty 操作的是原对象,只能遍历对象属性然后对其直接修改。proxy 有 13 种拦截方法,不限于 apply、ownKeys、deleteProperty 等,而 Object.defineporperty 不具备。

2> defineProperty 无法监听对象新增属性以及无法跟踪数组索引以及数组 length 的问题,proxy 正好解决了该问题。

在 vue2 中,我们给对象新增一个属性时,如果新增属性的值发生改变的时候,我们发现视图并没有更新,因为新增属性是无法监听到的。同样的,通过下标直接改变数组,视图也是无法更新的,也是因为监听不到。

在 vue3 中新增 proxy ,解决了这些问题。

 vue2和vue3生命周期的变化

Vue2-------Vue3

beforeCreate   -> setup() 开始创建组件之前,创建的是data和method

created        -> setup()

beforeMount    -> onBeforeMount 组件挂载到节点上之前执行的函数。

mounted        -> onMounted 组件挂载完成后执行的函数

beforeUpdate  -> onBeforeUpdate 组件更新之前执行的函数。

updated        -> onUpdated 组件更新完成之后执行的函数。

beforeDestroy  -> onBeforeUnmount 组件挂载到节点上之前执行的函数。

destroyed      -> onUnmounted 组件卸载之前执行的函数

activated      -> onActivated 组件卸载完成后执行的函数

deactivated    -> onDeactivated

注意:如果想要在Vue中获取dom节点在created中使用this.$nexttick

Vue2和Vue3的diff算法

Vue2:

Vue2 diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom;

Vue2 diff算法会比较每一个vnode,而对于一些不参与更新的元素,进行比较是有点消耗性能的;

Vue3:

Vue3 diff算法在初始化的时候会给每个虚拟节点添加一个patchFlags,patchFlags就是优化的标识。只会比较patchFlags发生变化的vnode,进行更新视图,对于没有变化的元素做静态标记,在渲染的时候直接复用;

V-if和v-for的优先级

Vue2:我们最好不要把v-if和v-for同时用在一个元素上,这样会带来性能的浪费(每次都要先渲染才会进行条件判断) image.png Vue3:

image.png Vue中会给我们报警告:

意思是:属性“index”在渲染期间被访问,但未在实例上定义v-if先进行判断,

但是这个时候v-for还没有渲染,所以index是找不到的

重绘和回流

回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
重绘:当计算好盒模型的位置,大小及其它属性后,浏览器根据每个盒子特性进行绘制
重绘不一定会引起回流,但回流一定会触发重绘

深拷贝,浅拷贝

我觉得深拷贝和浅拷贝就是对一个数据类型的操作吧,因为在工作的过程中,我们经常要去操作一些复杂数据类型,当我们对一个数据类型拷贝出一个副本来,修改了副本之后,原数据对象发送改变,就是浅拷贝,原来的数据类型不发生改变,就是深拷贝.其实这个地方就是涉及一个堆栈内存的问题.因为引用数据类型是存储在堆内存里面的,保存的是一个内存地址的指针指向..浅拷贝就是指针指向相同.所以修改了其中一个,另一个也是访问的堆内存里面的相同位置.所以都会发生变换.深拷贝相当于把里面的引用类型,重新在堆内存里面开辟了一块空间.有了新的一个地址指针的指向.就相互不影响了.项目里面的时候,我经常使用 JSON.parse(JSON.stringify(深拷贝的对象))的方式来实现吧.也可以用一个递归的方式.来实现,用的多的话.封装成一个函数,放到utils.js 中,在引用的地方导入调用.

说一下你对 http 状态码的了解多少?

1xx(临时响应)
表示临时响应并需要请求者继续执行操作的状态代码
2xx (成功) 表示成功处理了请求的状态码。
常见的 2 开头的状态码有:
200 服务器成功返回网页
3xx (重定向)
表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向
常见的 3 字开头的状态码有:
301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应时,会自动将请求者转到新位置。
302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。
常见的 4 字开头的状态有:
404 请求的网页不存在
401 没权限吧,当时我们项目里面的时候token值过期就是返回401,我在响应拦截器做了处理
5xx(服务器错误) 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
常见的以5开头的状态码有:
500 (服务器内部错误) 服务器遇到错误,无法完成请求。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。

说一下 JS 作用域和作用域链

答:JS 作用域也就是 JS 识别变量的范围,作用域链也就是 JS 查找变量的顺序先说作用域,JS 作用域主要包括全局作用域、局部作用域和 ES6 的块级作用域
全局作用域:也就是定义在 window 下的变量范围,在任何地方都可以访问,
局部作用域:是只在函数内部定义的变量范围
块级作用域:简单来说用 let 和 const 在任意的代码块中定义的变量都认为是块级作用域中的变量,例如在 for 循环中用 let 定义的变量,在 if 语句中用 let 定义的变量等等
注:尽量不要使用全局变量,因为容易导致全局的污染,命名冲突,对 bug查找不利。
2 而所谓的作用域链就是由最内部的作用域往最外部,查找变量的过程.形成的链条就是作用域链
原型链往外层找,找到 null
作用域链,从找到的地方外外层,一直找到 window

JS 设计模式有哪些(单例模式观察者模式等)

答:JS 设计模式有很多,但我知道的有单例模式,观察者模式
单例模式:就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在 JavaScript 里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。
观察者模式: 观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。
总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化

虚拟 dom 的理解

我大体说下对虚拟 dom 的理解吧,正是 virtual(喂车额奥) DOM 这一个特性,让现代这些流行的框架,能够性能更高,才得以市场率占有这么高的.比方说,vue react阿瓦隆.这些框架都是虚拟 dom 的.在传统的 jq 中,操作的都是真实的 DOM,.而一个真实 dom 的渲染过程,要经过渲染引擎构建 DOM 树.构建样式表.组建成 render(渲染)树,的过程,要经过不断的重绘回流才能够展示给用户.那么在直接 js 操作 dom 的过程中,比方说一个循环 10 次插入 dom 元素,其实每一次都会经历上面的过程..经历大量的重绘回流.代价特别大.性能低下.所以出现了虚拟 dom虚拟 dom 其实就是提前使用 js 的方式表示出 dom 结构树来.存储在内存里面.同样的循环.只会最终合并执行一次,大大的提高了性能.(这个地方有点儿像 js 中的createElementFragment 文档碎片)而在对比的过程中.通过 diff 算法进行比较差异.这个比较在我理解而言就是同层比较.降低了时间复杂度空间复杂度一些什么玩意儿.最终把差异同步到真实 dom上去.这就是我理解的虚拟 dom

事件循环机制 EventLoop

这个东西我研究过.是 js的一个底层运行原理,js是单线程的,但是也有一些耗时任务,会影响执行效率.代码都在主线程中执行,当遇见你像ajax请求.setTimeout 定时器时候,会单独开启异步线程.异步线程耗时之后会推入异步队列中等待执行.然后当主线程执行完毕之后.会到异步队列中取出到主线程中执行.然后再去异步队列中取第二个.这个来回取的过程就是您所说的事件循环(eventLoop)吧

微任务宏任务

问题一般是.promise 和 setTimeout 谁先执行
这里面的执行.是牵扯到一个微任务宏任务的执行顺序的问题.你像 setTimeoutsetInterval 都是宏任务的.promise.then.catch 是微任务的,当这个任务执行的时候,会先执行宏任务里面的代码,然后在执行宏任务中微任务的代码.只有当前宏任务执行完毕,才会去执行下一个宏任务.我就是这样理解的.

说一下 vue 最大特点是什么或者说 vue 核心是什么

答:vue 最大特点我感觉就是“组件化“和”数据驱动”
组件化就是可以将页面和页面中可复用的元素都看做成组件,写页面的过程,就是写组件,然后页面是由这些组件“拼接“起来的组件树
数据驱动就是让我们只关注数据层,只要数据变化,页面(即视图层)会自动更新,至于如何操作 dom,完全交由 vue 去完成,咱们只关注数据,数据变了,页面自动同步变化了,很方便

说一下 vue 常用基本指令有哪些

v-if:根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。
v-show:根据表达式之真假值,切换元素的 display CSS 属性。
v-for:循环指令,基于一个数组或者对象渲染一个列表,vue 2.0 以上必须需 配合 key 值 使用。
v-bind:动态地绑定一个或多个特性,或一个组件 prop 到表达式。
v-on:用于监听指定元素的 DOM 事件,比如点击事件。绑定事件监听器。
v-model:实现表单输入和应用状态之间的双向绑定
v-pre:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
v-once:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子 节点将被视为静态内容并跳过。这可以用于优化更新性能。

Vue 常用的修饰符

v-on 指令常用修饰符:

stop - 调用 event.stopPropagation(),禁止事件冒泡。
prevent - 调用 event.preventDefault(),阻止事件默认行为。
capture - 添加事件侦听器时使用 capture 模式。
self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
native - 监听组件根元素的原生事件。
once - 只触发一次回调。
left - (2.2.0) 只当点击鼠标左键时触发。
right - (2.2.0) 只当点击鼠标右键时触发。
middle - (2.2.0) 只当点击鼠标中键时触发。
passive - (2.3.0) 以 { passive: true } 模式添加侦听器 注意: 如果是在自己封装的组件或者是使用一些第三方的 UI 库时,会发现并不起效果,这时就需要用·.native 修饰符了,
如://使用示例:

<el-input
v-model="inputName"
placeholder="搜索你的文件"
@keyup.enter.native="searchFile(params)"
>
</el-input>

v-bind 指令常用修饰符:

prop - 被用于绑定 DOM 属性 (property)。(差别在哪里?)
camel - (2.1.0+) 将 kebab-case 特性名转换为 camelCase. (从 2.1.0 开 始支持)
sync (2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器。

v-model 指令常用修饰符:

lazy - 取代 input 监听 change 事件
number - 输入字符串转为数字
trim - 输入首尾空格过滤

Vue 组件中 data 为什么必须是函数

因为一个组件是可以复用的,但他们的 data 应该是私有的,如果在里面直接返回一 个对象.而不是函数的话,在组件复用的时候,就会影响其他的组件 而我们以函数的方式 返回,就是一个新的 data 对象,在堆内存里面指向的就是不同的空间地址.就不会影响 了.

说一下 v-if 和 v-show 的区别

v-ifv-show都可以显示和隐藏一个元素,但有本质区别
v-if: 是惰性的,只是值为 false 就不会加载对应元素,为 true 才动态加载对应元素
v-show:是无论为 true 和为 false 都会加载对应 html 代码,但为 false时用 display:none 隐藏不在页面显示,但为 true 时页面上用 display:block显示其效果
适用场景:切换频繁的场合用 v-show,切换不频繁的场合用 v-if

说一下 vue 组件通讯(即传值)

第一种:父传子:主要通过 props 来实现的
具体实现:父组件通过 import 引入子组件,并注册,在子组件标签上添加要传递的属性,子组件通过 props 接收,接收有两种形式一是通过数组形式[‘要接收的属性’ ],二是通过对象形式{ }来接收,对象形式可以设置要传递的数据类型和默认值,而数组只是简单的接收
第二种:子传父:主要通过emit 来实现
具体实现:子组件通过通过绑定事件触发函数,在其中设置this.emit(‘要派发的自定义事件’,要传递的值),emit 中有两个参数一是要派发的自定义事件,第二个参数是要传递的值然后父组件中,在这个子组件身上@派发的自定义事件,绑定事件触发的methods 中的方法接受的默认值,就是传递过来的参数
第三种:兄弟之间传值有两种方法:
方法一:通过 event bus 实现具体实现:创建一个空的 vue 并暴露出去,这个作为公共的 bus,即当作两 个组件的桥梁,在两个兄弟组件中分别引入刚才创建的 bus,在组件 A 中通过 bus.emit(’自定义事件名’,要发送的值)发送数据,在组件B中通过bus.on (‘自定义事件名‘,function(v) { //v 即为要接收的值 })接收数据
方法二:通过 vuex 实现具体实现:vuex 是一个状态管理工具,主要解决大中型复杂项目的数据 共享问题,主要包括 state,actions,mutations,getters和 modules 5个要素, 主要流程:组件通过 dispatch 到 actions,actions 是异步操作,再 actions 中通过 commit 到 mutations,mutations 再通过逻辑操作改变 state,从而同 步到组件,更新其数据状态

说一下 vue 开发环境和线上环境如何切换

主 要 通 过 检 测 process.env.NODE_ENV===”production”和 process.env.NODE_ENV===”development”环境,来设置线上和线下环境地址, 从而实现线上和线下环境地址的切换

说一下 vue 中 methods,computed,watch 的区别

methods 中都是封装好的函数,无论是否有变化只要触发就会执行 computed:是vue独有的特性计算属性,可以对data中的依赖项再重新计算,得到一个新值,应用到视图中,和 methods 本质区别是 computed 是可缓存的,也就是说computed 中的依赖项没有变化,则computed中的值就不会重新计算,而 methods 中的函数是没有缓存的。Watch 是监听 data 和计算属性中的新旧变化。

vue 如何动态添加属性,实现数据响应?

vue 主要通过用 this.$set(对象,‘属性‘,值)实现动态添加属性,以实现数据的响应注意是添加,我记忆中如果是修改引用类型属性的值,是可以自动渲染的.

说一下 vue 和 jquey 的区别

答:jquery 主要是玩 dom 操作的“神器“,强大的选择器,封装了好多好用的dom 操作方法和如何获取 ajax 方法 例如:$.ajax()非常好用
vue:主要用于数据驱动和组件化,很少操作 dom,当然 vue 可能通过 ref来选择一个 dom 或组件

说一下 nextTick 的作用和使用场景

vue 中的 nextTick 主要用于处理数据动态变化后,DOM 还未及时更新的问 题,用 nextTick 就可以获取数据更新后最新 DOM 的变化

vue 中 keep-alive 组件的作用

keep-alive:主要用于保留组件状态或避免重新渲染。
比如: 有一个列表页面和一个 详情页面,那么用户就会经常执行打开详情=>返回列 表=>打开详情这样的话 列表 和 详情 都是一个频率很高的页面,那么就可以对列表 组件使用进行缓存,这样用户每次返回列表的时候,都 能从缓存中快速渲染,而不是重新渲染。
1、属性: include:字符串或正则表达式。只有匹配的组件会被缓存。 exclude:字符串或正则表达式。任何匹配的组件都不会被缓存。
2、用法: 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 相 似,是一个抽象组件:它自身不会渲染一 DOM 元素,也不会出现在父 组件链中。

VUE 中的动态组件

你说的应该是那个 vue 里面的内置标签 component 吧 在 vue 里面的时候.有一个内置标签 component 可以配合自身的 is 属性来进行动态组件的渲染.就是 is 属性的值可以是一个变量.是哪一个组件的名字就去加载对应的组件
Tips:发表自己的一点儿见解:这个东西其实和 slot 插槽有点儿想.它是 is 属性符合加载对应组件.slot 插槽是 name 值符合加载传递到组件里面的 dom 结构[注意:如果往这儿引导,那么插槽的匿名具名作用域插槽都要熟悉] 另外可以结合项目说:
当时我开发的移动端的项目.后台首页的时候,把组件都拆解开了.返回了对应的组件名字,以及组件需要的数据,我就是用了一个动态组件的方式.在当前页面同级见了个components 文件夹.把返回的组件名字写了一遍.导入.然后使用循环的方式.遍历这个component 标签.让 is 属性的值是后台返回的组建名.这样我感觉就是让页面结构显得更加简洁清晰吧.

Vue 中的动态侧边栏路由如何实现的

在登录成功后,我记得后端当时给我返了一个 token 值.然后呢我拿着 token 值中导航守卫里面.去请求了一个动态路由表.因为后端返回的动态路由表是一个路由字符串.我这边没法解析.然后我当时递归遍历了一下.相当于把里面的字符串类型的组件地址,变成了一个箭头函数方式引入的.因为有子路由.所以只能递归处理.其实最后就是把后端返回的路由字符串.变成了一个路由对象.最后通过路由的一个 addRouters 方法.来添加到基础路由表里面去.这样.渲染出来的路由,就是一个动态的.可以被控制的. Tips:如果问到了.beforeEach 里面怎么配置的.或者是.导航守卫里面配置,每次都请求路由表,递归遍历.性能不好一类的问题:其实当时我还让后端那边提供了一个查询角色信息的接口,中导航守卫里面,如果有 token 值了.回去判断有没有角色,有角色了正常走next()没有的话,请求角色,顺便拿着角色请求的侧边栏路由表.那么下次尽量判断,就不走请求路由表了

项目中的按钮权限是如何来进行控制的?

当时我是中项目中创建了一个 directives 文件夹.在这里面通过 Vue.directive 创建了一个控制按钮是否显示的指令,其实就是中登录的时候,后端会通过接口返回一个,当前用户具有的所有按钮的权限标识数组;然后我这边.把自定义指令定义中每一个按钮上.传上一个当前按钮的权限标识. 然后中指令的 inserted 钩子函数里面,来判断这个权限标识是否中后端返回的标识数组中,如果在就操作按钮正常展示.不在就隐藏.因为这个钩子函数默认传入了我记得像当前元素 el .binding vnode 这些东西吧.就是直接操作

说一下对 MVVM 的理解

现在来看的话我感觉,主流的框架都是 MVVM 模式的,比方说我项目中经常用的 vue.
MVVM 框架里面 M 的话就是数据层 V 就是 View 视图层.中间通过一个(viewmodel)进行了一个双向绑定.能进行 dom 监听和数据绑定.里面是通过 Object.defineProperty 对当前组件的中 data 的所有属性添加了 get 和 set 方法,这样的话.形成了数据驱动视图.而且视图层和数据层就相互分离,便于维护了.只要修改了数据层视图层就发生变化.如果不变的话,可以使用 this.$set 方法来进行.

什么是 BFC

它就是一个 block format content 块级格式化上下文.是一个布局里面的概念.把一个盒子 设置成 bfc 之后,里面无论怎么布局.都不会影响外面的变动.另外如果是一个 bfc 盒子.浮 动的元素也会参数计算.用它可以解决一些布局方面的问题吧.比方说.margin 重叠.高度塌 陷等. overflow:hidden.float. display:inline-block

说一下什么情况下会产生跨域及产生跨域的解决方案和实现原理

产生跨域的情况有:不同协议,不同域名,不同端口以及域名和 ip 地址的访 问都会产生跨域。

跨域的解决方案目前有三种主流解决方案:

跨域是浏览器做出的限制,和后端没关系

一、 是 jsonp
jsonp 实现原理:主要是利用动态创建 script 标签请求后端接口地址,然后传递 callback 参数,后端接收 callback,后端经过数据处理,返回 callback 函数调用的形式,callback 中的参数就是 json
二、 通过代理的方式(前端代理和后端代理) 前端代理我在 vue 中使用那个 vue.config.js 里面配置一个proxy,里面有个 target 属性指向跨域链接.修改完重启项目就可以了.实际上就是启动了一个代理服务器.绕开同源策略,在请求的时候,通过代理服务器获取到数据再转给浏览器
三、 是 CORS
CORS 全称叫跨域资源共享,主要是后台工程师设置后端代码来达到前端跨域请求的注:现在主流框架都是用代理和 CORS 跨域实现的

说一下git多人操作同一个文件,如果出现冲突该如何解决

当遇到多人协作修改同一个文件时出现冲突,我先将远程文件先 git pull 下来,手动修改冲突代码后,再 git add ,git commit,git push 再上传到远程 仓库。如果 pull 也 pull 不下来提示冲突的话,可以先通过 git stash 暂存下 来,然后再 pull 拉取,然后 git stash pop,取出原来写的,手动修改,然 后提交