2021 前端 VUE 面试题总汇

阅读 45

2022-02-23

文章目录

1、vue的生命周期

2、Vue2.x 双向绑定原理

vue 是通过 数据劫持 结合 发布者-订阅者 来实现双向绑定;数据劫持通过Object.defineProperty()方法的get和set方法来重新定义,set是设置属性值触发的函数,get是读取属性值触发的函数;

底层实现:

详细过程参考

3、Vue3.x 响应式原理

Vue3.x改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。

Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?

判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。

监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?

我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。

4、v-for 为什么要加上 key

key 的主要功能是提高 vdom 的更新速度;因为 vue 在 patch (补丁)过程中的 diff 算法对新旧节点比对时是可以通过 key 精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使 patch 过程更高效。(源码位置:src/core/vdom/patch.js)
尽量避免使用index最为key值。

function sameVnode (a, b) {
  return (
    a.key === b.key && (
      (
        a.tag === b.tag &&
        a.isComment === b.isComment &&
        isDef(a.data) === isDef(b.data) &&
        sameInputType(a, b)
      ) || (
        isTrue(a.isAsyncPlaceholder) &&
        a.asyncFactory === b.asyncFactory &&
        isUndef(b.asyncFactory.error)
      )
    )
  )
}

5、Vdom的理解

vdom 就是用 js 对象来描述真实 DOM,虚拟DOM的实现就是普通对象包含tag、data、children等属性对真实节点的描述。(本质上就是在JS和DOM之间的一个缓存);由于直接操作 DOM 性能低,但是 js 层的操作效率高,可以将 DOM 操作转化成对象操作,最终通过diff算法比对差异进行更新DOM (减少了对真实DOM的操作)。虚拟DOM不依赖真实平台环境从而也可以实现跨平台。

优点:

缺点:

6、vuex 的结构,以及 actiion 和 mutation 的区别

结构:state(初始化数据)、action(异步处理数据)、mutation(唯一能够修改state的操作)、getter(从state中动态获取相关数据)、module(模块化)

区别:

7、axios 的原理以及优点,如何终止 axios 请求

原理:属于 XMLHttpRequest,是一个基于 Promise 的 http 请求库,可用于浏览器和 Node

优点:

中断请求:使用 CancelToken 函数
1)、使用 cancelToken.sourse 工厂方法创建 cancel token

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
	cancelToken: source.token
}).catch(function(thrown) {
	if (axios.isCancel(thrown)) {
		console.log('Request canceled', thrown.message);
	}else {
		// 处理错误
	}
});
source.cancel('xxx');

2)、通过传递一个 executor 函数到CancelToken构造函数创建cancel token

const CancelToken = axios.CancelToken;
let cancel
axios.get('/user/12345', {
	cancelToken: new CancelToken(function executor(c){
		cancel = c
	})
});
cancel();

注意: executor 处理器函数接收两个函数 resolve 和 reject 作为参数,异步任务执行成功调用 resolve,否则调用reject。

8、vue 的缺点

9、vue 和 react 区别

相同:

不同:

10、computed 和 watch 区别,以及 computed 如何实现缓存和更新的

区别

watch的参数:
deep:深度监听
immediate :组件加载立即触发回调函数执行

computed缓存原理:
conputed本质是一个惰性的观察者;当计算数据存在于 data 或者 props里时会被警告;

vue初次运行会对 computed 属性做初始化处理(initComputed),初始化的时候会对每一个 computed 属性用 watcher 包装起来 ,这里面会生成一个 dirty 属性值为 true;然后执行 defineComputed 函数来计算,计算之后会将 dirty 值变为 false,这里会根据 dirty 值来判断是否需要重新计算;如果属性依赖的数据发生变化,computed 的 watcher 会把 dirty 变为 true,这样就会重新计算 computed 属性的值。

11、hash 和 history 区别 ,分别是怎么实现的

区别:

原理:
hash:通过 onhashchange 事件监听 hash 变化,然后根据 hash 的变化更新页面部分内容(hash变化不会触发浏览器请求,但是会触发 hashchange 事件)。
history:主要是 H5 新增的两个 API(pushState、replaceState);他们可以改变url,但是不会发送请求,这样就可以通过 onpopstate 监听 url 变化来实现页面部分内容更新。

