Auc的个人博客

vuePress-theme-reco Auc    2020 - 2021
Auc的个人博客 Auc的个人博客

选择一个主题吧~

  • 暗黑
  • 自动
  • 明亮
主页
分类
  • JavaScript
  • Vue
  • 数据结构与算法
  • 文档
  • 面试题
  • 笔记
标签
笔记
  • CSS
  • ES6
  • JavaScript
  • Vue
  • C语言
文档
  • Vueのapi
  • Vue Router
  • axios
  • Vue CLI
面试
  • JS
  • Vue
  • 基础
时间线
联系我
  • GitHub (opens new window)
author-avatar

Auc

62

文章

11

标签

主页
分类
  • JavaScript
  • Vue
  • 数据结构与算法
  • 文档
  • 面试题
  • 笔记
标签
笔记
  • CSS
  • ES6
  • JavaScript
  • Vue
  • C语言
文档
  • Vueのapi
  • Vue Router
  • axios
  • Vue CLI
面试
  • JS
  • Vue
  • 基础
时间线
联系我
  • GitHub (opens new window)
  • Vue

    • Vue 自测基础面试题

Vue 自测基础面试题

vuePress-theme-reco Auc    2020 - 2021

Vue 自测基础面试题

Auc 2020-10-06 Vue面试题

# Vue 自测基础面试题

# 什么是 SPA, 有什么优缺点

# SPA

SPA(single-page application) 单页面应用程序,其仅在WEB 页面初始化时加载对应的 HTML、JavaScript、CSS 等资源,一旦页面加载完成,就不由用户的操作而对也页面进行重复加载,而是通过路由跳转实现 HTML 内容的互换,避免重复加载。

# 优点与缺点

  1. 优点
  • 用户体验好,网页整体加载速度快,WEB 网页的内容不需要加载整个页面,避免的资源的重复加载。
  • 可以减轻对服务器的压力
  • 前后端职责分离,构架清晰,服务端侧重于数据处理,客户端侧重于页面的交互逻辑
  1. 缺点
  • 首次加载较慢:由于其但单页面的特性,需要在首次加载网页时对于资源的同一进行加载。
  • 前进后退路由管理:由于 SPA 在一个页面中显示所有的内容,无法使用浏览器的前进后退功能,具体表现是点击前进后退会重新刷新页面,所有的页面都需要建立自己的堆栈管理
  • SEO 难度较大:由于所有的内容都需要在一个页面中动态的替换显示。

# 怎样理解 Vue 中的单向数据流

# 理解

  1. 在 Vue 的组件树中,父组件向子组件传递数据,这个数据称为单向数据流, prop 可以被多个子组件接收,子组件通过 props 选项接收父组件传递过来的 prop。这就说明一旦父组件中的关联的数据更改,其下与此数据关联的子组件中的 prop 也会随之更改。

  2. 但是,通过子组件更改 prop 从而修改父组件中的对应数据是不被允许的,原因是一但父组件中的这个数据更改,下面所有子组件的 prop 都会更改,这就导致应用的数据流向难以理解。

# 子组件变相修改 prop

由于单项数据流,所以在子组件中修改 prop 是不被允许的,但是可以变相修改,下面提供两个方案:

  1. 将 prop 值放在 data 选项中存储
props: ['givenData'];
data () {
  return {
    value: this.givenData
  }
};
1
2
3
4
5
6
  1. 使用计算属性
props: ['givenData'];
computed: {
  givenData () {
    return this.givenData.toUpperCase();
  }
}
1
2
3
4
5
6

# Vue 无法检测变化的数组操作

这里我之前写过一篇文章有过解释,点击传送门

# 说一下Vue的生命周期

# 简述什么是生命周期

Vue 实例有一个完整的生命周期,即从创建实例开始、初始化实例数据、编译模板、挂载 Dom ==> 更新 ==> 卸载

# 生命周期示意图

20201121152105

# 父组件和子组件生命周期钩子函数执行顺序

分为以下四个情况:

1. 加载渲染过程 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

2. 子组件更新过程 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

3. 父组件更新过程 父 beforeUpdate -> 父 updated

