面试专题总结:Vue 知识总结

5,592 阅读15分钟

简介

希望读者依此构建自己的知识树(思维导图)

偷懒一下:可参考我自己总结思维导图 : 点这里

附带:高频面试题积累文档。 来自于(学长、牛客网等平台)

自己开发的博客地址:zxinc520.com

github地址: 点击

此篇 js - 【vue 知识总结】 知识点: 全部弄懂了,面试很容易。

我的博客【MVVM 和 vue】参考地址:地址

我的博客【虚拟 DOM】参考地址:地址

一、虚拟 dom

1、虚拟 dom是什么

  • virtual dom,虚拟 DOM
  • 用 JS 模拟 DOM 结构
  • DOM 变化的对比,放在 JS 层来做 ( 图灵完备语言 )
  • 优点:提高重绘性能

2、虚拟 dom存在的意义

  • DOM 操作非常 “昂贵”
  • 将 DOM 对比操作放在 JS 层,提高效率
  • 项目越复杂 ,影响就越严重

3、vdom 的如何应用,核心 API 是什么?

  • 介绍 snabbdom
    • snabbdom :一个 vdom 实现库。
  • 核心 API
    • h 函数
    • h 函数是生成虚拟 DOM 的函数
    • h('<标签名>',{ ... 属性 ... },[... 子元素 ...])
    • h('<标签名>',{ ... 属性 ... },[ ‘....’])
    • patch 函数
      • 使用 diff 算法来比较旧 VNode 及新的 VNode 之间的差异然后执行 Patch Operation 或者叫 Patch 函数来高效更新 Dom 节点。
      • patch(container,vnode)
      • patch(vnode,newVnode)

4、介绍 一下 diff 算法

  • 简介

    • diff 算法 一直在我们身边,并不是 Vue 和 React 创造出来的概念
  • vdom 为何用diff 算法

    • DOM 操作是 “昂贵”的,因此尽量减少DOM 操作
    • 找出本次 DOM 必须更新的节点来更新,其它的 不更新
    • 这个 “ 找出 ” 的过程,就需要 diff 算法
  • diff 实现过程

    • patch (container,vnode)

      • 核心 逻辑createElment

        function createElement(vnode) {
            let tag = vnode.tag
            let attrs = vnode.attr || {}
            let children = vnode.children || []
            let elem = document.createElement(tag)
            for (let attrName in attrs) {
                if (attr.hasOwnProperty(attrName)) {
                    elem.setAttribute(attrName, attrs[attrName])
                }
            }
            children.forEach((childVnode) => {
                elem.append(createElement(childVnode))
            })
            return elem
        }
        
        
    • patch (vnode,newVnode)

      • 核心 逻辑updataChildren

        function updateChildren(vnode, newVnode) {
            let children = vnode.children || []
            let newChildren = newVnode.children || []
            children.forEach((child, index) => {
                let newChild = newChildren[index]
                if (newChild == null) {
                    return
                }
                if (child.tag === newChild.tag) {
                    updateChildren(child, newChild)
                } else {
                    replaceNode(child, newChild)
                }
            })
        }
        
  • 介绍一下 diff 算法?

    • 知道什么是 diff 算法,是linux 的基础命令
    • vdom 中 应用 diff算法目的: 是为了 找出需要更新的节点
    • diff 实现:patch (container,vnode)和 patch (vnode,newVnode)
    • 核心 逻辑 , createElment 和 updataChildren

二、Vue 知识总结

5、框架和库的区别

  • ibrary(库)
    • 小而巧的库,只提供特定的API;优点就是 船小好调头。可以很方便的从一个库切换到另外的库;但是代码几乎不会改变。
  • Framework(框架)
    • 大而全的是框架;框架提供了一整套的解决方案;所以,如果在项目中间,想切换到另外的框架,是比较困难的。
  • 说一下 使用 jQuery 和 使用框架的区别
    1. 数据 和 视图 的分离,解耦( 开放封闭原则 )
    2. VUE 以数据驱动视图,只关心数据变化,DOM 操作被封装

6、Vue与Angular以及React的区别

  • vue与angular
    • AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
  • vue 与React
    • React采用的Virtual DOM会对渲染出来的结果做脏检查;Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM。

7、Vue概述

  • Vue是什么
    • 是一个渐进式的构建用户界面的js框架
  • where
    • 小到的简单的表单处理,大到复杂的数据操作比较频繁的单页面应用程序
  • 为什么用VUE
    • 数据驱动
    • 组件化
  • 工作方式
    • 可以通过丰富的指令扩展模板,可以通过各种各样的插件来增强功能

