Vue面试中,常常会被问到的面试题/知识点(2019改进版)

在初版的基础上进行了优化,新增一些面试题/知识点,对一些知识点进行更加深刻的描述。javascript

1、对于MVVM的理解?

MVVM 是 Model-View-ViewModel 的缩写。
Model表明数据模型,也能够在Model中定义数据修改和操做的业务逻辑。
View 表明UI 组件,它负责将数据模型转化成UI 展示出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,链接Model和View。
在MVVM架构下,View 和 Model 之间并无直接的联系,而是经过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 所以View 数据的变化会同步到Model中,而Model 数据的变化也会当即反应到View 上。
ViewModel 经过双向数据绑定把 View 层和 Model 层链接了起来,而View 和 Model 之间的同步工做彻底是自动的,无需人为干涉,所以开发者只需关注业务逻辑,不须要手动操做DOM, 不须要关注数据状态的同步问题,复杂的数据状态维护彻底由 MVVM 来统一管理。css

MVVM的优缺点?
优势:
分离视图(View)和模型(Model),下降代码耦合,提升视图或者逻辑的重用性: 好比视图(View)能够独立于Model变化和修改,一个ViewModel能够绑定不一样的"View"上,当View变化的时候Model不能够不变,当Model变化的时候View也能够不变。你能够把一些视图逻辑放在一个ViewModel里面,让不少view重用这段视图逻辑
提升可测试性: ViewModel的存在能够帮助开发者更好地编写测试代码
自动更新dom: 利用双向绑定,数据更新后视图自动更新,让开发者从繁琐的手动dom中解放
缺点:
Bug很难被调试: 由于使用双向绑定的模式,当你看到界面异常了,有多是你View的代码有Bug,也多是Model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的
一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就形成了花费更多的内存
对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。html

2、Vue的生命周期

beforeCreate(建立前) 在数据观测和初始化事件还未开始
created(建立后) 完成数据观测,属性和方法的运算,初始化事件,$el属性尚未显示出来
beforeMount(载入前) 在挂载开始以前被调用,相关的render函数首次被调用。实例已完成如下的配置:编译模板,把data里面的数据和模板生成html。注意此时尚未挂载html到页面上。
mounted(载入后) 在el 被新建立的 vm.$el 替换,并挂载到实例上去以后调用。实例已完成如下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程当中进行ajax交互。
beforeUpdate(更新前) 在数据更新以前调用,发生在虚拟DOM从新渲染和打补丁以前。能够在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated(更新后) 在因为数据更改致使的虚拟DOM从新渲染和打补丁以后调用。调用时,组件DOM已经更新,因此能够执行依赖于DOM的操做。然而在大多数状况下,应该避免在此期间更改状态,由于这可能会致使更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy(销毁前) 在实例销毁以前调用。实例仍然彻底可用。
destroyed(销毁后) 在实例销毁以后调用。调用后,全部的事件监听器会被移除,全部的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
1.什么是vue生命周期?
答: Vue 实例从建立到销毁的过程,就是生命周期。从开始建立、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。前端

2.vue生命周期的做用是什么?
答:它的生命周期中有多个事件钩子,让咱们在控制整个Vue实例的过程时更容易造成好的逻辑。vue

3.vue生命周期总共有几个阶段?
答:它能够总共分为8个阶段:建立前/后, 载入前/后,更新前/后,销毁前/销毁后。java

4.第一次页面加载会触发哪几个钩子?
答:会触发 下面这几个beforeCreate, created, beforeMount, mounted 。node

5.DOM 渲染在 哪一个周期中就已经完成?
答:DOM 渲染在 mounted 中就已经完成了。面试

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

vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,经过Object.defineProperty()来劫持各个属性的setter,getter,在数据变更时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来做为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,可是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。ajax

vue的数据双向绑定 将MVVM做为数据绑定的入口,整合Observer,Compile和Watcher三者,经过Observer来监听本身的model的数据变化,经过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通讯桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变动双向绑定效果。正则表达式

js实现简单的双向绑定

<body>
    <div id="app">
    <input type="text" id="txt">
    <p id="show"></p>
</div>
</body>
<script type="text/javascript">
    var obj = {}
    Object.defineProperty(obj, 'txt', {
        get: function () {
            return obj
        },
        set: function (newValue) {
            document.getElementById('txt').value = newValue
            document.getElementById('show').innerHTML = newValue
        }
    })
    document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value
    })
