前端vuevue

2022年Vue面试题

2022-07-09  本文已影响0人  德日班勒

本次让我们系统的熟悉vue的面试常用题目,无论是给准备面试还是准备跳槽的小伙伴,一个学习以及温故知新的版块。这其中加入了我的理解,我尽量用白话文和易懂的语言形式进行解释,如果有地方解释的不正确或者不准确的情况,还请大家不吝赐教。

2022年大环境的不景气,让我们用知识充实自己,面对生活的一次次挑战与考验,加油!

入门级:

1:什么是mvvm?

MVVM是Model-View-ViewModel的简写,是M-V-VM三部分组成。它本质上是MVC的改进版本,MVVM就是将其中的View的状态和行为抽象化,其中ViewModel将视图 UI 和业务逻辑分开,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。

MVVM采用双向数据绑定,view中数据变化将自动反映到viewmodel上,反之,model中数据变化也将会自动展示在页面上。把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

让我们开发人员可以专注于业务逻辑方面(ViewModel),对于数据的绑定与展示(自动更新dom),不需要过多的dom操作。可以理解为数据(medel)驱动UI(view),而UI(view)的改变也会影响数据(medel)。他们之间就是靠ViewModel来实现。

因此开发者只需要关注业务逻辑,不需要手动操作 DOM,也不需要关注数据状态的同步问题,这些都由 MVVM 统一管理。

2:为什么要用vue?

1.虚拟dom:dom操作是非常的消耗性能的,不再使用原生的dom操作节点的方式,模拟一个虚拟dom树,运用deff算法,只更新修改的dom节点,极大的释放了dom操作。

2.视图,数据,结构的分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能晚餐相关操作(mvvm)

3.组件化:把一个单页应用中的各个模块拆分到一个个单独的组件中,各个组件有自己的行为逻辑以及自己的样式,相互不干扰。便于开发,以及后期维护。相同组件的公用也大大节约了开发成本。

3:vue的生命周期(11个钩子函数)


1.beforeCreate(创建前):在此生命周期函数执行的时候,data和methods中的数据还没有初始化。

2.created(创建后):在这个生命周期中,data 和 methods 都已经被初始化好了,可以访问到data中的数据以及methods的方法。

3.beforeMount(载入前):在此生命周期函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去,此时页面还是旧的。

4.mounted(载入后):此时页面和内存中是最新数据,dom树创建,此时才可以操作dom节点。

5.beforeUpdate(更新前):顾名思义,这个时候页面正准备进行更新,只是还未开始更新动作,所以页面中的数据还是旧的,但是data中的数据已经是新的,只是页面并未和最新数据同步。

6.Updated(更新后):此时页面显示数据和最新的 data 数据同步。

7.beforeDestroy(销毁前):组件销毁前的钩子函数,此时实例上的data,methods,以及过滤器等都处于可用状态。我们一般可以在这个钩子内清除当前组件的定时器(clearInterval() )

8.destroyed(销毁后):此时组件以及被完全销毁,实例中的所有的数据、方法、属性、过滤器…等都已经不可用了

// 以上8个生命周期钩子函数是我们常用的钩子函数,接下来的3个钩子函数可以在特定的时候使用,方便我们理解

9.activated(组件激活时):在使用被keep-alive缓存的组件时,进入函数。如果一个子页面或者一个组件被keep-alive包含,那么当这个页面或组件第一次时会走一次完整的生命周期,当此组件无法被销毁。等重复使用的时候,会从缓存里面直接调出,此时组件不在执行前4个生命周期钩子(因为它并没有被销毁,不需要重新创建)。取而代之的是进入activated钩子函数。表示该页面或组件被激活。

10.deactivated(组件未激活时):实例没有被激活时(同9)。

11.errorCaptured(错误调用):当捕获一个来自后代组件的错误时被调用

注意:有很多面试会问父子组件创建销毁的生命周期顺序:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

即父组件走到beforeMount 后,开始走完子组件的完整生命周期,然后再走父组件mounted

这样理解:
1.有父才有子,所以一开始是父组件先创建实例。
2.mounted本身就是挂在到页面上的钩子,所以必须要等所有的子组件完成后再一起挂载到页面中。(既然是父,当然要包含所有的子)

4:watch、computed 和 methods 的区别

许多面试官喜欢问computed 和watch的区别,这里请多加理解

5.Vue.js的特点

6:插槽的理解

