前端开发那些事儿Vue优质的web前端专题

梳理Vue常考面试题

2020-08-12  本文已影响0人  程序员poetry

完整版推荐在线阅读 https://poetries1.gitee.io/fe-interview

1 对于MVVM的理解

MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。

2 请详细说下你对vue生命周期的理解

答:总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后

生命周期是什么

Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期

各个生命周期的作用

生命周期 描述
beforeCreate 组件实例被创建之初,组件的属性生效之前
created 组件实例已经完全创建,属性也绑定,但真实dom还没有生成,$el还不可用
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用
mounted el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
beforeUpdate 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
update 组件数据更新之后
activited keep-alive专属,组件被激活时调用
deadctivated keep-alive专属,组件被销毁时调用
beforeDestory 组件销毁前调用
destoryed 组件销毁后调用
image

由于Vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data对象上存在才能让Vue将它转换为响应式的。Vue提供了$set方法用来触发视图更新

export default {
    data(){
        return {
            obj: {
                name: 'fei'
            }
        }
    },
    mounted(){
        this.$set(this.obj, 'sex', 'man')
    }

}

什么是vue生命周期?

vue生命周期的作用是什么?

vue生命周期总共有几个阶段?

第一次页面加载会触发哪几个钩子?

DOM 渲染在哪个周期中就已经完成?

3 Vue实现数据双向绑定的原理:Object.defineProperty()

4 Vue组件间的参数传递

父组件与子组件传值

父组件传给子组件:子组件通过props方法接受数据;

非父子组件间的数据传递,兄弟组件传值

eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适(虽然也有不少人推荐直接用VUEX,具体来说看需求)

5 Vue的路由实现:hash模式 和 history模式

5 vue路由的钩子函数

首页可以控制导航跳转,beforeEachafterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。

6 vuex是什么?怎么使用?哪种功能场景使用它?

image image

modules:项目特别复杂的时候,可以让每一个模块拥有自己的statemutationactiongetters,使得结构非常清晰,方便管理

image

7 v-if 和 v-show 区别

8 $route$router的区别

9 如何让CSS只在当前组件中起作用?

将当前组件的<style>修改为<style scoped>

10 <keep-alive></keep-alive>的作用是什么?

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

比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染

11 指令v-el的作用是什么?

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

12 在Vue中使用插件的步骤

13 请列举出3个Vue中常用的生命周期钩子函数?

14 vue-cli 工程技术集合介绍

问题一:构建的 vue-cli 工程都到了哪些技术,它们的作用分别是什么?

问题二:vue-cli 工程常用的 npm 命令有哪些?

npm install
npm run dev
npm run build
npm run build --report

在浏览器上自动弹出一个 展示 vue-cli 工程打包后 app.jsmanifest.jsvendor.js 文件里面所包含代码的页面。可以具此优化 vue-cli 生产环境部署的静态资源,提升 页面 的加载速度

15 NextTick

nextTick可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM

16 vue的优点是什么?

17 路由之间跳转?

声明式(标签跳转)

<router-link :to="index">

编程式( js跳转)

router.push('index')

18 实现 Vue SSR

其基本实现原理

Vue SSR 的实现,主要就是把 Vue 的组件输出成一个完整 HTML, vue-server-renderer 就是干这事的

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

20 Vue computed 实现

实现时,主要如下

21 Vue complier 实现

可以简单理解成以下步骤:

22 怎么快速定位哪个组件出现性能问题

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

23 开发中常用的指令有哪些

使用了v-if的时候,如果值为false,那么页面将不会有这个html标签生成。 v-show则是不管值为true还是false,html元素都会存在,只是CSS中的display显示或隐藏

语法:v-bind:title="msg"简写::title="msg"

24 Proxy 相比于 defineProperty 的优势

Object.defineProperty() 的问题主要有三个:

Proxy 在 ES2015 规范中被正式加入,它有以下几个特点

除了上述两点之外,Proxy 还拥有以下优势:

25 vue-router 有哪几种导航守卫?

全局守卫

vue-router全局有三个守卫