4. 销毁过程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

# 在哪个阶段可以放置异步请求

在created beforeMount mounted 里可以放置异步请求,原因是在此三个阶段中数据选项 data methods 都已经创建,可以将服务端返回的数据进行赋值

推荐在 created 中放置异步请求,原因见下

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

# 在什么阶段才能访问操作DOM

在 mounted 中可以操作 DOM, 原因是在进行到 mounted 时,Vue 已经将编译好的模板挂载到了页面上。

# 父组件可以监听到子组件的生命周期吗?

可以的,举个例子,在子组件生命周期进行到 mounted 后父组件做出一定处理,提供两个方案

1. 方案一

使用 $emit() 进行 子 => 父 组件之间的通信

<Child @speakToFa="watchChildMounted"><>
1
// 子组件 使用钩子函数
mounted () {
  this.$emit('speakToFa');
}
// 父组件
methods: {
  watchChildMounted () {
    console.log('监听到子组件钩子函数 mounted 执行');
  }
}
1
2
3
4
5
6
7
8
9
10

2. 方案二

使用 hook

解释

hook 常用于在父组件监听子组件的种种生命周期

写法: 可以用v-on 指令绑定事件hook:生命周期

<Child @hook:mounted="watchChildMounted"><>
1
// 父组件
methods: {
  watchChildMounted () {
    console.log('监听到子组件钩子函数 mounted 执行');
  }
}
1
2
3
4
5
6

# 谈谈你对 keep-alive 的了解

详见 Vue的内置组件 keep-alive 部分

keep-alive 是 Vue 内置的一个组件,用于组件缓存,避免重复组件渲染。有以下特性:

  1. 一般结合路由和动态组件一起使用,用于缓存组件。

  2. 有三个属性

include 与 exclude 两者都支持字符串或正则表达式

  • include 只有名称匹配的组件会被缓存。
  • exclude 任何名称匹配的组件都不会被缓存。 其中当两者同时出现时 exclude 的优先级比 include 高;
  • max 数字。最多可以缓存多少组件实例。
  1. 被 keep-alive 包裹的组件拥有两个钩子函数 activated 和 deactivated ,当组件被激活时,触发activated,当组件被移除时,触发deactivated。

# Vue 中子组件的 data 选项为什么是函数

Vue 的子组件中,data 选项为一个函数 return 一个对象的形式。

原因:

  1. 为什么不用对象的形式: 由于 Vue 中的组件是会被复用的,使用对象,如果使用对象,作用域无法隔离,各个组件之间的 data 数据会互相产生影响。
  2. 为什么使用函数: 使用函数形式并 return 一个对象,每个实例都会保存一份被返回对对象的独立的拷贝,这样就保证了各个组件之间的 data 数据的独立性。

# 了解 v-model 吗?说一下其原理

v-model 本质上是语法糖,逻辑上绑定表单元素的属性、注册事件监听来起到数据双向绑定的特点。

# v-model 绑定表单元素

详见:传送门 Vueのapi

举几个例子:

  1. 作用于 text 或 textarea 元素时,绑定其 value 属性并注册 input 事件监听。
  2. 作用于 checkbox 或 radio 元素时,绑定其 checked 属性并注册 change 事件监听。
  3. 作用于 select 元素时,绑定其 value 属性并注册 change 事件监听。

# v-model 作用于组件上

v-model 默认会利用名为 value 的 prop 和名为 input 的事件。

# Vue 组件间通信有哪几种方式?

在之前的 blog 中我已经总结了几种方式,点击传送门

简单罗列一下:

  1. 使用 props 与 $emit: 适用于父子组件之间通信
  • 绑定自定义属性使用 props 接收父组件传递过来的数据。
  • 绑定自定义事件使用 $emit 传递触发的事件到父组件,由于可以传参,所以可以传递数据
  1. 使用 $emit 与 $on: 适用于所有组件之间的通信
  • 创建一个全局的空的 Vue 实例。
  • 在传递数据方调用该空实例的 $emit 方法来触发当前实例上的事件,并将需要传递的数据利用参数存储。
  • 在接受数据方调用该空实例的 $on 方法来监听当前实例上的自定义事件。