插槽用于决定将所携带的内容,插入到子组件指定的某个位置,但内容必须在父组件中子组件的标签内定义,在子组件中用标签接收。slot 是组件内部的占位符。即组件编写时留给组件使用者的一个插入口。使用者可以在此编写自己的html格式以及样式,插入的位置由编写者制定

7.Vue组件之间传参

1.父传子:

this.$refs.nodeName // refs是根据组件名字来取的响应组件的属性
this.$children[2] // 返回一个数组(使用几率小,一般不这样调用)

2.子传父:

3.兄弟组件之间(这里写的是兄弟组件之间,实际上是所有组件之间都可以,包括父子)

this.$eventBus.$on('sayName',(data)=>{
  console.log(data)
}) // 监听事件总线中sayName方法
this.$eventBus.$emit('sayName',data)

详细讲解请移步到:https://www.jianshu.com/p/267e17c59d32

4.浏览器本地缓存,例如localStorage,sessionStorage

8.vue 组件中的 data 为什么是一个函数

data 是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。Object 是引用数据类型,如果不用 function 返回,每个组件的 data 都是内存的同一个地址(储存的是地址,指针,此处可以复习浅拷贝和深拷贝),一个数据改变了其他也改变了。
理解就是运用了function的函数作用域让他变成局部作用域,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间

9.路由懒加载(首页加载优化)

在单页应用中,如果没有应用懒加载,运用 webpack 打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时

原理:vue 异步组件技术:异步加载,vue-router 配置路由 , 使用 vue 的异步组件技术 , 实现按需加载


import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
  routes: [
//     {
//       path: '/',
//       name: 'HelloWorld',
//       component: HelloWorld
//     }
        {
          path: '/',
          name: 'HelloWorld',
          component: () => import('@/components/HelloWorld.vue')
        }
  ]
})