// main.js 入口文件
import router from './router'; // 引入路由
router.beforeEach((to, from, next) => { 
  next();
});
router.beforeResolve((to, from, next) => {
  next();
});
router.afterEach((to, from) => {
  console.log('afterEach 全局后置钩子');
});

路由独享守卫

如果你不想全局配置守卫的话,你可以为某些路由单独配置守卫

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => { 
        // 参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖
        // ...
      }
    }
  ]
})

路由组件内的守卫

26 组件之间的传值通信

组件之间通讯分为三种: 父传子、子传父、兄弟组件之间的通讯

1. 父组件给子组件传值

<template>
    <child :msg="message"></child>
</template>

<script>
import child from './child.vue';
export default {
    components: {
        child
    },
    data () {
        return {
            message: 'father message';
        }
    }
}
</script>

子组件vue模板child.vue:

<template>
    <div>{{msg}}</div>
</template>

<script>
export default {
    props: {
        msg: {
            type: String,
            required: true
        }
    }
}
</script>

2. 子组件向父组件通信

父组件向子组件传递事件方法,子组件通过$emit触发事件,回调给父组件

父组件vue模板father.vue:

<template>
    <child @msgFunc="func"></child>
</template>

<script>
import child from './child.vue';
export default {
    components: {
        child
    },
    methods: {
        func (msg) {
            console.log(msg);
        }
    }
}
</script>

子组件vue模板child.vue:

<template>
    <button @click="handleClick">点我</button>
</template>

<script>
export default {
    props: {
        msg: {
            type: String,
            required: true
        }
    },
    methods () {
        handleClick () {
            //........
            this.$emit('msgFunc');
        }
    }
}
</script>

3. 非父子, 兄弟组件之间通信

vue2中废弃了broadcast广播和分发事件的方法。父子组件中可以用props和$emit()。如何实现非父子组件间的通信,可以通过实例一个vue实例Bus作为媒介,要相互通信的兄弟组件之中,都引入Bus,然后通过分别调用Bus事件触发和监听来实现通信和参数传递。Bus.js可以是这样:

import Vue from 'vue'
export default new Vue()

在需要通信的组件都引入Bus.js:

<template>
    <button @click="toBus">子组件传给兄弟组件</button>
</template>

<script>
import Bus from '../common/js/bus.js'
export default{
    methods: {
        toBus () {
            Bus.$emit('on', '来自兄弟组件')
        }
      }
}
</script>

另一个组件也import Bus.js 在钩子函数中监听on事件

import Bus from '../common/js/bus.js'
export default {
    data() {
      return {
        message: ''
      }
    },
    mounted() {
       Bus.$on('on', (msg) => {
         this.message = msg
       })
     }
   }

27 Vue与Angular以及React的区别?

Vue与AngularJS的区别

Vue与React的区别

28 vuex是什么?怎么使用?哪种功能场景使用它?

vuex的使用借助官方提供的一张图来说明:

image

Vuex有5种属性: 分别是 state、getter、mutation、action、module;

state

Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据

mutations

mutations定义的方法动态修改Vuex 的 store 中的状态或数据。

getters

类似vue的计算属性,主要用来过滤一些数据

action

一般面试官问到这里vue基本知识就差不多了, 如果更深入的研究就是和你探讨关于vue的底层源码;或者是具体在项目中遇到的问题,下面列举几个项目中可能遇到的问题:

28 watch与computed的区别

computed:

watch:

小结:

29、Vue是如何实现双向绑定的?

利用Object.defineProperty劫持对象的访问器,在属性值发生变化时我们可以获取变化,然后根据变化进行后续响应,在vue3.0中通过Proxy代理对象进行类似的操作。

// 这是将要被劫持的对象
const data = {
  name: '',
};

function say(name) {
  if (name === '古天乐') {
    console.log('给大家推荐一款超好玩的游戏');
  } else if (name === '渣渣辉') {
    console.log('戏我演过很多,可游戏我只玩贪玩懒月');
  } else {
    console.log('来做我的兄弟');
  }
}

// 遍历对象,对其属性值进行劫持
Object.keys(data).forEach(function(key) {
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      console.log('get');
    },
    set: function(newVal) {
      // 当属性值发生变化时我们可以进行额外操作
      console.log(`大家好,我系${newVal}`);
      say(newVal);
    },
  });
});