</script>

4、Vue组件间的参数传递

Vue 组件间通讯只要指如下 3 类通讯:父子组件通讯、隔代组件通讯、兄弟组件通讯
(1)props / $emit 适用 父子组件通讯
这种方法是 Vue 组件的基础,相信大部分同窗耳闻能详,因此此处就不举例展开介绍。
(2)ref 与 $parent / $children 适用 父子组件通讯
ref:若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
(3)EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通讯
这种方法经过一个空的 Vue 实例做为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通讯,包括父子、隔代、兄弟组件。
(4)$attrs/$listeners 适用于 隔代组件通讯
$attrs:包含了父做用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含全部父做用域的绑定 ( class 和 style 除外 ),而且能够经过 v-bind="$attrs" 传入内部组件。一般配合 inheritAttrs 选项一块儿使用。
$listeners:包含了父做用域中的 (不含 .native 修饰器的) v-on 事件监听器。它能够经过 v-on="$listeners" 传入内部组件

(5)provide / inject 适用于 隔代组件通讯
祖先组件中经过 provider 来提供变量,而后在子孙组件中经过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通讯问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间创建了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通讯
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地获得高效更新。
改变 store 中的状态的惟一途径就是显式地提交 (commit) mutation。这样能够方便地跟踪每个状态的变化。

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

hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特色:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动做,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号以前的内容会被包含在请求中,如 http://www.xxx.com,所以对于后端来讲,即便没有作到对路由的全覆盖,也不会返回 404 错误。
hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。好比下面这个网站,它的 location.hash 的值为 '#search':

https://www.abc.com#search

hash 路由模式的实现主要是基于下面几个特性:
URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
hash 值的改变,都会在浏览器的访问历史中增长一个记录。所以咱们能经过浏览器的回退、前进按钮控制hash 的切换;
能够经过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用  JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
咱们可使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()能够对浏览器历史记录栈进行修改,以及popState事件的监听到状态变动。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端若是缺乏对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还须要后台配置支持……因此呢,你要在服务端增长一个覆盖全部状况的候选资源:若是 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变化。其中作最主要的 API 有如下两个:history.pushState() 和 history.repalceState()。这两个 API 能够在不进行刷新的状况下,操做浏览器的历史纪录。惟一不一样的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,以下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
history 路由模式的实现主要基于存在下面几个特性:
pushState 和 repalceState 两个 API 来操做实现 URL 的变化 ;
咱们可使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时咱们须要手动触发页面跳转(渲染)。

abstract模式 : 支持全部 JavaScript 运行环境,如 Node.js 服务器端。若是发现没有浏览器的 API,路由会自动强制进入这个模式.
(后续补上)

6、说说对v-model的了解?

咱们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上建立双向数据绑定,咱们知道 v-model 本质上不过是语法糖,v-model 在内部为不一样的输入元素使用不一样的属性并抛出不一样的事件:
text 和 textarea 元素使用 value 属性和 input 事件;
checkbox 和 radio 使用 checked 属性和 change 事件;
select 字段将 value 做为 prop 并将 change 做为事件。
以 input 表单元素为例:

<input v-model='something'>

至关于

<input v-bind:value="something" v-on:input="something = $event.target.value">

若是在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,以下所示:
父组件:

<ModelChild v-model="message"></ModelChild>

子组件:

<div>{{value}}</div>

props:{
    value: String
},
methods: {
  test1(){
     this.$emit('input', '小红')
  },
},

7、vue路由的钩子函数

首页能够控制导航跳转,beforeEach,afterEach等,通常用于页面title的修改。一些须要登陆才能调整页面的重定向功能。

beforeEach主要有3个参数to,from,next:

to:route即将进入的目标路由对象,

from:route当前导航正要离开的路由

next:function必定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。能够控制网页的跳转。

8、vuex是什么?怎么使用?哪一种功能场景使用它?

只用来读取的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。
在main.js引入store,注入。新建了一个目录store,….. export 。
场景有:单页应用中,组件之间的状态、音乐播放、登陆状态、加入购物车
图片描述

state
Vuex 使用单一状态树,即每一个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不能够直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
相似vue的计算属性,主要用来过滤一些数据。
action
actions能够理解为经过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操做数据。view 层经过 store.dispath 来分发 action。

