vue面试题总结

175 阅读12分钟

1.vue全家桶包含哪些?

vue(整体架构) vue-cli(脚手架) + vue-router(路由) + vuex(状态管理) + axios(请求) + elementUI/Vant组件库等

2.对 SPA 单页面的理解

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

优点:

  • 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
  • 基于上面一点,SPA 相对对服务器压力小;
  • 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;

缺点:

  • 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载
  • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;vue-router
  • SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。SSR

3.v-show 与 v-if 有什么区别

  • v-if: 是真正的条件渲染,标签节点会真正的被销毁或者重建;
  • v-show:不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。

场景使用:v-if适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

4.v-model的实现原理

v-model指令是双向数据绑定的语法糖,指令在表单 input、textarea、select 等元素上创建双向数据绑定

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

5.双向数据绑定的原理

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

Vue 主要通过以下 4 个步骤来实现数据双向绑定的:

  1. 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
  2. 实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
  3. 实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
  4. 实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。

Proxy 与 Object.defineProperty 优劣对比

Proxy 的优势如下:

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

Object.defineProperty 的优势如下:

  • 兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到3.0版本才能用 Proxy 重写。

6.组件中的data为何是函数return {}

因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题

7.怎样理解 Vue 的单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

8.computed 和 watch 的区别和运用的场景

  • computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
  • watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

运用场景:

  • 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
  • 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用watch选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

9.如何检测数组的变化

由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:

  • 当你利用索引直接设置一个数组项时,例如:vm.list[indexOfItem] = newValue
  • 当你修改数组的长度时,例如:vm.list.length = newLength
   // 第一个情况解决方法
   // 1. Vue.set()
   Vue.set( vm.list, propertyName/index, newValue )
   // 2. vm.$set()
   vm.$set( this.list, propertyName/index, newValue )
   // 3. Array.prototype.splice
   vm.list.splice(index, 1, newValue)
   
   // 第二种情况
   // Array.prototype.splice
   vm.items.splice(newLength)

变异方法: Vue 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice() --增加/删除/替换
  • sort() --排序
  • reverse() -- 颠倒

10.生命周期函数

  • 创建前后 beforeCreate、created
  • 挂载前后 beforeMount、mounted
  • 更新前后 beforeUpdate、updated
  • 销毁前后 beforeDestroy、destroyed

10.1 父子组件生命周期的执行顺序

  • 加载渲染过程

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

  • 子组件更新过程

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

  • 父组件更新过程

    父 beforeUpdate -> 父 updated

  • 销毁过程

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

10.2 在哪个生命周期内调用异步请求

可以在钩子函数 createdbeforeMountmounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

  • 能更快获取到服务端数据,减少页面 loading 时间;
  • ssr服务端渲染不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;

10.3 什么阶段可以操作DOM

在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM

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

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

  • 一般结合路由和动态组件一起使用,用于缓存组件
  • 提供 includeexclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
  • 对应两个钩子函数 activateddeactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

12. 组件之间的通信方式

  • 父子传值
    // 父组件
    <son abc='123' :def='name'></son>
    // 子组件
    props: ['abc','def']  // props的用法与data用法一致
    
  • 子父传值
    // 子组件
    this.$emit('abc',参数1...)
    // 父组件
    <son @abc='fn'></son>
    methods:{
        fn(参数1...){}
    }
    
  • 非父子传值
    // eventBus
    // 原理就是new一个新的Vue实例当容器
    const eventBus = new Vue()
    export default eventBus
    
    // 发送方
    eventBus.$emit('事件名',参数1...)
    // 创建事件方
    eventBus.$on('事件名'function(参数1...){}) // 一般用在钩子函数里注册 这样发送方触发就会执行使用
    
    // vuex 第二种传值
    

13. vuex

五个属性:

  • state:存储数据,存储状态;在根实例中注册了store 后,用 this.$store.state 来访问;对应vue里面的data;存放数据方式为响应式,vue组件从store中读取数据,如数据发生变化,组件也会对应的更新。
  • getter:可以认为是 store 的计算属性,它的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
  • mutation:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
  • action:包含任意异步操作,通过提交 mutation 间接更变状态。
  • module:将 store 分割成模块,每个模块都具有state、mutation、action、getter、甚至是嵌套子模块。
// ========== main.js
import store from './store'
new Vue({
    store
})
// ========== store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    // 1. state  相当于data
    state: {count:10},
    // 2. getters  相当于computeds
    getters: {},
    // 3. mutations  相当于methods
    // this.$store.commit("方法名",...)
    mutations: {
        addCount: (state, 参数)=>{
            state.count += 参数
        }
    }
    // 4. actions 处理异步方法
    // this.$store.dispatch('异步方法名',...)
    actions: {
        asyncAddCount: (context, 参数)=>{
            setTimeout(()=>{
                context.commit('addCount', 参数)
            },1000)
        }
    },
    modules:{
        m1: {
            namespaced:true// state,getters,mutations,actions都存在
            // 使用 this.$store.state.模块名.数据名
        }
    }
})

14.浅谈SSR

服务端渲染 SSR(server side render):就是 vue 在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的 html 片段直接返回给客户端这个过程就叫做服务端渲染。

  1. 优点

    • 更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
    • 首屏加载更快: SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
  2. 缺点

    • 更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
    • 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略
  • React: Next.js
  • Vue: Nuxt.js

15.vue-router 路由模式

- hash:  使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
- history:  依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
- abstract:  支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。

hash 和 history 路由模式实现原理

  • hash模式实现原理

    hashchange 事件来检测 location.hash(#后面的值) 值的变化来改变页面的渲染

  • history模式实现原理

    HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState()history.repalceState()。使用 popstate 事件来监听 url 的变化

vue-router有哪几种导航钩子

  • 全局守卫:beforeEach、beforeResolve、afterEach
  • 独享守卫:beforeEnter
  • 组件内守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

16.虚拟 DOM 实现原理

  • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
  • diff 算法 — 比较两棵虚拟 DOM 树的差异;
  • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

vue项目的优化

可以参考一篇文章《 Vue 项目性能优化 — 实践指南 》,文章主要介绍从 3 个大方面,22 个小方面详细讲解如何进行 Vue 项目的优化。

  • 1.代码层面的优化
    • v-if 和 v-show 区分使用场景
    • computed 和 watch 区分使用场景
    • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
    • 长列表性能优化
    • 事件的销毁
    • 图片资源懒加载
    • 路由懒加载
    • 第三方插件的按需引入
    • 优化无限列表性能
    • 服务端渲染 SSR or 预渲染
  • 2.Webpack 层面的优化
    • Webpack 对图片进行压缩
    • 减少 ES6 转为 ES5 的冗余代码
    • 提取公共代码
    • 模板预编译
    • 提取组件的 CSS
    • 优化 SourceMap
    • 构建结果输出分析
    • Vue 项目的编译优化
  • 3.基础的 Web 技术的优化
    • 开启 gzip 压缩
    • 浏览器缓存
    • CDN 的使用
    • 使用 Chrome Performance 查找性能瓶颈

vue基础

如果没有对 Vue 基础没有进行过整理的同学,可以参考本文作者的另一篇文章 《vue基础笔记》