补充:pushState、replaceState 这两个方法应用于浏览器的历史记录栈,在当前已有的back、forward、go的基础上,他们提供了对当前浏览器进行修改的功能,只是当它们被修改时,虽然浏览器的URL发生

12、nextTick 作用和实现

作用:vue 更新 dom 是异步更新的,数据变化,dom 的更新不会马上完成;nextTick 的回调是在下次 DOM 更新循环结束之后执行的延迟回调。

实现:(源码:src/core/util/next-tick.js)
原理是使用异步方法处理任务,vue 会根据当前环境优先使用 promise.then、MutationObserver 、setImmediate,如果都不支持就使用 setTimeout 把函数延迟到 DOM 更新之后再使用。(原因是宏任务消耗大于微任务,优先使用微任务,最后使用消耗最大的宏任务)

13、solt 插槽种类以及原理

种类:
1)、匿名插槽:只能有一个
2)、实名插槽:可以有多个,在使用时必须使用name属性来标识

原理:

3)、作用域插槽:父组件获取子组 件solt 中携带的数据
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 元素的一个 attribute 绑定上去:

//子组件
<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

绑定在 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:

//父组件
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。

原理:

本题参考

14、keep-alive使用和原理

keep-alive 主要是组件缓存,采用的是LRU算法。
常用的两个属性 include(要缓存的)/ exclude(不要缓存的),允许组件有条件的进行缓存;都可以用逗号分隔字符串、正则表达式或一个数组来表示
因为keep-alive会将组件保存在内存中,并不会销毁以及重新创建,所以不会重新调用组件的created等方法,所以它自己有两个生命周期activated / deactivated,用来得知当前组件是否处于活跃状态。

缓存实现过程:

15、MVVM 和 MVC

1)、MVVM 即 Model-View-ViewModel 的简写。即模型-视图-视图模型。模型(Model)指的是后端传递的数据。视图(View)指的是所看到的页面。视图模型(ViewModel)是 mvvm 模式的核心,它是连接 view 和 model 的桥梁。它有两个方向:一是将模型(Model)转化成视图(View),即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将视图(View)转化成模型(Model),即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定。

2)、MVC 是 Model-View- Controller 的简写。即模型-视图-控制器。M 和 V 指的意思和 MVVM 中的 M 和 V 意思一样。C 即 Controller 指的是页面业务逻辑。使用 MVC 的目的就是将 M 和 V 的代码分离。MVC 是单向通信。也就是 View 跟 Model,必须通过 Controller 来承上启下。MVC和MVVM的区别并不是VM完全取代了C,只是在MVC的基础上增加了一层VM,只不过是弱化了C的概念,ViewModel 存在目的在于抽离 Controller 中展示的业务逻辑,而不是替代 Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用,使开发更高效,结构更清晰,增加代码的复用性。

可以参考阮一峰的文章很详细

16、template 模板引擎的渲染过程

template 的作用是模板占位符,可帮助我们包裹元素,但在循环过程当中,template不会被渲染到页面上

在 vue 实例初始化 $mount 的时候,先调用 render 函数如果 render 函数不存在,则调用 template 进行编译得到 render 函数。如果没有 template 则会调用 el 来获取 template。
 
渲染过程:

17、SPA页面的理解

SPA (single-page application:单页面应用程序)在页面初始化的时候加载相应的 HTML、js、css,一旦加载完成,页面不会因为用户的操作进行页面的重新加载或者跳转,而是使用路由机制实现HTML内容转换,避免页面重新加载。

优点:

缺点:

18、vue 性能优化?

1)、去掉编译文件中的 map 文件
map文件主要是帮我们线上调试代码、查看样式的,通常线上不需要这些文件;找到 config/index.js 文件,将 productionSourceMap 设置为 false。

2)、开启 gzip 打包压缩 && 后台配合 gzip 访问
首先安装依赖 compression-webpack-plugin ,推荐使用 1.1.11 版本;找到 config/index.js 文件,将 productionGzip 设置为 true;

npm install --save-dev compression-webpack-plugin@1.1.11

在nginx.conf 里面配置