10:请说出 vue.cli 项目中 src 目录每个文件夹和文件的用法`

assets 文件夹是放静态资源;
components 是放组件;
router 是定义路由相关的配置;
view 视图;
app.vue 是一个应用主组件;
main.js 是入口文件

11.Vue 中 key 值的作用

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key 的作用主要是为了高效的更新虚拟 DOM。
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速

12:vue 的指令

13.v-for 与 v-if

因为v-for的优先级高于v-if,势必会先遍历数组,然后再判断是否显示,这样就会影响速度,尤其是只需要渲染很小一部分的时候,渲染了许多无用的节点,增加许多无用的dom操作,建议用computed先把需要的数组格式化出来

<div v-for="item in list">
    {{item}}
</div>

computed() {
    list() {
        return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
    }
 }

14.Vue怎么兼容IE

使用 babel-polyfill 插件省省吧,2022年6月16日起ie都退役了

15.Vue 怎么重置 data

在很多情况下我们会遇到初始化data的时候,比如在调用element的dialog组件时(组件是v-show控制,会保留之前的状态)

Object.assign(this.$data, this.$options.data()) // 初始化data

16.route 和 router

17.Vue的修饰符有哪些?


常用修饰符:

中级:

1.虚拟 DOM 原理

虚拟 DOM,其实就是用对象的方式取代真实的DOM操作,把真实的DOM操作放在内存当中,在内存中的对象里做模拟操作。当页面打开时浏览器会解析 HTML 元素,构建一颗 DOM树,将状态全部保存起来,在内存当中模拟我们真实的 DOM操作,操作完后又会生成一颗 dom 树,两颗DOM树进行比较,根据 diff 算法比较两颗 DOM树不同的地方,只渲染一次不同的地方。
用diff算法把虚拟DOM和真实DOM比较,找出不同的地方,页面只渲染不同的地方

2:nextTick 的理解

Vue 是异步修改 DOM 的,并且不鼓励开发者直接接触 DOM,但是有时候需要必须对数据更改后的 DOM 元素做相应的处理,但是获取到的 DOM 数据并不是更改后的数据,这时候就需要 this.$nextTick();
很常见一个例子,在数据变化后马上要使用ref进行节点操作,由于vue是异步修改dom的,所以在数据变化后可能没有找到需要操作的dom,故而报错

<div ref="mydom"  v-if="isShow"></div>

  methods() {
    this.isShow = true;
    this.$nextTick(()=>{
      this.$refs.mydom.play() // 等待最新的dom更新了之后的回调,此时已经有了mydom节点,可以对其进行dom操作
    })
  },

3.不需要响应式的数据应该怎么处理?

在我们的Vue开发中,会有一些数据,从始至终都未曾改变过,这种死数据,既然不改变,那也就不需要对他做响应式处理了,不然只会做一些无用功消耗性能,比如一些写死的下拉框,写死的表格数据,这些数据量大的死数据,如果都进行响应式处理,那会消耗大量性能。

// 方法一:将数据定义在return 之外(初始化时期只会对data内的数据绑定getter和setter)
data () {
    this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
    return {}
 }
    
// 方法二:Object.freeze()
data () {
    return {
        list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
        list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
    }
 }

4.vue 中的双向绑定原理

概念:双向绑定是vue的一个核心功能,所谓双向绑定就是当试图发生改变的时候传递给VM(ViewModel ),让数据得到更新,当数据发生改变的时候传给VM(ViewModel ),使得视图发生变化。

那么vue怎么做到的呢?
observer(观察者),劫持监听所有属性,什么意思呢?

vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。

先简单的实现一个js的双向数据绑定来熟悉一下Object.defineProperty()方法

 <input type="text" id="in"/>
    输入的值为:<span id="out"></span>
    <script>
        var int = document.getElementById('in');
        var out = document.getElementById('out');
        var obj = {};
        Object.defineProperty(obj, 'msg', {
            enumerable: true,
            configurable: true,
            set (newVal) {
                out.innerHTML = newVal;
            }
        })
        int.addEventListener('input', function(e) {
            obj.msg = e.target.value;
        })

/*通过给in输入框添加事件监听input来触发obj对象的set方法,
而set在修改了访问器属性的同时,也修改了dom样式,改变了span标签内的文本。*/

5.vue3的了解

大致有三个点:
1.关于提出的新 API setup()函数。
2.对于 Typescript 的支持。
3.最后说了关于替换 Object.defineProperty 为 Proxy 的支持。详细说了下关于 Proxy 代替带来的性能上的提升,因为传统的原型链拦截的方法,无法检测对象及数组的一些更新操作,但使用 Proxy 又带来了浏览器兼容问题。

6.vue-cli 替我们做了哪些工作

vue-cli 是基于 Vue.js 进行快速开发的完整系统,也可以理解成是很多 npm 包的集合。
vue-cli 完成的功能:

7.axios拦截器

响应拦截

axios.interceptors.response.use((response)=>{
    //对响应数据做点什么
    return response.data
},(error)=>{
    //对错误响应做点什么
    return Promise.reject(error)
})

请求拦截

axios.interceptors.request.use((config)=>{
    //在发送请求之前做些什么
    return config
},(error)=>{
    //对请求错误做些什么
    return Promise.reject(error)
})

14:vue-router 路由钩子函数是什么 执行顺序是什么

路由钩子的执行流程, 钩子函数种类有:全局守卫、路由守卫、组件守卫

完整的导航解析流程:

1.全局守卫:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  }
]

const router = new VueRouter({
  routes
})
// 跳转路由之前
router.beforeEach((to,from,next)=>{
  // ..............
  next()
})
// 跳转路由之后
router.afterEach((to,from,next)=>{
  // ..............
  next()
})

export default router

2.单个路由独享的守卫

const routes = [
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue'),
    beforeEach:((to,from,next)=>{
      // ..............
      next()
    })
  }
]

3.组件级守卫(beforeRouteEnter)组件级的守卫写在页面组件中,与data,methods同级。如图

export default {
  data(){
    return{}
  },
  created:{},
  components: {},
  methods:{},
  beforeRouteEnter(to,from,next){
      // ..............
  next()
  }
};

15.Vuex的理解重点

定义:Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式储存管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
使用场景:需要构建一个中大型单页应用,您很可能会考虑如何更好的在组件外部管理状态,Vuex将会成为自然而然的选择。
优点:当你在state中定义了一个数据之后,可以在所在项目中的任何一个组件里进行获取、进行修改、并且你的修改可以得到全局的响应变更。(全局变量理解方式)

主要包括以下几个模块:

Vuex的运行机制:Vuex提供数据(state)来驱动试图(vue components),通过dispath派发actions,在其中可以做一些异步的操作,然后通过commit来提交mutations,最后mutations来更改state。

import Vuex from 'vuex';
import Vue from 'vue';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
  },
  mutations: {
    increment(state, val) {
      console.log(state);
    },
  },
  actions: {
    addCount(context) {
      console.log(context);
      // 可以包含异步操作
      // context 是一个与 store 实例具有相同方法和属性的 context 对象
    },
  },
  getters: {
    doneTodos(state) {
      return state;
    }
  }
});

export default store;

16:Vuex 页面刷新数据丢失怎么解决

我们都知道,vuex是作为一个公共的状态管理集合,但是他是存储在内存中的,在浏览器中,刷新页面会清空一次内存。显然对于一些公共的状态,我们是不希望刷新清空的,这里就有了vuex数据持久化的一个问题

需要做 vuex 数据持久化 一般使用本地存储的方案来保存数据 可以自己设计存储方案 也可以使用第三方插件

1.推荐使用 vuex-persist 插件,它就是为 Vuex 持久化存储而生的一个插件。不需要你手动存取 storage ,而是直接将状态保存至 cookie 或者 localStorage 中
(注实际开发中使用sessionStorage来保存,原因是localStorage在同一浏览器中多个账户登录保存到localStroage会出问题)

2.或者手动把vuex数据存储在sessionStorage中

window.addEventListener('beforeunload', e => {
  console.log('页面进行了刷新或者关闭')
// 保存vuex操作
})
// 注意要卸载监听事件
window.removeEventListener('beforeunload', e =>{})

17.你都用过哪些vue性能优化

这里只列举针对 Vue 的性能优化 整个项目的性能优化是一个大工程 可以另写一篇性能优化的文章了

1.减少http请求
2.对象层级不要过深,否则性能就会差
3.不需要响应式的数据不要放到 data的return 中(可以用 Object.freeze() 冻结数据)
4.v-if 和 v-show 区分使用场景
5.computed 和 watch 区分使用场景
6.v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
7.大数据列表和表格性能优化-虚拟列表/虚拟表格
8.防止内部泄漏,组件销毁后把全局变量和事件销毁
9.图片懒加载
10.路由懒加载(首页加载优化)
11.第三方插件的按需引入
12.适当采用 keep-alive 缓存组件
13.防抖、节流运用
14.服务端渲染 SSR or 预渲染
15.插件引入尽量不要全局引入(首页加载优化)

18.描述下vue从初始化页面=>修改数据=>刷新页面 UI 过程?

1.当 Vue 进入初始化阶段时,一方面 Vue 会遍历 data 中的属性,并用 Object.defineProperty 将它转化成 getter/setterd 的形式,实现数据劫持;
2.另一方面,Vue 的指令编译器 Compiler 对元素节点的各个指令进行解析,初始化视图,并订阅 Watcher 来更新视图,此时 Watcher 会将自己添加到消息订阅器 Dep 中,此时初始化完毕。
3.当数据发生变化时,触发 Observer 中 setter 方法,立即调用 Dep.notify( ),Dep 这个数组开始遍历所有的订阅者,并调用其 update 方法,Vue 内部再通过 diff 算法,patch 相应的更新完成对订阅者视图的改变。

19.给对象添加了新的属性,data改变了,但是页面上没有改变,为什么?

这里用到$set

原因:vue在创建实例的时候把data深度遍历所有属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。让 Vue 追踪依赖,在属性被访问和修改时通知变化。所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。

vue.$set 是能让 vue 知道你添加了属性, 它会给你做处理

change(){
  this.$set(this.list,1,0);
}

进阶级

1.说一下Vue.mixin

在日常的开发中,我们经常会遇到在不同的组件中经常会需要用到一些相同或者相似的代码,这些代码的功能相对独立,可以通过 Vue 的 mixin 功能抽离公共的业务逻辑,原理类似“对象的继承”,当组件初始化时会调用 mergeOptions 方法进行合并,采用策略模式针对不同的属性进行合并。当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”
详细请点击https://www.jianshu.com/p/772d67305f98

2.keep-alive 使用场景和原理

keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。

3.函数式组件使用场景和原理

1.函数式组件需要在声明组件是指定 functional:true
2.不需要实例化,所以没有this,this通过render函数的第二个参数context来代替
3.没有生命周期钩子函数,不能使用计算属性,watch
4.不能通过$emit对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件
5.因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement
6.函数式组件的props可以不用显示声明,所以没有在props里面声明的属性都会被自动隐式解析为prop,而普通组件所有未声明的属性都解析到$attrs里面,并自动挂载到组件根元素上面(可以通过inheritAttrs属性禁止)

上一篇下一篇

猜你喜欢

热点阅读