本质上就是创建了一个空的 Vue 实例作为事件总线,来在它身上触发($emit)和监听($on)事件

  1. 使用 $attrs 与 $listeners: 适用于隔代组件之间的通信
  • 向下级通信: 子组件 $attrs 属性可以自动接收父组件传递过来但是没被 props 接收的数据。这样就可以在子组件中通过 v-bind="$attrs" 的方式继续向下一层组件传递。
  • 向上级通信: 子组件通过 $emit() 触发的事件,都会记录在父组件的 $listeners 中,在父组件中就可以通过 v-on=$listeners 的方式继续向上一层组件传递。
  1. 使用 ref 与 $refs: 适用于父子组件之间通信
  • 使用 ref 给子组件一个独立的标识,而 $refs 这个对象就存储着标识后的子组件,并且以键值对的形式,key 为 ref 时给的独立标识。
  • 当需要子组件的数据,直接用 $refs.key.数据 的形式指代子组件取里面数据。
  1. 使用 $parent 与 $childrens: 适用于父子组件之间通信
  • 拿下级组件数据: $childrens 属性,这个数组里面存储着该组件所有的直接下属组件,当需要子组件的数据时,直接用 $childrens[index].数据 就可以指代对应的子组件并取里面的数据。
  • 拿上级组件数据: $parent 属性,里面就是该组件的父组件,当需要父组件数据时,直接使用 $parent.数据 就可以指代父组件并取里面的数据。
  1. 使用 productor 与 reject: 适用于父组件将数据共享给所有子孙组件
  • productor 为一个对象或函数返回一个对象的形式,用于放置父组件欲要共享的数据。
  • reject 为一个数组存字符串或者对象的形式,用于子孙组件取得共享的数据。
  1. 使用 vuex

# 知道 SSR 吗?

了解一点 SSR

# 什么是 SSR

SSR(server side render) 服务端渲染,Vue 原本是在客户端将标签渲染成整个 HTML 片段,而通过 SSR 将这个工作转交给了服务端,再将渲染完成后的整个 HTML 片段返回给客户端的过程就是 SSR。

# 优点

  1. 有利于 SEO:
  • 传统的 SPA 中,SPA 是通过发送 ajax 请求加载的,而搜索引擎爬取页面内容不会等到 ajax 异步请求完成。
  • 使用 SSR 后,直接在服务端将渲染完成的页面返回,所以搜索引擎可以爬取到。
  1. 加载速度更快:
  • 传统的 SPA 中,是等到 Vue 编译的 js 文件都加载完再渲染页面,以首屏加载需要一定的时间。
  • 而 SSR, 是在服务端会将渲染完的页面直接返回到客户端,加载速度更快。

# 缺点

  1. 更高的开发条件
  • SSR 中只支持 beforeCreate 与 created 两个钩子,这就导致有些第三方扩展库需要特殊处理才可使用在上面。
  • 服务端渲染应用程序,需要在 Node.js server 环境下运行
  1. 加大服务器负载
  • 会加大服务器 CPU 的损耗,如果在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

# vue-router 有几种路由模式

  1. hash 使用 URL Hash 值作为路由,支持所有浏览器。
  2. history 依赖于 HTML5 history API
  3. abstract 支持所有的 JavaScript 环境,包括 Node.js 端,如果没有浏览器 API, 会强制进入此路由模式

# 说一下 vue-router 中的 hash 与 history 两种路由模式的实现

# hash

hash 值,就是 URL 尾部 #... 那一部分。

vue-router 中的 hash 是根据如下特性实现的:

  1. 当用户点击一个 a 标签,并设置 href 属性,实现 hash 值的变化;或者通过 JavaScript 来改变 location.hash 来变更 hash 值。
  2. hash 值的改变,都会在浏览器访问历史中留下记录,所以可以通过浏览器的前进和回退操作 hash 值切换。
  3. 可以通过 hashChange 事件来监听 hash 值的变化,从而实现页面的跳转。
  4. URL 中的 hash 值只是客户端的一种状态,当想服务端发送请求时, # 后面的内容不会发送到服务端。