const store = new Vuex.Store({ //store实例
      state: {
         count: 0
             },
      mutations: {                
         increment (state) {
          state.count++
         }
          },
      actions: { 
         increment (context) {
          context.commit('increment')
   }
 }
})

modules
项目特别复杂的时候,可让每个模块拥有本身的state、mutation、action、getters,使得结构很是清晰,方便管理。

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
 }
const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
 }

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
})

9、vue-cli如何新增自定义指令?

1.建立局部指令

var app = new Vue({
    el: '#app',
    data: {    
    },
    // 建立指令(能够多个)
    directives: {
        // 指令名称
        dir1: {
            inserted(el) {
                // 指令中第一个参数是当前使用指令的DOM
                console.log(el);
                console.log(arguments);
                // 对DOM进行操做
                el.style.width = '200px';
                el.style.height = '200px';
                el.style.background = '#000';
            }
        }
    }
})

2.全局指令

Vue.directive('dir2', {
    inserted(el) {
        console.log(el);
    }
})

3.指令的使用

<div id="app">
    <div v-dir1></div>
    <div v-dir2></div>
</div>

10、vue如何自定义一个过滤器?

html代码:

<div id="app">
     <input type="text" v-model="msg" />
     {{msg| capitalize }}
</div>

JS代码:

var vm=new Vue({
    el:"#app",
    data:{
        msg:''
    },
    filters: {
      capitalize: function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
      }
    }
})

全局定义过滤器

Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

过滤器接收表达式的值 (msg) 做为第一个参数。capitalize 过滤器将会收到 msg的值做为第一个参数。

11、对keep-alive 的了解?

keep-alive是 Vue 内置的一个组件,可使被包含的组件保留状态,或避免从新渲染。
在vue 2.1.0 版本以后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。

使用方法

<keep-alive include='include_components' exclude='exclude_components'>
  <component>
    <!-- 该组件是否缓存取决于include和exclude属性 -->
  </component>
</keep-alive>

参数解释
include - 字符串或正则表达式,只有名称匹配的组件会被缓存
exclude - 字符串或正则表达式,任何名称匹配的组件都不会被缓存
include 和 exclude 的属性容许组件有条件地缓存。两者均可以用“,”分隔字符串、正则表达式、数组。当使用正则或者是数组时,要记得使用v-bind 。

使用示例

<!-- 逗号分隔字符串,只有组件a与b被缓存。 -->
<keep-alive include="a,b">
  <component></component>
</keep-alive>

<!-- 正则表达式 (须要使用 v-bind,符合匹配规则的都会被缓存) -->
<keep-alive :include="/a|b/">
  <component></component>
</keep-alive>

<!-- Array (须要使用 v-bind,被包含的都会被缓存) -->
<keep-alive :include="['a', 'b']">
  <component></component>
</keep-alive>

12、对 SPA 单页面的理解,它的优缺点分别是什么?

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会由于用户的操做而进行页面的从新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的从新加载。
优势:
用户体验好、快,内容的改变不须要从新加载整个页面,避免了没必要要的跳转和重复渲染;
基于上面一点,SPA 相对对服务器压力小;
先后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
初次加载耗时多:为实现单页 Web 应用功能及显示效果,须要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
前进后退路由管理:因为单页应用在一个页面中显示全部的内容,因此不能使用浏览器的前进后退功能,全部的页面切换须要本身创建堆栈管理;
SEO 难度较大:因为全部的内容都在一个页面中动态替换显示,因此在 SEO 上其有着自然的弱势。

十3、Class 与 Style 如何动态绑定?

Class 能够经过对象语法和数组语法进行动态绑定:

对象语法:

<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>

data: {
  isActive: true,
  hasError: false
}

数组语法:

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

Style 也能够经过对象语法和数组语法进行动态绑定:

对象语法:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

data: {
  activeColor: 'red',
  fontSize: 30
}

数组语法:

<div v-bind:style="[styleColor, styleSize]"></div>

data: {
  styleColor: {
     color: 'red'
   },
  styleSize:{
     fontSize:'23px'
  }
}

十4、理解 Vue 的单向数据流?

全部的 prop 都使得其父子 prop 之间造成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,可是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而致使你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中全部的 prop 都将会刷新为最新的值。这意味着你不该该在一个子组件内部改变 prop。若是你这样作了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能经过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
有两种常见的试图改变一个 prop 的情形 :