http {  //在 http中配置如下代码,
   gzip on;
   gzip_disable "msie6"; 
   gzip_vary on; 
   gzip_proxied any;
   gzip_comp_level 8; #压缩级别
   gzip_buffers 16 8k;
   #gzip_http_version 1.1;
   gzip_min_length 100; #不压缩临界值
   gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript 		 application/x-httpd-php image/jpeg image/gif image/png;
 }

3)、路由懒加载
组件的延迟加载,可以把页面资源划分为多份,用到的时候才会按需加载,这样减少第一次加载的消耗。

4)、使用 CDN 引入第三方库减少服务器压力
首先、在index.html 文件中添加 CDN 相关的代码,以vue、vue-router、vuex 为例

  <body>
    <div id="app"></div>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
    <script src="https://cdn.bootcss.com/vue-router/3.0.4/vue-router.min.js"></script>
  </body>

在 vue.config.js(vue-cli3) 文件 ,build/webpack.base.conf.js(vue-cli2)文件,增加 externals,将引入的外部模块导入

module.exports = {
entry: {
app: './src/main.js'
},
externals:{
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex':'Vuex'
}

最后、去掉 inport 以及 Vue.use(xxx)

//import Vue from 'vue'
//import Router from 'vue-router'
//import Vuex from 'vuex'
//Vue.use(Router)
///Vue.use(Vuex)

好处:加快打包速度,减轻服务器访问压力。

5)、尽量使用轻量级的工具库
moment是处理时间的标杆,但是它过于庞大,我们可以使用day.js替代

6)、对于短时间的大量操作(缩放、滚动)
使用防抖、节流函数

7)、代码精简,去除 console ,可复用的方法、组件提取出来

8)、不要写行内样式,避免dom重绘

9)、按需引入
比如我们只使用 elementUI 里面的部分组件,我们只引入需要用到的组件

10)、SPA页面采用keep-alive缓存组件

11)、key保证唯一性

12)、v-if 当值为false时内部指令不会执行,具有阻断功能,很多情况下使用v-if替代v-show

13、服务端渲染ssr

19、父子组件加载顺序

1)、加载过程:父beforeCreate->父cerated->父beforeMount->子beforeCreate->子cerated->子beforeMount>子mounted->父mounted
2)、子组件更新:父beforeUpdate->子beforeUpdate->子updated->父updated
3)、父组件更新:父beforeUpdate->父updated
4)、销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

20、Vue.use是做什么的,原理是什么

作用:用来注册使用插件或者组件的方法。
原理:

21、Proxy 与 Object.defineProperty 优劣对比

Proxy 的优势如下:
1)Proxy 可以直接监听对象而非属性;
2)Proxy 可以直接监听数组的变化;
3)Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
4)Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
5)Proxy 作为新标准将受到浏览器厂商重点持续的性能优化;

Object.defineProperty 的优势如下:
兼容性好,支持 IE9,而Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

22、v-model 的原理

v-model本质就是一个语法糖,可以看成是value + input方法的语法糖,是 vue 的双向绑定指令。一方面 model 层通过 defineProperty 来劫持每个属性,一旦监听到变化通过相关的页面元素更新。另一方面通过编译模板文件,为控件的 v-model 绑定 input 事件,从而页面输入能实时更新相关 data 属性值。

可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性。

v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
1)text 和 textarea 元素使用 value 属性和 input 事件;
2)checkbox 和 radio 使用 checked 属性和 change 事件;
3)select 字段将 value 作为 prop 并将 change 作为事件。

23、v-if 和 v-show 的区别

24、v-for 和 v-if 优先级

25、Vue.mixin 使用场景和原理

Vue.mixin的作用就是抽离公共的业务逻辑,原理类似“对象的继承”,在使用 mixin 的组件中引入后,mixin 中的方法和属性也就并入到该组件中,可以直接使用;当组件初始化时会调用 mergeOptions 方法进行合并,采用策略模式针对不同的属性进行合并,如果混入的数据和本身组件中的数据冲突,会采用“就近原则”以组件的数据为准。

//全局引用 main.js
import mixin from './mixin'
Vue.mixin(mixin)

//在vue文件中引用
import '../mixin'; // 引入mixin文件
export default {
   mixins: [mixin]
}