data.name = '渣渣辉';
//大家好,我系渣渣辉
//戏我演过很多,可游戏我只玩贪玩懒月

29 Vue2.x 响应式原理

Vue 采用数据劫持结合发布—订阅模式的方法,通过 Object.defineProperty() 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

image

Watcher 订阅者是 ObserverCompile 之间通信的桥梁,主要做的事情

Vue3.x响应式数据原理

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

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

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

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

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

30 v-model双向绑定原理

v-model本质上是语法糖,v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件

所以我们可以v-model进行如下改写:

<input v-model="sth" />
//  等同于
<input :value="sth" @input="sth = $event.target.value" />
//Parent
<template>
    {{num}}
    <Child v-model="num">
</template>
export default {
    data(){
        return {
            num: 0
        }
    }
}

//Child
<template>
    <div @click="add">Add</div>
</template>
export default {
    props: ['value'],
    methods:{
        add(){
            this.$emit('input', this.value + 1)
        }
    }
}

31 scoped样式穿透

scoped虽然避免了组件间样式污染,但是很多时候我们需要修改组件中的某个样式,但是又不想去除scoped属性

  1. 使用/deep/
//Parent
<template>
<div class="wrap">
    <Child />
</div>
</template>

<style lang="scss" scoped>
.wrap /deep/ .box{
    background: red;
}
</style>

//Child
<template>
    <div class="box"></div>
</template>
  1. 使用两个style标签
//Parent
<template>
<div class="wrap">
    <Child />
</div>
</template>

<style lang="scss" scoped>
//其他样式
</style>
<style lang="scss">
.wrap .box{
    background: red;
}
</style>

//Child
<template>
    <div class="box"></div>
</template>

32 ref的作用

33 computed和watch区别

  1. 当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性computed

Computed本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。 适用于计算比较消耗性能的计算场景。当表达式过于复杂时,在模板中放入过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中处理

image
<template>{{fullName}}</template>
export default {
    data(){
        return {
            firstName: 'xie',
            lastName: 'yu fei',
        }
    },
    computed:{
        fullName: function(){
            return this.firstName + ' ' + this.lastName
        }
    }
}
  1. watch用于观察和监听页面上的vue实例,如果要在数据变化的同时进行异步操作或者是比较大的开销,那么watch为最佳选择

Watch没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用unWatch手动注销

image
<template>{{fullName}}</template>
export default {
    data(){
        return {
            firstName: 'xie',
            lastName: 'xiao fei',
            fullName: 'xie xiao fei'
        }
    },
    watch:{
        firstName(val) {
            this.fullName = val + ' ' + this.lastName
        },
        lastName(val) {
            this.fullName = this.firstName + ' ' + val
        }
    }
}

34 vue-router守卫

导航守卫 router.beforeEach 全局前置守卫

// main.js 入口文件
import router from './router'; // 引入路由
router.beforeEach((to, from, next) => { 
  next();
});
router.beforeResolve((to, from, next) => {
  next();
});
router.afterEach((to, from) => {
  console.log('afterEach 全局后置钩子');
});

路由独享的守卫 你可以在路由配置上直接定义 beforeEnter 守卫

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内的守卫你可以在路由组件内直接定义以下路由导航守卫

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用,我们用它来禁止用户离开
    // 可以访问组件实例 `this`
    // 比如还未保存草稿,或者在用户离开前,
    将setInterval销毁,防止离开之后,定时器还在调用。
  }
}

35 vue修饰符

36 vue项目中的性能优化

37 vue.extend和vue.component

38 Vue的SPA 如何优化加载速度

39 移动端如何设计一个比较友好的Header组件?

当时的思路是头部(Header)一般分为左、中、右三个部分,分为三个区域来设计,中间为主标题,每个页面的标题肯定不同,所以可以通过vue props的方式做成可配置对外进行暴露,左侧大部分页面可能都是回退按钮,但是样式和内容不尽相同,右侧一般都是具有功能性的操作按钮,所以左右两侧可以通过vue slot插槽的方式对外暴露以实现多样化,同时也可以提供default slot默认插槽来统一页面风格

