Vue常见的面试题总结

998 阅读5分钟

Vue常见的面试题

1. 什么是 MVVM

Model层:数据模型层,指后端传递的数据

View层:视图层,指的是看到的页面

ViewModel层:视图模型层,是连接 View层Model层 的桥梁
主要负责暴露数据给 View层 中的数据绑定声明、指令声明、事件绑定声明,进行实际的业务逻辑实现。

  • 数据变化了,试图自动更新 ==> ViewModel 底层会做好监听Object.defineProperty,当 Model层 数据变化时,View层 会自动更新。

  • 视图变化了,绑定的数据自动更新 ==> ViewModel 会监听双向绑定的表单元素的变化,一旦 View层 的视图发生变化,绑定数据的Model层也会得到自动更新。

mvvm.png

2. v-show 和 v-if 的区别

相同点

都用于条件渲染,用来控制元素的显示和隐藏

不同点

  • 实现本质不同
    v-show 通过 cssdisplay 样式属性进行显示和隐藏
    v-if 动态的对 DOM 元素进行添加和删除

  • 编译的区别
    v-show 有更高的初始渲染消耗
    v-if 有更高的切换性能消耗

  • 编译条件的区别
    v-show 不管初始条件是什么,元素都会被渲染,并且只是简单的基于 css 进行切换
    v-if 如果在初始渲染时条件为 false ,则什么也不会执行,一直到第一次条件变为 true 时,才会开始渲染条件快。

  • 性能的比较
    v-show 适用于需要频繁切换条件的场景
    v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景

场景

  • 如果元素需要频繁的进行切换,推荐使用 v-show
  • 如果元素在运行时很少改变条件,推荐使用 v-if

3.vue中的 key 作用

如果列表中数据的位置会 动态改变 或者有新的项目添加到列表中,并且希望列表中的数据保持自己的特征和状态,例如:input中输入的内容,switch的选中状态,都需要使用 :key指定列表中的数据的唯一标识,提高渲染性能。

代码演示

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>

<body>
  <div class="app">
    <div>
      <label>Id:
        <input type="text" v-model="id">
      </label>

      <label>Name:
        <input type="text" v-model="name">
      </label>

      <input type="button" value="添加" @click="add">
    </div>

    <p v-for="item in list" :key="item.id">
      <input type="checkbox">{{item.id}} --- {{item.name}}
    </p>
  </div>
  </div>

  <script>
    var vm = new Vue({
      el: '.app',
      data: {
        id: '',
        name: '',
        list: [
          { id: 1, name: '李斯' },
          { id: 2, name: '嬴政' },
          { id: 3, name: '赵高' },
          { id: 4, name: '韩非' },
          { id: 5, name: '荀子' }
        ]
      },
      methods: {
        add() {
          this.list.unshift({ id: this.id, name: this.name })
        }
      }
    })
  </script>
</body>

</html>

4.computed 和 watch 和 methods 的区别

作用

  • computed :在模版中放入太多的逻辑会让模版过重且难以维护,因此对于任何复杂逻辑,我们都需要放在计算属性中。
  • watch :在数据变化时执行异步或开销较大的操作时,来响应数据的变化。
  • metchods:表示一个具体的操作,主要书写业务逻辑。

区别

  • 使用场景的区别
    computed:是计算属性,依赖其他的属性计算所得出的最后的值,用于定义基础数据 data 之上的数据 newData
    watch:是去监听一个值的变化,然后执行相应的函数,用于在某个数据变化时做一些操作。
    methods:执行相应的业务逻辑。

  • 是否支持缓存
    computed:支持缓存,只有依赖数据发生变化,才会重新进行计算。
    watch:不支持缓存,数据变化,直接会触发相应的操作。
    methods:不支持缓存,每当触发重新渲染时,调用方法将会再次执行函数。

  • 写法上的区别
    computed:通常就是简单的计算,内部必须有返回值。
    watch:回调里面会传入监听属性的新值和旧值,通过这两个值可以做一些特定的操作。
    methods:执行相关的业务逻辑。

使用场景

  • 当我们需要进行数值计算,并且依赖于其他数据时,应该使用computed,因为可以利用computed的缓存特性,避免每次获取值时,都要重新计算,或者我们需要在插值表达式内放入太多逻辑时,可以对逻辑的处理进行抽取。

  • 当有一些数据、功能需要随着其他数据的变化而变化时,或者需要在数据变化时执行异步或开销较大的数据时,应该使用watch

5.vue 中是如何检测数组变化的

背景

由于JavaScript的限制(性能问题),vue不能检测数组和对象的动态变化,这里的不能检测数组和对象的变化,是指改变对象的某一个属性,例如属性的添加或移除,检测不到属性的动态变化,数组也是同理。因为没有使用Object.defineProperty()对数组、对象的属性动态进行拦截。

场景

vue不能检测以下数组的变动:

  • 利用索引直接设置一个数组项,例如:vm.items[indexOfltem] = newValue

  • 修改数组长度,例如:vm.item.length = newLength

解决方案

  • 重写了数组原型上的方法,即将data中的数组,进行了原型链重写。指向了自己定义的数组原型方法,这样当调用数组 api时,就可以通知依赖更新,这些重写了原型链的数组方法,我们称之为变更方法。
    push() pop() shift() unshift() splice() sort() reverse()