同名选项合并:

26、Vue.set

起因:因为 ES5 的限制,vue 无法检测到对象属性的添加和删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。

用法:Vue.set(target, key, value)

执行流程:

Vue.set( ) 是将 set 函数绑定在 Vue 构造函数上,this.$set() 是将 set 函数绑定在 Vue原型上。

27、组件中写 name 选项有哪些好处

28、vue 组件通讯有哪些方法

1)、props / $emit 适用 父子组件通信(常规操作)

2)、ref 与 $parent / $children 适用 父子组件通信

3)、EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信

4)、$ attrs/$listeners 适用于 隔代组件通信

简单来说:$ attrs与$ listeners 是两个对象,$ attrs 里存放的是父组件上绑定的非 Props 属性,$ listeners里存放的是父组件中绑定的非原生事件。

5)、provide / inject 适用于 隔代组件通信

6)、Vuex 适用于 父子、隔代、兄弟组件通信

29、vue 组件的data 为什么是一个函数

组件是可复用的,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的 data 数据都应该是相互隔离,互不影响的,基于这一理念,组件每复用一次,data 数据就应该被复制一次,之后,当某一处复用的地方组件内 data 数据被改变时,其他复用地方组件的 data 数据不受影响.

组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份 data,就会造成一个变了全都会变的结果。(对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题)

30、data 里面数据量比较大如何优化

vue 把 data、props、store 等数据做成响应式,也就是会对这些响应式数据做深度监听,给每一个object类型的key(包括嵌套object)添加observer(vue3使用proxy)。所以如果我们不需要数据是响应式的,可以在.vue 文件头部直接使用 let、const 定义变量,在组件销毁的时候将该这些变量设为null。

31、子组件里面可以修改父组件的值吗

答案是传递的是对象和数组可以修改,如果是基础数据类型也可以修改,但是控制台会报错;对象和数组修改之后父组件是可以监听到这个值的变化的。那么为什么呢?

那么基础类型要怎么修改呢?
1)、可以使用.sync 修饰符来修改。参考:vue 父子组件双向绑定传值的实现
2)、子组件用 data 或者 computed 重新定义一个变量,然后修改新定义的变量。

32、生命周期钩子是如何实现的

Vue的生命周期钩子就是回调函数而已,当创建组件实例的过程中会调用对应的钩子方法。

内部主要是使用callHook方法来调用对应的方法。核心是一个发布订阅模式,将钩子订阅好(内部采用数组的方式存储),在对应的阶段进行发布。

33、什么是 vue-loader

作用:解析和转换.vue文件。提取出其中的逻辑代码 script,样式代码style,以及HTML 模板template,再分别把他们交给对应的loader去处理

用途:js可以写es6,style样式可以写scss或less

特性:

css-loader:加载由vue-loader提取出的CSS代码
vue-template-compiler:把vue-loader提取出的HTML模板编译成可执行的javascript代码

34、vue 是怎么检测数组的变化的

流程:

35、vue 是怎样依赖收集的?(dep 和 Watcher 是什么关系)

注意:Dep 是一个用来负责收集 Watcher 的类,Watcher 是一个封装了渲染视图逻辑的类,用于派发更新的。Watcher 是不能直接更新视图的还需要结合 vdom 经过 patch() 中的 diff 算法才可以生成真正的 DOM

36、vue路由的钩子函数(导航守卫)

全局的路由钩子函数:beforeEach、afterEach(一般用于全局进行权限跳转)
单个的路由钩子函数:beforeEnter、beforeLeave(路由内部钩子,一般在路由表里)
组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate

37、vue $route 和 $router 的区别

38、vue 中的 diff 算法原理

diff 算法的原理是递归加上遍历节点来比较节点复用性。diff 算法通过对比虚拟树的差异,将差异通过 patch (补丁)一边比较一边打补丁方式更新到真实 dom 节点上。patch 函数是 diff 流程的入口函数。在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。

在数据发生变化的时候,set 方法就会调用 dep.notify 通知所有订阅者,订阅者会调用 patch 给真实的 dom 打补丁,更新相应的视图。

更详细内容参考

39、Vue. extend 能做什么

作是扩展组件成个构造器,通常会与 $mount 起使。