这个 prop 用来传递一个初始值;这个子组件接下来但愿将其做为一个本地的 prop 数据来使用。 在这种状况下,最好定义一个本地的 data 属性并将这个 prop 用做其初始值:

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

这个 prop 以一种原始的值传入且须要进行转换。 在这种状况下,最好使用这个 prop 的值来定义一个计算属性

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

十5、computed 和 watch 的区别和运用的场景?

computed: 是计算属性,依赖其它属性值,而且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会从新计算 computed 的值;
watch: 更多的是「观察」的做用,相似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操做;
运用场景:
当咱们须要进行数值计算,而且依赖于其它数据时,应该使用 computed,由于能够利用 computed 的缓存特性,避免每次获取值时,都要从新计算;
当咱们须要在数据变化时执行异步或开销较大的操做时,应该使用 watch,使用 watch 选项容许咱们执行异步操做 ( 访问一个 API ),限制咱们执行该操做的频率,并在咱们获得最终结果前,设置中间状态。这些都是计算属性没法作到的。

十6、直接给一个数组项赋值,Vue 能检测到变化吗?

因为 JavaScript 的限制,Vue 不能检测到如下数组的变更:
当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength

为了解决第一个问题,Vue 提供了如下操做方法:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

为了解决第二个问题,Vue 提供了如下操做方法:

// Array.prototype.splice
vm.items.splice(newLength)

十7、在哪一个生命周期内调用异步请求?

能够在钩子函数 created、beforeMount、mounted 中进行调用,由于在这三个钩子函数中,data 已经建立,能够将服务端端返回的数据进行赋值。可是本人推荐在 created 钩子函数中调用异步请求,由于在 created 钩子函数中调用异步请求有如下优势:

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

十8、父组件能够监听到子组件的生命周期吗?

好比有父组件 Parent 和子组件 Child,若是父组件监听到子组件挂载 mounted 就作一些逻辑处理,能够经过如下写法实现:

// Parent.vue
<Child @mounted="doSomething"/>
    
// Child.vue
mounted() {
  this.$emit("mounted");
}

以上须要手动经过 $emit 触发父组件的事件,更简单的方式能够在父组件引用子组件时经过 @hook 来监听便可,以下所示:

//  Parent.vue
<Child @hook:mounted="doSomething" ></Child>

doSomething() {
   console.log('父组件监听到 mounted 钩子函数 ...');
},

//  Child.vue
mounted(){
   console.log('子组件触发 mounted 钩子函数 ...');
},        
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...

固然 @hook 方法不只仅是能够监听 mounted,其它的生命周期事件,例如:created,updated 等均可以监听

十9、组件中 data 为何是一个函数?

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

二10、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?

受现代 JavaScript 的限制 ,Vue 没法检测到对象属性的添加或删除。因为 Vue 会在初始化实例时对属性执行 getter/setter 转化,因此属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。可是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 来实现为对象添加响应式属性,那框架自己是如何实现的呢?

Vue 源码
export function set (target: Array<any> | Object, key: any, val: any): any {
  // target 为数组  
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 修改数组的长度, 避免索引>数组长度致使splcie()执行有误
    target.length = Math.max(target.length, key)
    // 利用数组的splice变异方法触发响应式  
    target.splice(key, 1, val)
    return val
  }
  // key 已经存在,直接修改属性值  
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  // target 自己就不是响应式数据, 直接赋值
  if (!ob) {
    target[key] = val
    return val
  }
  // 对属性进行响应式处理
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

阅读以上源码可知,vm.$set 的实现原理是:
若是目标是数组,直接使用数组的 splice 方法触发相应式;
若是目标是对象,会先判读属性是否存在、对象是不是响应式,最终若是要对属性进行响应式处理,则是经过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)

二11、Vue 中的 key 有什么做用?

key 是为 Vue 中 vnode 的惟一标记,经过这个 key,咱们的 diff 操做能够更准确、更快速。
Vue 的 diff 过程能够归纳为:oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,若是以上 4 种比较都没匹配,若是设置了key,就会用 key 再进行比较,在比较的过程当中,遍历会往中间靠,一旦 StartIdx > EndIdx 代表 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。
因此 Vue 中 key 的做用是:key 是为 Vue 中 vnode 的惟一标记,经过这个 key,咱们的 diff 操做能够更准确、更快速。

更准确:由于带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中能够避免就地复用的状况。因此会更加准确。
更快速:利用 key 的惟一性生成 map 对象来获取对应节点,比遍历方式更快,源码以下:

function createKeyToOldIdx (children, beginIdx, endIdx) {
  let i, key
  const map = {}
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key
    if (isDef(key)) map[key] = i
  }
  return map
}

二12、说说对于 SSR了解?有没有使用过?

Vue.js 是构建客户端应用程序的框架。默认状况下,能够在浏览器中输出 Vue 组件,进行生成 DOM 和操做 DOM。然而,也能够将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上彻底可交互的应用程序。
即:SSR大体的意思就是vue在客户端将标签渲染成的整个 html 片断的工做在服务端完成,服务端造成的html 片断直接返回给客户端这个过程就叫作服务端渲染。
优势:
更好的 SEO: 由于 SPA 页面的内容是经过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,因此在 SPA 中是抓取不到页面经过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),因此搜索引擎爬取工具能够抓取渲染好的页面;
更快的内容到达时间(首屏加载更快): SPA 会等待全部 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等须要必定的时间等,因此首屏渲染须要必定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,因此 SSR 有更快的内容到达时间;
缺点:
更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会致使一些外部扩展库须要特殊处理,才能在服务端渲染应用程序中运行;而且与能够部署在任何静态文件服务器上的彻底静态单页面应用程序 SPA 不一样,服务端渲染应用程序,须要处于 Node.js server 运行环境;
更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源。

二十3、服务端渲染 SSR or 预渲染

服务端渲染是指 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 资源,所以若是你预料在高流量环境下使用,请准备相应的服务器负载,并明智地采用缓存策略。
若是你的项目的 SEO 和 首屏渲染是评价项目的关键指标,那么你的项目就须要服务端渲染来帮助你实现最佳的初始加载性能和 SEO。若是你的 Vue 项目只需改善少数营销页面(例如  /products, /about, /contact 等)的 SEO,那么你可能须要预渲染,在构建时 (build time) 简单地生成针对特定路由的静态 HTML 文件。优势是设置预渲染更简单,并能够将你的前端做为一个彻底静态的站点,具体你可使用 prerender-spa-plugin 就能够轻松地添加预渲染 。

二十4、虚拟 DOM 的优缺点?

优势:
保证性能下限: 框架的虚拟 DOM 须要适配任何上层 API 可能产生的操做,它的一些 DOM 操做的实现必须是普适的,因此它的性能并非最优的;可是比起粗暴的 DOM 操做性能要好不少,所以框架的虚拟 DOM 至少能够保证在你不须要手动优化的状况下,依然能够提供还不错的性能,即保证性能的下限;
无需手动操做 DOM: 咱们再也不须要手动去操做 DOM,只须要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮咱们以可预期的方式更新视图,极大提升咱们的开发效率;
跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 能够进行更方便地跨平台操做,例如服务器渲染、weex 开发等等。
缺点:
没法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 没法进行针对性的极致优化。

二十5、虚拟 DOM 实现原理?

虚拟 DOM 的实现原理主要包括如下 3 部分:
①用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
②diff 算法 — 比较两棵虚拟 DOM 树的差别;
③pach 算法 — 将两个虚拟 DOM 对象的差别应用到真正的 DOM 树。

二十6、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 查找性能瓶颈

二十7、vue3.0 特性的了解

