面试题--Vue篇

106 阅读1分钟

1.vue的生命周期有哪些以及每个生命周期做了什么以及应用场景?

下图为思维导图

image.png

下面是详细的表格

生命周期功能应用场景
beforeCreate是new Vue()之后的第一个钩子,此时data、methods、computed以及watch中的数据都不可以访问1.根据一些信息的判断,决定是否向下渲染,如果不需要,就跳转到其他的组件。2.权限校验,根据有没有权限,决定是否渲染此组件(但是这个是路由中的导航守卫更适合干这个事情)
created实例创建完成后发生,当前阶段已经完成了数据监测,也就是可以使用数据以及修改数据。这里修改数据不会触发updated函数。在这个阶段,可以进行一些数据的初始化,但是此时还不能和dom进行交互,如果一定要交互,就是用vm.$nextTick来访问dom提前初始化数据(为了让了页面展示数据快一些,提前获取数据,发送异步请求数据)
beforeMount实例已经完成了模版编译,但是还没有挂载到页面中。当前阶段虚拟dom已经创建完成,即将开始渲染。此时也可以对数据进行更改,但不会触发updated基本上不做什么,也有人在这里发送异步请求数据
mounted将编译好的模版,挂载到页面当中显示。此时阶段,真实的dom已经挂载完毕,数据已经完成双向绑定。可以访问到真实的dom节点,可以对dom进行操作,做一些对dom的处理以及监听工作
beforeUpdate发生在响应式数据更新之前,虚拟dom被重新渲染之前触发,你可以在当前阶段进行更改数据,不会造成重新渲染基本不做什么
updated发生在更新之后,当前的dom已经更新完毕。要注意的是,不能在此时更改数据,容易造成无限循环的更新和mouted类似,也是对dom一些处理。注意:在这里不要对响应式数据进行修改,容易造成死循环
beforeDestroy在实例销毁之前触发,此时实例还没有没销毁,还可以做一些善后工作,比如清除计时器做一些数据的保存,或者提示,要不然没机会了
destroyed发生在实例销毁之后,组件已经被拆卸,双向绑定被销毁,监听被移除,子实例被销毁

2.vue的响应式原理是什么?vue3的有何不同?

  • Vue 2 的响应式原理主要通过数据劫持(Object.defineProperty)和发布订阅模式实现的。

    通过Object.defineProperty来劫持data中的各个属性的setter、getter,监听数据变化。在数据发生变化时发布消息给订阅者(watcher),订阅者触发响应的回调(update)更新视图

  • Vue3.x改用proxy替代Object.defineProperty,因为proxy可以直接监听对象和数组的变化,有13中拦截方法get set ownKeys has deleteProperty apply defineproperty constructor

注意:proxy只会代理对象的第一层,vue3中怎么做的呢?

在reflect.get中判断是不是object,如果是,再通过reactive做代理,这样就实现了深度监听

注意:检测数组的时候可能触发多次get和set,那么如何防止多次触发呢?

可以在自定义 setter 中添加逻辑,首先判断属性是否是目标对象自身的属性,然后再判断旧值和新值是否相等。只有满足这两个条件中的一个时,才执行更新操作,避免不必要的触发。

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

import { reactive } from 'vue';

const target = reactive({
    // 响应式对象
    count: 0,
    name: 'Vue'
});

const handler = {
    set(target, key, value) {
        if (key in target && target[key] === value) {
            return true; // 值没有变化,不执行更新
        }
        return Reflect.set(target, key, value); // 执行更新操作
    }
};

const proxy = new Proxy(target, handler);

proxy.count = 1; // 更新 count 属性,会触发更新
proxy.count = 1; // 由于值没有变化,不会触发更新
proxy.name = 'Vue'; // 更新 name 属性,会触发更新

3.vue3和vue2的区别?

  • 生命周期上

    image.png

2.响应式原理上,vue3利用proxy可以对整个对象以及数组进行监听

3.vue3支持Composition Api,使用更灵活。vue2是option Api

4.vue3打包体积更小,采用webpack中的treeShaking,剔除没有用到的代码

5.vue3模版支持多节点,vue2只支持单节点

6.vue3重写虚拟dom的实现

7.vue3更好对ts进行支持

4.讲下vue3怎么重写虚拟dom实现的?

vue3里面是在初始化的时候给没一个虚拟节点添加一个patchFlag,也就是一个优化标识,只会比较patchFlag发生改变的节点,进行视图更新。而对于pathchFlag没有发生改变的节点,做一个静态标记,在渲染的时候直接复用

vue2的时候是遍历每个虚拟节点,对比出不一样的,得到一个patch对象,边对比变更新

5.谈一谈对MVVM的理解?

MVVM是Model-View-ViewModel缩写,model代表数据层,view代表视图层,viewModel是数据和视图层的中间桥梁,数据自动绑定到viewModel上,发生改变自动更新视图,视图发生变化会通知viewModel更新数据