<div id="mount-point"></div>


// 创建构造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')

这样div里面就有了下面内容

<p>Walter White aka Heisenberg</p>

40、vue 的 mixin 和 mixins 区别

41、vue 组件渲染和更新的过程

渲染:

更新:

42、vue 为什么采用异步渲染

因为如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染;所以为了性能考虑,Vue会在本轮数据更新后,再去异步更新视图。

原理:

43、vue 为什么要使用异步组件

组件功能多打包出的结果会变大,我可以采用异步的方式来加载组件。
异步组件的核心是把组件定义成一个函数,主要依赖import()这个语法,可以实现文件的分割加载。

原理:在createComponent方法中,会有相应的异步组件处理

44、vue 如何快速定位那个组件出现性能问题的

timeline 具。 通过 timeline 来查看每个函数的调时常,定位出哪个函数的问题,从能判断哪个组件出了问题

45、vue3.x 对比 vue2.x 变化

https://blog.csdn.net/r657225738/article/details/115551368

46、watch 的 deep 如何实现的

watch 在监听对象的时候,如果 deep 值是 true,就会执行 traverse 这个方法,这个方法里就是做了个数组递归,如果是数组的话,会根据数组的每一项索引取值,进行递归追加依赖,如果是对象会拿 key 进行遍历取值,进行递归追加依赖,traverse 就是deep:true实现的核心。这样就会把数组或者对象的每一个属性都追加依赖进行监听,只要依赖发生变化就会通知视图更新。

47、v-html 会导致那些问题

48、v-el作用

提供一个在页面上已存在的 DOM元素作为 Vue实例的挂载目标.可以是 CSS 选择器,也可以是一个 HTMLElement 实例,

48、说说vue的动态组件

动态组件就是几个组件放在一个挂载点下,然后根据父组件的某个变量来决定显示哪个,或者都不显示。
在挂载点使用 component 标签,然后使用 is =“组件名”,它会自动去找匹配的组件名,如果有,则显示;

<div id="app">
    <component is="one"></component>
</div>

new Vue({
    el: '#app',
    components: {
        one: {template: '<div>我是线路一</div>'},
        two: {template: '<div>我是线路二</div>'},
        thr: {template: '<div>我是线路三</div>'}
    }
})

49、怎么定义vue-router的动态路由?怎么获取传过来的值?

可以有两种方式传递参数:params、query
1)、params:在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的 params.id 获取。

//index.js
path:'/user/:id',
compunent: user


// 方法1:
<router-link :to="{ name: 'users', params: { id: 123}}">按钮</router-link>
// 方法2:
this.$router.push({name:'users',params:{id:123}})
// 方法3:
this.$router.push('/user/' + 123)

2)、query:不需要配置路由格式,使用 router 对象的 query.id 获取。

// 方法1:
<router-link :to="{ name: 'users', query: { id: 123}}">按钮</router-link>
// 方法2:
this.$router.push({ name: 'users', query:{ id:123}})
// 方法3:
<router-link :to="{ path: '/user', query: {  id:123 }}">按钮</router-link>
// 方法4:
this.$router.push({ path: '/user', query:{  id:123 }})
// 方法5:
this.$router.push('/user?uname=' + 123)

50、自定义指令

自定义指令的生命周期,有5个事件钩子,可以设置指令在某一个事件发生时的具体行为:

钩子函数的参数 (包括 el,binding,vnode,oldVnode)

51、自定义过滤器

参考:vue 全局注册过滤器 filter

52、vue 中 extend、mixins、extends的区别

1)、extend:使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件对象。

2)、mixins:可以混入多个,当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

var vm = new Vue({
  created: function () { console.log(3) },
  mixins: [mixinA ,mixinB ]
})

3)、extends:声明扩展另一个组件(可以是一个简单的选项对象或构造函数)无需使用 Vue.extend。这主要是为了便于扩展单文件组件。和 mixins 类似。但extends只能继承一个。

var otherComp= { ... }
// 继承 otherComp
var comp = {
  extends: otherComp,
  ...
}

总结:

以上是我总结的 VUE 相关的知识点,还会继续更新,如有错误或者遗漏的地方欢迎指正!!!

精彩评论(0)

0 0 举报