8、MVVM框架模式

  • 概念
    • MVVM是Model-View-ViewModel的简写。是一个软件架构设计模式,是一种简化用户界面的事件驱动编程方式。
  • 每个字母解析
    • Model 代表数据模型
    • View 代表视图,它负责将数据模型转化成UI 展现出来
    • 连接 Model 和 View,简单理解就是一个同步View 和 Model的对象,连接Model和View

9、Vue 三要素 *

9.1、响应式:vue 如何监听到 data 的每个属性变化?

响应式中observe还有dep 还有wather 各自都分担什么任务

【observe类】【dep对象】【wather 执行者】--- 弄清楚

  • Vue 响应式是什么

    • data 属性被代理到 vm 上
  • Object.defineProperty(双向数据绑定)

    • 定义
      • Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
    • 缺点 【为什么Vue3.x 升级使用 Proxy 取代 Object.defineProperty】
      • Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
      • Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
      • Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
  • data 属性变化过程

    1. 修改属性,被响应式 的 set 监听到
    2. set 中执行 updataComponent ( 异步 )
    3. updataComponent 重新执行 vm.render()
    4. 生成的 vnode 和 prevVnode,通过 patch 进行比较
    5. 渲染到 html 中
  • 模拟实现 Vue 如何监听data

    let vm={}
    let data={
        name:'zc',
        age:'12'
    }
    for(let key in data){
        if(data.hasOwnProperty(key)){
            Object.defineProperty(vm,key,{
                get:function () {
                    return data[key]
                },
                set:function (newValue) {
                    data[key] = newValue
                }
            })
        }
    }
    

9.2、模板引擎:vue 的模板如何被解析,指令如何处理?

  • 模板是什么?
    • 本质:字符串
    • 有逻辑,如 v-if 、v-for 等
    • 与 html 格式很像,但有很大区别
    • 最终还要转换为 html 来显示
  • 模板最终必须转化成 JS 代码,因为
    • 有逻辑(v-if、v-for),必须用 JS 才能实现 ( 图灵完备 )
    • 转换成 html 渲染页面,必须用 JS 才能实现
    • 因此,模板最重要转换成 一个 JS 函数 (render 函数)
  • vue 中如何解析 模板
    • 概述
      • 模板必须转换成 render 函数 (编译的第一步是将模板通过 parse 函数解析成 AST(抽象语法树),第二步优化AST(检测出不需要更改的DOM的纯静态子树),第三步根据优化后的抽象语法树生成包含渲染函数字符串的对象。)
      • 具体
        1. 第一步是将模板通过 parse 函数解析成 AST(抽象语法树)
          • 解析成AST:parse 函数【源码】
          • 在解析模版的过程中,不断触发各种钩子函数,将节点信息通过 start, end 和 chars ,comment方法传递给 Vue.js 的 ast 构建程序,边解析边同时构建模版的 ast。
        2. 第二步优化AST(检测出不需要更改的DOM的纯静态子树)
          • 优化AST:optimize 函数【源码】
        3. 第三步根据优化后的抽象语法树生成包含渲染函数字符串的对象。
          • 生成渲染函数:generate 函数【源码】

9.3、渲染:vue的模板如何被渲染成html?以及渲染过程

  • render 函数 与 vdom

    • updataComponent 中实现了生成 vdom 的 patch

    • 页面首次渲染执行 updataComponent

    • render 函数 - with 的用法

      • 总结

        • 模板中所有信息都包含在了 render 函数中
        • this 即 vm
        • price 即 this.price 即 vm.price ,即 data 中的 price
        • _c 即 this. _c 即 vm. _c
      • 使用

        // 使用 with
        function fn1() {
            with (obj) {
                console.log(name)
                console.log(age)
                getAddress()
            }
        }
        
    • data 中每次修改属性,执行 updataComponent

10、vue 的整个实现流程?

  • 第一步:解析模板成 render 函数
  • 第二步:响应式开始监听
  • 第三步:首次渲染,显示页面,且绑定依赖
  • 第四步:data 属性变化,触发 rerender