6.在vue2.x是如何检测到数组的变化的?

使用了函数劫持的方式,重新写了数组的方法,vue将data中的数组进行了原型链重写,指向了自己定义的数组原形方法,这样当调用数组api时,就通知依赖更新。如果数组中包含引用类型,就递归遍历进行监控,达到深度检测数组变化的效果

下面是自己写的重新数组方法的mini demo

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

var originArr = [1, 2, 3];

var customArr = [];
customArr.push = function(params) {
  console.log('custom 1');
  Array.prototype.push.apply(this,arguments);
  console.log('custom 2', customArr);
}

customArr.push(4);
customArr.push(5);
console.log('finally', customArr)

7.v-model双向绑定的原理是什么?

v-model本质上是一个语法糖,是value属性+input方法的结合语法糖,

使得在表单元素和应用状态之间建立了一个双向的数据绑定。

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Custom v-model Example</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <input  v-bind:value="myValue" v-on:input="updateMyValue($event.target.value)" type="text" >
      
    </input>
    <p>Input Value (Custom v-model): {{ myValue }}</p>
  </div>

  <script>
    new Vue({
      el: '#app',
      data: {
       myValue:''
      },
      methods: {
        updateMyValue(value) {
          this.myValue = value;
        }
      }
    });
  </script>
</body>

</html>

8.vue2.x和vue3.x的diff算法分别说下

简单来说diff算法

  • 同级比较,再比较子节点

  • 先判断一方有子节点一方没有子节点的情况

  • 比较都有子节点的情况

  • 递归比较子节点

vue2.x的diff算法,就是对遍历虚拟节点的进行逐一对比,得到一个patch对象,然后更新到视图上,边对比变更新。采用双端比较算法,循环向中间靠拢

vue3.x diff算法,就是给每个虚拟节点添加一个patchFlag,也就是一个优化标识,发生变化的pathflag节点,会更新到视图,没发生变化的会进行静态标记,渲染会复用这个节点

9.vue组件通信方式有哪些以及原理?

  • 通过props 父->子

  • 通过$emit 子->父

  • 通过ref 父拿子里面的数据

  • 通过$$root

  • 通过provide inject

  • 通过event Bus

  • 通过$attrs

  • vuex

组件通信方式适用场景例子
props父组件传数据给子组件
$emit子组件传值给父组件
ref父组件拿子组件数据
eventBus兄弟组件传值
$$root兄弟组件,拥有共同祖辈兄弟组件:this.$parent.on('add', this.handleAdd)
attrsattrs和listeners祖先传递数据给子孙
provide和inject祖先传递给子孙
vuex用于复杂的数据关系,用来共享变量

10.vue的路由实现,hash路由和history路由原理说一下?