1.监测机制的改变
Vue3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的不少限制:①只能监测属性,不能监测对象;②检测属性的添加和删除;③检测数组索引和长度的变动;④支持 Map、Set、WeakMap 和 WeakSet。
新的 observer 还提供了如下特性:
用于建立 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
默认采用惰性观察。在 2.x 中,无论反应式数据有多大,都会在启动时被观察到。若是你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
更精确的变动通知。在 2.x 中,经过 Vue.set 强制添加新属性将致使依赖于该对象的 watcher 收到变动通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
不可变的 observable:咱们能够建立值的“不可变”版本(即便是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树之外的变化。
更好的调试功能:咱们可使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在何时以及为何从新渲染。

2.模板
模板方面没有大的变动,只改了做用域插槽,2.x 的机制致使做用域插槽变了,父组件会从新渲染,而 3.0 把做用域插槽改为了函数的方式,这样只会影响子组件的从新渲染,提高了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。
3.对象式的组件声明方式
vue2.x 中的组件是经过声明的方式传入一系列 option,和 TypeScript 的结合须要经过一些装饰器的方式来作,虽然能实现功能,可是比较麻烦。3.0 修改了组件的声明方式,改为了类式的写法,这样使得和 TypeScript 的结合变得很容易。
此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂以后,必须有一个静态类型系统来作一些辅助管理。如今 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实颇有必要。
4.其它方面的更改
vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其它的更改:
支持自定义渲染器,从而使得 weex 能够经过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
支持 Fragment(多个根节点)和 Protal(在 dom 其它部分渲染组建内容)组件,针对一些特殊的场景作了处理。
基于 treeshaking 优化,提供了更多的内置功能。

二十8、Vue路由跳转方式有哪几种?

1.router-link

**不带参数** 
<router-link :to="{name:'home'}"> 
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name  
// 注意:router-link中连接若是是'/'开始就是从根路由开始,若是开始不带'/',则从当前路由开始。 
带参数
<router-link :to="{name:'home', params: {id:1}}">       
// params传参数 (相似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id" 
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留     
// html 取参  $route.params.id
// script 取参  this.$route.params.id 
<router-link :to="{name:'home', query: {id:1}}"> 
// query传参数 (相似get,url后面会显示参数)
// 路由可不配置
// html 取参  $route.query.id
// script 取参  this.$route.query.id

2.this.$router.push() (函数里面调用)

不带参数 
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
query传参 
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}}) 
// html 取参  $route.query.id
// script 取参  this.$route.query.id

2.1 params传参

this.$router.push({name:'home',params: {id:'1'}})  // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参  $route.params.id
// script 取参  this.$route.params.id

2.2 query和params区别

query相似 get, 跳转以后页面 url后面会拼接参数,相似?id=1, 非重要性的能够这样传, 密码之类仍是用params刷新页面id还在 
params相似 post, 跳转以后页面 url后面不会拼接参数 , 可是刷新页面id 会消失

3.this.$router.replace() (用法同上,push)

4. this.$router.go(n) ()

二十9、其它Vue面试题

1.css只在当前组件起做用
答:在style标签中写入scoped便可 例如:<style scoped></style>

2.v-if 和 v-show 区别
答:v-if按照条件是否渲染,v-show是display的block或none;

3.$route和$router的区别
答:$route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而$router是“路由实例”对象包括了路由的跳转方法,钩子函数等。

4.vue.js的两个核心是什么?
答:数据驱动、组件系统

5.vue几种经常使用的指令
答:v-for 、 v-if 、v-bind、v-on、v-show、v-else

6.vue经常使用的修饰符?
答:.prevent: 提交事件再也不重载页面;.stop: 阻止单击事件冒泡;.self: 当事件发生在该元素自己而不是子元素的时候会触发;.capture: 事件侦听,事件发生的时候会调用

7.v-on 能够绑定多个方法吗?
答:能够

8.vue中 key 值的做用?
答:当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。若是数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每一个元素,而且确保它在特定索引下显示已被渲染过的每一个元素。key的做用主要是为了高效的更新虚拟DOM。

9.什么是vue的计算属性?
答:在模板中放入太多的逻辑会让模板太重且难以维护,在须要对数据进行复杂处理,且可能屡次使用的状况下,尽可能采起计算属性的方式。好处:①使得数据处理结构清晰;②依赖于数据,数据更新,处理结果自动更新;③计算属性内部this指向vm实例;④在template调用时,直接写计算属性名便可;⑤经常使用的是getter方法,获取数据,也可使用set方法改变数据;⑥相较于methods,无论依赖的数据变不变,methods都会从新计算,可是依赖数据不变的时候computed从缓存中获取,不会从新计算。

10.vue等单页面应用及其优缺点
答:优势:Vue 的目标是经过尽量简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好。
缺点:不支持低版本的浏览器,最低只支持到IE9;不利于SEO的优化(若是要支持SEO,建议经过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可使用浏览器的导航按钮须要自行实现前进、后退。

11.怎么定义 vue-router 的动态路由? 怎么获取传过来的值
答:在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的 params.id 获取。

三10、有没有封装过功能组件?

面试官:聊聊,你写过最复杂的一个功能组件?遇到了哪些困难?最后是怎样解决的?

有部份内容来自网络,若有涉及侵权,私信联系删除。
Vue面试中,常常会被问到的面试题/Vue知识点整理(初版)
532道前端真实大厂面试题
学习ES6笔记──工做中经常使用到的ES6语法

相关文章
相关标签/搜索