代码演示:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <div>
      <span>
        <input type="text" v-model='fname'>
        <button @click='add'>添加</button>
        <button @click='del'>删除</button>
        <button @click='change'>替换</button>
      </span>
    </div>
    <ul>
      <li :key='index' v-for='(item,index) in list'>{{item}}</li>
    </ul>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      Vue数组操作
      1、变更方法:会影响数组的原始数据的变化。
      2、替换数组:不会影响原始的数组数据,而是形成一个新的数组。
    */
    var vm = new Vue({
      el: '#app',
      data: {
        fname: '',
        list: ['apple','orange','banana']
      },
      methods: {
        add: function(){
          this.list.push(this.fname);
        },
        del: function(){
          this.list.pop();
        },
        change: function(){
          this.list = this.list.slice(0,2);
        }
      }
    });
  </script>
</body>
</html>

  • vue.set vm.$set
    向响应式对象中添加一个property,并确保这个新的property同样是响应式的,且触发视图更新。

代码演示:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <ul>
      <li v-for='item in list'>{{item}}</li>
    </ul>
    <div>
      <div>{{info.name}}</div>
      <div>{{info.age}}</div>
      <div>{{info.gender}}</div>
    </div>
	<button @click="change">改变</button>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      动态处理响应式数据
      
    */
    var vm = new Vue({
      el: '#app',
      data: {
        list: ['apple', 'orange', 'banana'],
        info: {
          name: 'lisi',
          age: 12
        }
      },
	  methods: {
	  
		change () {
		
			this.$set(this.list, 1, '亚瑟')
			this.$set(vm.info, 'gender', 'female');
		
		}
	  
	  }
    });
	
    // vm.list[1] = 'lemon';
    // Vue.set(vm.list, 2, 'lemon');
    // vm.$set(vm.list, 1, 'lemon');

    // vm.info.gender = 'male';
    // vm.$set(vm.info, 'gender', 'female');

    
  </script>
</body>
</html>

6.对Vue生命周期的理解

1.概念

Vue实例从创建到销毁的过程,就是生命周期,也就是从创建、初始化数据、编译模板、挂载Dom → 渲染、更新→ 渲染、卸载等一系列过程,我们称之为Vue的生命周期。

2.图示

vue生命周期.png

代码演示

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
</head>

<body>
  <div id="app">
    <input type="button" value="修改msg" @click="msg='No'">
    <h3 id="h3">{{ msg }}</h3>
  </div>

  <script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        msg: 'ok'
      },
      methods: {
        show() {
          console.log('执行了show方法')
        }
      },
      beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
        // console.log(this.msg)
        // this.show()
        // 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
      },
      created() { // 这是遇到的第二个生命周期函数
        // console.log(this.msg)
        // this.show()
        //  在 created 中,data 和 methods 都已经被初始化好了!
        // 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
      },
      beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
        // console.log(document.getElementById('h3').innerText)
        // 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
      },
      mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
        // console.log(document.getElementById('h3').innerText)
        // 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
      },


      // 接下来的是运行中的两个事件
      beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗?  数据肯定被更新了】
        /* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
        console.log('data 中的 msg 数据是:' + this.msg) */
        // 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
      },
      updated() {
        console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
        console.log('data 中的 msg 数据是:' + this.msg)
        // updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
      }
    });
  </script>
</body>

</html>

7.谈谈你对 keep-alive 的了解

1.理解

keep-aliveVue内置的一个组件,可以使被包含的组件保留状态,实现组件的缓存,当组件切换时不会对当前组件进行卸载,避免重新渲染,它自身不会渲染一个DOM元素,也不会出现在组件的父组件链中。当组件在<keep-alive>内被切换,它的activateddeactivated的两个生命周期钩子函数将会被对应执行。

2.场景

keep-alive一般结合路由和动态组件一起使用,用于缓存组件。
众所周知动态组件是根据不同条件而动态切换组件,当组件由渲染到被销毁,再由销毁变为渲染状态,其内部的数据都要初始化并且所有的声明周期都会重新调用。如果内部的组件存在一些异步调用或者资源申请,会造成性能浪费,如果使用keep-alive,由于其包含的组件都会被缓存到内存中,那么他就会避免被多次销毁和初始化。

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

为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象

  • 因为组件是用来复用的,且js里对象是引用关系。
  • 如果组件中data是一个对象,就使得所有组件实例共用了一份data,就会造成一个组件数据改变其他组件也跟着变。
  • 如果组件中的data选项是一个函数,数据以函数返回值的形式定义,这样每复用一次组件,就会返回一份新的data,类似于给每一个组件实例创建一个私有的数据空间,让个组价实例维护各自的数据
  • new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

官方文档:data-必须是一个函数

9.Vue 组件间通信有哪几种方式

父传子

子组件内定义 props:['变量名']
父组件的标签内 :变量名 = '父组件需要向子组件传递的数据'

// family父组件
<template>
  <div >
    <com-article :family="familyList"></com-article>
  </div>
</template>

<script>
import home from './test/home.vue'
export default {
  name: 'HelloWorld',
  components: { home },
  data() {
    return {
      familyList: ['张一一', '冯家祺', '张美丽']
    }
  }
}
</script>
// 子组件 home.vue
<template>
  <div>
    <span v-for="(item, index) in family" :key="index">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: ['family']
}
</script>

子传父

子组件内的定义 this.$emit('自定义事件名',需要传递的数据)
父组件的标签内 @'自定义事件名' = 'methods的方法名'

10.Vuex提供了哪些核心方法

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。它包含 5 个核心的概念

StateMutationsGettersActionsModules

  • state:定义了应用中的数据源,即设置全局共享的数据源。
  • mutations:唯一更改 store 中状态的方法,且必须是同步函数。
  • getters:允许数组从 store 中获取数据对数据进行格式化或者提取,类似于计算属性。
  • actions:处理 Vuex 中的异步操作,用于提交 mutation,而不是直接变更状态。
  • modules:允许将单一的 store 拆分为多个 store,且同时保存在单一的状态树中。