# history

HTML5 提供了 history API 来实现 URL 的变化。最主要的是使用了其中两个 API:

history.pushState() 以及 history.replaceState(),详见 MDN

这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录

  • history.pushState() 往当前浏览器历史堆栈中添加一个新的状态。
  • history.replaceState() 替换历史堆栈中的状态。

再介绍一个东西 window.popState, 这是一个事件,当活动历史记录条目更改时,将触发 popstate 事件。

下面来看下 history 根据哪些特性实现:

  1. 利用 pushState() 以及 replaceState() 操控 URL 变化。
  2. 使用 popState 事件来监听 URL 变化,实现页面跳转。
  3. pushState() 以及 replaceState() 不会触发 popState 所以我们要手动触发页面跳转。

# 简述 MVVM

MVVM 是一种软件架构设计模式,它分为三层,分别是:

  1. M => Model 层
  • 也称 数据层,在前端看来泛指后端提供的 api 接口。
  1. V => View 层
  • 也称 视图层,也就是用户界面,前端主要由 HTML 以及 CSS 来构建。
  1. VM => ViewModel 层
  • 也称视图数据层,这一层的出现让 Model 层与 View 层解耦(前后端分离思想)。
  • 是前端开发者写代码的地方,处理 Model 层的数据,并完整的描述 View 层。

值得一提的是,MVVM 实现了数据与视图的双向数据绑定,ViewModel 的内容会实时展现在 View 层这样前端开发者不必操作 DOM, 只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新。

# 说一下 Vue 中的双向数据绑定

# 什么是双向数据绑定

  • 当数据变化更新视图
  • 当视图变化更新数据

# Vue 中的实现方式

  1. 实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;
    • 劫持原理:遍历数据对象,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 存值器setter 和 取值器getter。
    • 获取数据时,触发取值器;修改数据时,触发存值器。从而监听数据变化。
  2. 实现一个订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理;
  3. 实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图;
  4. 实现一个解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化

如果想深入研究,请移步我的另一篇文章 造轮子 - 双向数据绑定,这里不多赘述。

# Vue 框架怎么实现对象和数组的监听

在 Vue 中使用 Object.defineProperty() 对对象中的属性添加观测,这就导致了属性内部为引用类型时,就无法检测内部变化了。

Vue 中的解决方案,只是部分,也有无法监听的情况,详见我的另一个篇 Blog: Vue 无法检测的数组改动问题

使用递归,检测属性的类型,如果是对象或数组,再次调用 observe 类,递归使其可观测。

// 数组
let childOb = !shallow && observe(val) // observe 功能为监测数据的变化
// 对象
if (typeof val === 'Object') {
  new observer(val);
}
1
2
3
4
5
6

# 谈一下 Proxy 与 Object.defineProperty 优劣对比

# Proxy

优势如下:

  1. 可以直接监听对象与数组的变化,而 Object.defineProperty 并不能将所有修改二者的方法都观测到;
  2. 拥有多达13种拦截方式,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
  3. Proxy 返回的是一个新对象,直接操作新对象达到目的;而 Object.defineProperty 只能遍历对象属性并直接修改;
  4. Proxy 作为信标准收到浏览器厂商重点性能优化,拥有新标准性能红利。

# Object.defineProperty

兼容性好,支持 IE9,而 Proxy 要在 Vue3.0 才能使用 Proxy。

# 虚拟 DOM 优缺点

# 优点

1. 保证性能下线

虽然它需要适配上层 API,性能不是最优的,但也要比直接操纵 DOM 好很多,保证性能下线。

2. 不用手动操作 DOM

前端开发者不必再手动操作 DOM,只需着重关注 ViewModel 层的逻辑,且虚拟 DOM 与 数据双向绑定,更新视图,提高开发效率。

3. 跨平台

虚拟 DOM 本质上是 JavaScript 对象,而真实 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

# 缺点

无法进行极致优化

虽然虚拟 DOM 有合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。

# 参考

掘金: 30 道 Vue 面试题,内含详细讲解 (opens new window)