40 Proxy与Object.defineProperty的优劣对比?

Proxy的优势如下:

Object.defineProperty的优势如下:

兼容性好,支持IE9

41 你是如何理解Vue的响应式系统的?

image

响应式系统简述:

42 既然Vue通过数据劫持可以精准探测数据变化,为什么还需要虚拟DOM进行diff检测差异?

现代前端框架有两种方式侦测变化,一种是pull一种是push

43 Vue为什么没有类似于React中shouldComponentUpdate的生命周期?

考点: Vue的变化侦测原理

前置知识: 依赖收集、虚拟DOM、响应式系统

根本原因是Vue与React的变化侦测方式有所不同

44 Vue中的key到底有什么用?

diff程可以概括为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较,这四种比较方式就是首、尾、旧尾新头、旧头新尾.

准确: 如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug.
快速: key的唯一性可以被Map数据结构充分利用,相比于遍历查找的时间复杂度O(n),Map的时间复杂度仅仅为O(1).

image

45 vue 项目性能优化

代码层面:

Webpack 层面优化:

46 nextTick

nextTick 可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM

nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用

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

47 说一下vue2.x中如何监测数组变化

使用了函数劫持的方式,重写了数组的方法,Vuedata中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。

48 你的接口请求一般放在哪个生命周期中

接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created

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

一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数

50 说一下v-model的原理

v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。 可以通过model属性的propevent属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性

51 Vue事件绑定原理说一下

原生事件绑定是通过addEventListener绑定给真实元素的,组件事件绑定是通过Vue自定义的$on实现的

52 Vue模版编译原理知道吗,能简单说一下吗?

简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:

53 Vue2.x和Vue3.x渲染器的diff算法分别说一下

简单来说,diff算法有以下过程

54 再说一下虚拟Dom以及key属性的作用

key的作用是尽可能的复用 DOM 元素

55 Vue中组件生命周期调用顺序说一下

加载渲染过程

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

子组件更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated

父组件更新过程

父 beforeUpdate -> 父 updated

销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

56 SSR了解吗

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

SSR有着更好的SEO、并且首屏加载速度更快等优点。不过它也有一些缺点,比如我们的开发条件会受到限制,服务器端渲染只支持beforeCreatecreated两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境。还有就是服务器会有更大的负载需求

57 你都做过哪些Vue的性能优化

编码阶段

SEO优化

打包优化

用户体验

还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

58 Vue.js特点

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

60 vue路由传参数

61 vuex 是什么? 有哪几种属性?

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
  • 有 5 种,分别是 stategettermutationactionmodule

vuex 的 getter 是什么?

vuex 的 mutation 是什么?

vuex 的 action 是什么?

面对复杂的应用程序,当管理的状态比较多时;我们需要将vuexstore对象分割成模块(modules)。

如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuexstate 里如果被其他地方复用,请将请求放入 action 里,方便复用,并包装成 promise 返回

62 如何让CSS只在当前组件中起作用?

将当前组件的<style>修改为<style scoped>

63 delete和Vue.delete删除数组的区别?

var a=[1,2,3,4]
var b=[1,2,3,4]
delete a[0]
console.log(a)  //[empty,2,3,4]
this.$delete(b,0)
console.log(b)  //[2,3,4]

64 v-on可以监听多个方法吗?

可以

<input type="text" :value="name" @input="onInput" @focus="onFocus" @blur="onBlur" />

v-on 常用修饰符

65 Vue子组件调用父组件的方法

66 vue如何兼容ie的问题

babel-polyfill插件

67 Vue 改变数组触发视图更新

以下方法调用会改变原始数组:push(), pop(), shift(), unshift(), splice(), sort(), reverse(),Vue.set( target, key, value )

68 DOM 渲染在哪个周期中就已经完成?

mounted

注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted

mounted: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}

69 简述每个周期具体适合哪些场景

第一次加载会触发哪几个钩子

会触发beforeCreate , created ,beforeMount ,mounted

70 动态绑定class

active classnameisActive 变量

<div :class="{ active: isActive }"></div>
上一篇 下一篇

猜你喜欢

热点阅读