hash路由模式原理:window.loaction.hash

  • 当用户访问一个hash(带有#)页面时,浏览器不会像普通页面那样直接请求服务器跳转,而是dom之前的切换

  • 通过监听hashChange监听路由的变化,从而触发路由的变化,来实现页面的切换

  • 会根据哈希部分的内容匹配到响应的路由配置,然后渲染对应的组件到页面

history路由原理

  • HTML5 history Api中的pushState和repaceState允许修改浏览器的历史记录,而不造成页面的重新渲染

  • 当用户在history路由模式下进行跳转时,会利用pushState在历史记录中添加新的记录,通过popSate事件监听路由变化

  • 根据新路由匹配路由配置,并渲染对应组件到页面中

注意:这个需要服务端配置

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

server {
    listen 80;
    server_name example.com;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

11.vue的v-show和v-if区别

v-show就当于css里面的display,但是dom元素依旧存在, 不涉及生命周期的改变

v-if切换就有一个编译/卸载的过程,涉及到组件的重建以及销毁

v-if有更高的切换消耗,v-show有更高的初始化渲染消耗

如果非常频繁的切换v-show比较好,如果在运行时条件很少改变用v-if比较好

12.keep-alive的常用属性有哪些以及原理?

keep-alive可以实现组件缓存,当组件切换时不会对当前组件卸载

常用属性include/exclude,允许组件有条件的缓存

两个生命周期activated,deactivated

13.nextTick的作用是什么?他的实现原理是什么?

nextTick的作用就是在下次DOM更新循环之后执行延迟回调。主要就是让我们在数据更新之后并且dom渲染完成之后执行一些操作,就是确保这些操作完成在执行

原理:nextTick主要使用了宏任务和微任务

主要过程就是promise mutactionObserver setImmedidate settimeout

定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列

14.说一下vue ssr的实现原理?

SSR也就是服务端渲染,也就是降Vue在客户端把标签渲染成HTML的工作放在服务端上,然后再把html直接返回客户端

SSR有着更好的SEO,并且首屏加载速度更快

也有缺点: 服务端渲染应用程序也要处于Node.js的运行环境,对于服务器有更大的负载需求

15.Vue组件的data为什么必须是函数?

Vue中都是组件化组成,复用一次组件就创建一个实例,复用多个,就创建多个实例,函数可以保证数据的隔离性,如果是对象的话,对象属于引用类型,会影响到所有的实例,造成数据污染,达不到数据隔离的效果,所以data必须是一个函数

16.说一下vue中的compiler的实现原理是什么?

vue中的compiler主要就是将template转换成render,render再生成vnode

简单来说三步

  • parse 将模版template转换成抽象语法树ast

  • optimize 遍历ast的节点进行静态标记,这样就知道那部分是改变的,通过diff算法,如果有不一样的进行更新,提升性能

  • generate 将前两步完善好的ast树,组成render字符串,然后将render字符串通过new Function的方式转换成渲染函数

17.vue和React的区别是什么?

相同点

  • 都有组件化思想

  • 都有服务端渲染

  • 都有数据驱动视图

  • 都有构建工具 vue vue-cli react create React App

  • 都有native vue是weex react是react native

  • 都使用了virtual DOM

不同点

  • vue是双向绑定 数据更变视图也会更新,视图改变也会通知数据更新 react是单项数据流,数据驱动视图

  • diff算法不同 react是使用diff队列保存需要更新那些DOM,得到一个patch树,再统一操作批量更新dom vue是两端对比,循环向中间靠拢,边对比,变更新

  • 通信方式不同 react是通过回调函数进行传递的,vue是通过事件和回调函数

18说一下watch和computed区别是什么?

  • 都是监测数据变化的

  • watch是一对多,监测一个数据改变,对其做一些逻辑处理和操作。computed是多对一,监听属性依赖其他属性

  • watch支持异步,computed不支持

  • computed有缓存,只有依赖的值变了,才会重新计算。watch没有

  • watch有两个参数,第一个最新值,第二个输入之前的值

  • computed将会混入到vuede 实例中去,所以需要监听自变量。而watch监听data、props里面变化的数据

19.computed的工作原理?

computed本质是一个惰性的观察者。如果属性存在props或者data中会给警告

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

20.说一下知道的vue修饰符有哪些?

  • 事件修饰符

  • 按键修饰符

  • 表单修饰符

事件修饰符

  • .stop阻止冒泡事件

  • .prevent阻止默认事件

  • .capture使用时间捕获模式

  • .self只在当前元素本身触发

  • .once只触发一次

  • .passive默认行为将立即触发

按键修饰符

  • .enter回车
  • .left 左键

  • .right右键

  • .up上

  • .down下

  • .middle滚轮

  • .space 空格

表单修饰符

  • .trim去空格过滤首尾
  • .lazy在文本框失去焦点才触发
  • .number将文本框输入的内容转为number类型

21.如何实现vue项目中的性能优化

编码阶段

  • 尽量减少data中的数据

  • v-if和v-for不能连用

  • 循环需要用key,并且保证key的唯一性

  • 使用路由懒加载,异步组件

  • 防抖和节流

  • 图片懒加载

  • 第三方模版按需导入

  • 长列表滚动到可视区域加载

seo优化

  • 服务端渲染
  • 预渲染

打包优化

  • 使用cdn加载第三方模块

  • 多线程打包happypack

  • spiltChunks抽离公共组件

  • sourceMap优化

  • tree SShaking

22.vue中的spa应用如何优化首页加载速度?

1.cdn加载第三方模块

2.缓存。将长时间不会变的第三方库和静态资源设置为强缓存,max-age设置为最大。再将访问路径加上hash值,等哈希值变了之后保证获取到最新资源

3.gzip压缩

4.懒加载。路由懒加载。当url匹配到响应的路径是你,会通过import动态的加载组件,从而提升首屏加载速度。webpack会把动态加载的组件分离成一个单独的chunk文件

5.预渲染。由于浏览器在渲染页面之前,首先要加载html、css、js文件。为此可能会有一段白屏的时间,可以加loading,或者骨架屏

6.http2.如果系统首屏要同时加载很多静态资源文件,但是浏览器对同域名的tcp链接数目是有限制的(chrome6个),超过规定的tcp连接数,要等到之前发送的请求有响应了之后,才能继续发送。然而http2对tcp连接并发多个请求是没限制的,在网络较差的情况下开启http2优化较为明显

7.封装。封装公共组件,对utils,插件,过滤器以及指令

8.图片懒加载。减少http请求

9.使用svg图标

10.压缩图片 image-webpack-loader

11.合理使用第三方库。尽量使用按需加载

12.使用可视化工具分析打包模块 webpack-bundle-analyzer

23.vue中key的作用?

key的作用更搞笑的更新虚拟dom

24.说下vue中的ref?

ref是用来给元素和子组件注册引用信息。引用信息将会注册在父组件的$refs上

  • 如果在普通的dom上,引用指向的就是dom元素

  • 如果在子组件上,引用就指向组件实例

使用场景

  • 本页获取dom元素

  • 或者子组件的数据以及方法

参考地址