11、Vue 生命周期

  • 生命周期是什么
    • Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
  • 作用
    • 它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
  • vue生命周期总共有几个阶段?
    • 它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
  • 第一次页面加载会触发哪几个钩子?
    • 第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
  • 简单描述每个周期具体适合哪些场景?
    • beforecreate:可以在这加个loading事件,在加载实例时触发
    • created:初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
    • mounted:挂载元素,获取到DOM节点
    • updated:如果对数据统一处理,在这里写上相应函数
    • beforeDestroy:可以做一个确认停止事件的确认框 nextTick : 更新数据后立即操作dom
  • 钩子函数
    1. beforeCreate :组件实例刚创建,data和methods 中的数据未被初始化
    2. created:组件实例创建完成,data 和 methods 都已经被初始化好了!但DOM还未生成
    3. beforeMount:模板编译/挂载之前
    4. mounted:模板编译/挂载之后
    5. beforeUpdate:组件更新之前
    6. updated:组件更新之后
    7. beforeDestroy:组件销毁前调用
    8. destroyed:组件销毁后调用

12、Vue 组件间的通信(父亲,兄弟,爷孙 等等)

六种方式

  1. props/$emit

    • 父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。
    • 适用范围:父子组件中
  2. emit/on

    • 这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

    • 具体实现方式

       var Event=new Vue();
          Event.$emit(事件名,数据);
          Event.$on(事件名,data => {});
      
    • 适用范围:包括父子、兄弟、跨级

  3. vuex

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

    • 多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----attrs/listeners
  5. provide/inject

    • 以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
  6. parent /children与 ref

    • 弊端:两种方法的弊端是,无法在跨级或兄弟间通信。

常见使用场景可以分为三类

  • 父子通信
    • 父向子传递数据是通过 props,子向父是通过 events(emit);通过父链 / 子链也可以通信(parent / children);ref 也可以访问组件实例;provide / inject API;attrs/$listeners
  • 兄弟通信
    • Bus;Vuex
  • 跨级通信
    • Bus;Vuex;provide / inject API、attrs/listeners

13、Vuex

  • 定义

    • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  • Vuex 背后的基本思想

    • 把组件的共享状态抽取出来,以一个全局单例模式管理,在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
  • Vuex 和单纯的全局对象有以下两点不同

    1. Vuex 的状态存储是响应式的。
    2. 你不能直接改变 store 中的状态。该变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。是因为我们想要更明确地追踪到状态的变化。
  • vuex的组成

    1. state

      • Vuex 使用 state来存储应用中需要共享的状态。为了能让 Vue 组件在 state更改后也随着更改,需要基于state创建计算属性。
    2. getters

      • 可以认为是 store 的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
    3. mutations(同步)

      • 是改变状态的执行者,mutations用于同步地更改状态

        store.commit({
          type: 'increment',
          amount: 10   //这是额外的参数
        })
        
    4. actions(异步)

      • 异步地更改状态,action并不直接改变state,而是发起mutation

        // 以对象形式分发
        store.dispatch({
          type: 'incrementAsync',
          amount: 10
        })
        
        // 以载荷形式分发
        store.dispatch('incrementAsync', {
          amount: 10
        })
        
    5. Module: 模块化store

      • 出现的问题
        • 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
      • 解决方案
        • Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块

vuex的工作流程

  1. 数据从state中渲染到页面
  2. 在页面通过dispatch来触发action
  3. action通过调用commit,来触发mutation
  4. mutation来更改数据,数据变更之后会触发dep对象的notify,通知所有Watcher对象去修改对应视图(vue的双向数据绑定原理)

应用场景

  • 多个视图依赖于同一状态
  • 来自不同视图的行为需要改变同一个状态

14、Vue的路由(vue-router)

Vue的路由的实现原理 【必须得很清楚】

  • vue-router

    • vue-router是什么

      • Vue Router 是 Vue.js 官方的路由管理器
    • 哪些组件

      1. <router-link >

      2. <router-view>

      3. <keep-alive >

        • 定义

          • keepalive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染 。也就是所谓的-----组件缓存
        • 组件缓存

          • 使用 include/exclude

            • include
            • exclude
          • 增加 router.meta 属性

            meta: {
                  keepAlive: true // 需要被缓存
                }
            
        • activated 当 keepalive 包含的组件再次渲染的时候触发

        • deactivated 当 keepalive 包含的组件销毁的时候触发

  • 动态路由

    • 创建:主要是使用path属性过程中,使用动态路径参数,以冒号开头

    • 响应路由参数的变化

      1. this.$route.params

      2. watch (监测变化) $route 对象

        watch: {
          $route(to, from){
            console.log(to.path)
            // 对路由变化做出响应
          }
        }
        
      3. 使用 2.2 中引入的 beforeRouteUpdate 导航守卫

  • 嵌套路由

    • 需要在 VueRouter 的参数中使用 children 配置
  • 导航

    • 声明式

      <router-link :to="...
      
    • 编程式

      router.push(...)
      
  • 重定向和别名

    • 重定向

      routes: [
          { path: '/a', redirect: '/b' }
        ]
      
    • 别名

      routes: [
          { path: '/a', component: A, alias: '/b' }
        ]
      
  • vue-router的两种模式

    • hash模式(默认)
      • 原理:原理是onhashchage事件,可以在window对象上监听这个事件
      • 描述:即通过在链接后添加 # + 路由名字,根据匹配这个字段的变化,触发 hashchange 事件,动态的渲染出页面。
    • history模式
      • 描述:利用了HTML5 History Interface 中新增的pushState()和replaceState()方法。
      • 缺点
        1. 需要后台配置支持
        2. 如果刷新时,服务器没有响应响应的资源,会刷出404
  • 导航钩子函数(导航守卫)

    • “导航”表示路由正在发生改变。
    • 作用
      • vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的
    • 导航守卫有哪些
      1. 全局前置守卫
        • router.beforeEach
      2. 全局解析守卫(2.5.0 新增)
        • router.beforeResolve
      3. 全局后置钩子
        • afterEach
        • 和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
      4. 路由独享的守卫
        • beforeEnter 守卫
      5. 组件内的守卫
        1. beforeRouteEnter :在渲染该组件的对应路由被 confirm 前调用
        2. beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用
        3. beforeRouteLeave:导航离开该组件的对应路由时调用
    • 相关题目
      1. vue-router是什么?有哪些组件?
      2. active-class 是哪个组件的属性?
        • active-class是router-link终端属性,用来做选中样式的切换,当router-link标签被点击时将会应用这个样式
      3. 怎么定义vue-router的动态路由?怎么获取传过来的值?
      4. vue-router有哪几种导航钩子?
      5. route和router的区别是什么?
        • router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等
        • route 是路由信息对象||跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数。
        • 传参是this.router,接收参数是this.route
      6. vue-router响应路由参数的变化
      7. vue-router 传参
      8. vue-router的两种模式
      9. vue-router实现路由懒加载(动态加载路由)
        • 把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应的组件即为路由的懒加载,可以加快项目的加载速度,提高效率

15、Mixin

  • Mixin是什么
    • 一种分发Vue组件中可复用功能的非常灵活的一种方式。
  • 什么时候使用Mixins
    • 页面的风格不用,但是执行的方法和需要的数据类似、

16、vue指令

  • 自定义指令
    • Vue.directive
  • 自定义一个过滤器
    • Vue.filter('过滤器的名称',function(){})

17、一句话就能回答的面试题

  1. css只在当前组件起作用
    • 在style标签中写入scoped即可 例如:<style scoped></style >
  2. v-if 和 v-show 区别
    • v-if按照条件是否渲染,v-show是display的block或none;
  3. vue.js的两个核心是什么?
    • 数据驱动、组件系统
  4. vue几种常用的指令
    • v-for 、 v-if 、v-bind、v-on、v-show、v-else
  5. vue常用的修饰符?
    • prevent: 提交事件不再重载页面;.stop: 阻止单击事件冒泡;.self: 当事件发生在该元素本身而不是子元素的时候会触发;.capture: 事件侦听,事件发生的时候会调用
  6. v-on 可以绑定多个方法吗?
    • 可以
  7. *vue中 key 值的作用?
    1. 更准确
      • 当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。使用key可以避免就地复用的情况。所以会更加准确
    2. 更快
      • 利用key的唯一性生成map对象来获取对应节点,比遍历方式更快
  8. 什么是vue的计算属性?
    • 在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。
    • 好处
      1. 使得数据处理结构清晰
      2. 依赖于数据,数据更新,处理结果自动更新
      3. 计算属性内部this指向vm实例
      4. 在template调用时,直接写计算属性名即可
      5. 常用的是getter方法,获取数据,也可以使用set方法改变数据
      6. 较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算
  9. vue等单页面应用( SPA)及其优缺点
    • 优点
      1. 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
      2. 基于上面一点,SPA 相对对服务器压力小
      3. 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
    • 缺点
      1. 不支持低版本的浏览器,最低只支持到IE9;
      2. 第一次加载首页耗时相对长一些
      3. 不可以使用浏览器的导航按钮需要自行实现前进、后退。
      4. 不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件)
  10. Vue 的父组件和子组件生命周期钩子函数执行顺序
    • 加载渲染过程
      • 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
    • 子组件更新过程
      • 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
    • 父组件更新过程
      • 父 beforeUpdate -> 父 updated
    • 销毁过程
      • 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
  11. Vue组件中 data 为什么是一个函数?
    • 因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题