前言

当回答面试官问及的Vue问题,我们除了照本宣科的回答外,其实还可以根据少量的源码来秀一把,来体现出你对Vue的深度了解。

本文会陆续更新,此次涉及以下问题:

  1. “new Vue()做了什么?”
  2. “什么阶段才能访问DOM?”
  3. “谈谈你对Vue生命周期的理解。”
  4. 扩展:新生命周期钩子serverPrefetch是什么?
  5. “vue-router路由模式有几种?”
  6. “谈谈你对keep-alive的了解?”
  7. “了解Vue2.6+新全局API:Vue.observable()吗?”

1. “new Vue()做了什么?”

new关键字代表实例化一个对象, 而Vue实际上是一个类, 源码位置是/src/core/instance/index.js

function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}
复制代码

接着我们跳转追踪至this._init(),即Vue.prototype._init,位于src\core\instance\init.js_init()方法的内部有一系列 init* 的方法

Vue.prototype._init = function (options?: Object) {const vm: Component = this// ...忽略,从第45行看起if (process.env.NODE_ENV !== 'production') {initProxy(vm)} else {vm._renderProxy = vm}// expose real selfvm._self = vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, 'beforeCreate')initInjections(vm) // resolve injections before data/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, 'created')// ...忽略if (vm.$options.el) {vm.$mount(vm.$options.el)}}
}
复制代码

1.1 这里我们概述一遍:

  1. initProxy,作用域代理,拦截组件内访问其它组件的数据。
  2. initLifecycle, 建立父子组件关系,在当前实例上添加一些属性和生命周期标识。如:$children$refs_isMounted等。
  3. initEvents,用来存放除@hook:生命周期钩子名称="绑定的函数"事件的对象。如:$on$emit等。
  4. initRender,用于初始化$slots$attrs$listeners
  5. initInjections,初始化inject,一般用于更深层次的组件通信,相当于加强版的props。用于组件库开发较多。

只要在上一层级的声明的provide,那么下一层级无论多深都能够通过inject来访问到provide的数据。这么做也是有明显的缺点:在任意层级都能访问,导致数据追踪比较困难,不知道是哪一个层级声明了这个或者不知道哪一层级或若干个层级使用。

  • initState,是很多选项初始化的汇总,包括:props、methods、data、computed 和 watch 等。
  • initProvide,初始化provide
  • vm.$mount,挂载实例。

2. “什么阶段才能访问DOM?”

这个回答可以从beforeCreate以及 created 的调用时机谈起,我们根据上面的概述,来简化下代码:

callHook(vm, 'beforeCreate')
// 初始化 inject
// 初始化 props、methods、data、computed 和 watch
// 初始化 provide
callHook(vm, 'created')
// 挂载实例 vm.$mount(vm.$options.el)
复制代码

所以当面试官问你:

  • beforeCreate以及 created 调用时,哪些数据能用与否?
  • 什么阶段才能访问DOM?
  • 为什么created之后才挂载实例?

知道怎么回答了吧。

3. “谈谈你对Vue的生命周期的理解”

常规回答这里就不说了,来稍微深入点的:

  1. created/mounted/updated/destroyed,以及对应的before钩子。分别是创建=>挂载=>更新=>销毁。
  2. Vue源码中定义了一个mergeHook函数来遍历一个常量数组LIFECYCLE_HOOKS,该数组实际上是由与生命周期钩子同名的字符串组成的数组。
// v2.6.10 最新版
var LIFECYCLE_HOOKS = ['beforeCreate','created','beforeMount','mounted','beforeUpdate','updated','beforeDestroy','destroyed','activated','deactivated','errorCaptured',// v2.6+ 'serverPrefetch'
];
复制代码

于是,你可以答多activated & deactivated(keep-alive 组件激活/停用)、errorCaptured(v2.5 以上版本有的一个钩子,用于处理错误)这三个。

3.1 新生命周期钩子:serverPrefetch是什么?

可以看到,serverPrefetch前身是ssrPrefetch。顾名思义,这是用来处理ssr的。允许我们在渲染过程中“等待”异步数据。可在任何组件中使用,而不仅仅是路由组件。

这里我们贴出一段官方例子:

<!-- Item.vue -->
<template><div v-if="item">{{ item.title }}</div><div v-else>...</div>
</template><script>
export default {computed: {item () {return this.$store.state.items[this.$route.params.id]}},serverPrefetch () {return this.fetchItem()},mounted () {if (!this.item) {this.fetchItem()}},methods: {fetchItem () {// return the Promise from the actionreturn this.$store.dispatch('fetchItem', this.$route.params.id)}}
}
</script>
复制代码
  • 绝大多数的面试官都不会去关注v2.6+ 以后的代码记录和变更。这里如果你说出这个v2.6.10的变化,啧啧...面试官肯定更加欣赏你。

3.2 生命周期钩子的合并策略

callHook(vm, 'created')讲,先判断组件的选项中有无对应名字的生命周期钩子,再判断是否有 parentVal(vm)。若存在parentVal(vm)且都有对应的生命周期钩子,则会将两者concat为一个数组(parentVal.concat(childVal))。所以,生命周期钩子其实是可以写成数组。如:

created: [
function () {console.log('first')
},
function () {console.log('second')
},
function () {console.log('third')
}]
复制代码

钩子函数将按顺序执行。

4. “Vue-router 路由模式有几种?”

三种 "hash" | "history" | "abstract",一般人只知道两种"hash" | "history"

这里贴出源码:

switch (mode) {case 'history':this.history = new HTML5History(this, options.base)breakcase 'hash':this.history = new HashHistory(this, options.base, this.fallback)breakcase 'abstract':this.history = new AbstractHistory(this, options.base)breakdefault:if (process.env.NODE_ENV !== 'production') {assert(false, `invalid mode: ${mode}`)}
}
复制代码

# mode

类型: string

默认值: "hash" (浏览器环境) | "abstract" (Node.js 环境)

可选值: "hash" | "history" | "abstract" 配置路由模式:

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

5. “谈谈你对keep-alive的了解?”

先贴一个常规回答:

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

然后你可以开始骚了:

  1. <keep-alive>Vue 源码中实现的一个全局抽象组件,通过自定义 render 函数并且利用了插槽来实现数据缓存和更新。它的定义在src/core/components/keep-alive.js 中:
export default {name: 'keep-alive',abstract: true,...
}
复制代码
  1. 所有的抽象组件是通过定义abstract选项来声明的。抽象组件不渲染真实DOM,且不会出现在父子关系的路径上(initLifecycle会忽略抽象组件),相关代码片段:
if (parent && !options.abstract) {// abstract 即 `ptions.abstract`// while 循环查找第一个非抽象的父组件while (parent.$options.abstract && parent.$parent) {parent = parent.$parent}parent.$children.push(vm)
}
复制代码

6. “了解Vue2.6+新全局APIVue.observable()吗?”

Vue2.6+新的全局API是Vue.observable(),它的使用方式:

import vue from vue;
const state = Vue.observable ({counter: 0,
});
export default {render () {return (<div>{state.counter}<button v-on:click={() => {state.counter ++; }}>Increment counter</ button></ div>);},
};
复制代码

而它定义在/src/core/global-api/index.js第48行:

import { observe } from 'core/observer/index'
// ...
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
复制代码

再看看它importobserve,最近一次提交在12/1/2018,唔。。。。

核心就是暴露出observe(obj)观测后的数据,代码啥都没改。懂了吧?

求一份深圳的内推

目前本人在准备跳槽,希望各位大佬和HR小姐姐可以内推一份靠谱的深圳前端岗位!

  • 微信:huab119
  • 邮箱:454274033@qq.com

作者掘金文章总集

  • 为何你始终理解不了JavaScript作用域链?
  • 「Vue实践」项目升级vue-cli3的正确姿势
  • 「从源码中学习」彻底理解Vue选项Props
  • 「从源码中学习」Vue源码中的JS骚操作
  • 「从源码中学习」面试官都不知道的Vue题目答案

公众号:

转载于:https://juejin.im/post/5c959f74f265da610c068fa8

「从源码中学习」面试官都不知道的Vue题目答案相关推荐

  1. 七点讲透分布式架构的前世今生,面试官都不知道的概念来了

    一.前言 以前我们聊过分布式架构的演进过程,那本文我们就来聊一聊目前主流的分布式架构和分布式架构中常见理论以及如何才能设计出高可用的分布式架构好了,基本上就是它的前世今生了. 分布式架构中,SOA和微 ...

  2. rust墙壁升级点什么_分享:如何在阅读Rust项目源码中学习

    今天做了一个Substrate相关的小分享,公开出来. 因为我平时也比较忙,昨天才选定了本次分享的主题,准备比较仓促,细节可能不是很充足,但分享的目的也是给大家提供一个学习的思路,更多的细节大家可以在 ...

  3. 【深度挖掘 RocketMQ底层源码】「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(Pull模式-下)

    承接[[深度挖掘 RocketMQ底层源码]「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(Pull模式-上)] pullBlockIfNotFound方法 通过该方法获取 ...

  4. MVVM架构~knockoutjs系列之从Knockout.Validation.js源码中学习它的用法

    说在前 有时,我们在使用一个插件时,在网上即找不到它的相关API,这时,我们会很抓狂的,与其抓狂,还不如踏下心来,分析一下它的源码,事实上,对于JS这种开发语言来说,它开发的插件的使用方法都在它的源码 ...

  5. 从框架源码中学习创建型设计模式

    文章目录 从框架源码中解读创建型设计模式 工厂模式 案例一:RocketMQ源码-创建Producer生产者 案例二:RocketMQ源码-创建过滤器工厂 抽象工厂 案例一:Dubbo源码-创建缓存的 ...

  6. hashmap containsvalue时间复杂度_不看看HashMap源码,怎么和面试官谈薪资

    HashMap 是日常开发中,用的最多的集合类之一,也是面试中经常被问到的 Java 类之一.同时,HashMap 在实现方式上面又有十分典型的范例.不管是从哪一方面来看,学习 HashMap 都可以 ...

  7. 从框架源码中学习结构型设计模式

    文章目录 从框架源码学习结构型设计模式 适配器模式 应用实例 案例一:dubbo框架日志适配器 Logger接口 日志实现类 Logger适配器接口 LoggerAdapter实现类 Logger日志 ...

  8. C/C++编程打造单机麻将「附源码+说明文档」一个不错的入门项目

    项目介绍 基于最新版 Cocos2d-X 3.17 与 cocostudio V3.10 开发的单机麻将游戏,麻将算法为商业级麻将服务器端算法,整个项目代码精简.注释详细. 如果你刚刚接触Cocos2 ...

  9. 从jquery源码中学习一些技巧

    1 '',null,undefined转换为bool类型时都为false //'handle '',undefined and null' 0也会转换为false,但一般不会输入0 if (selec ...

最新文章

  1. 局域网上传文件到服务器很慢,win10局域网内传文件很慢怎么办_win10局域网内文件传输很慢如何处理-win7之家...
  2. android动态监听事件,Android-常见的事件监听
  3. 9. 弹出键盘挡住input
  4. android 开发不能创建目录
  5. 前端学习(2226):react之组件
  6. 面试官:怎么解决MySQL中的死锁问题?
  7. cefsharp 网页另存为图片_如何一键导出所有图片?我另存为了两小时,结果同事十秒就搞定了...
  8. 职场学会这三招,谁都想跟你交朋友
  9. 用计算机如何算行列式,数学计算器使用操作步骤
  10. 「一本通 1.2 练习 2」扩散(loj10015)
  11. XV6 swtch.S详解
  12. python turtle画表情包--不可以色色
  13. [网鼎杯 2020 白虎组]PicDown(精讲)
  14. ijkplayer编译生成aar,支持https,rtsp,录制与截图
  15. ppt打印一页6张铺满不留白_你真的会做ppt?这些习惯和操作你有吗?
  16. (图解)数据库设计基础之:第一范式、第二范式及第三范式
  17. Oracle获取上一年的时间
  18. 淮北师范大学c语言试卷,2016年淮北师范大学物理与电子信息学院高级语言程序设计(加试)之C语言程序设计复试笔试最后押题五套卷...
  19. C++基础入门:实现类似暴风影音的影音播放器!(源码展示)
  20. 按键精灵-暗黑破坏神(野蛮人自动)

热门文章

  1. 从小屏到大屏,触控技术的现在与未来
  2. JSP中javaBean和Servlet理解
  3. ifstream eof()函数
  4. UA MATH567 高维统计III 随机矩阵10 亚高斯矩阵的应用:协方差估计与聚类问题的样本量需求计算
  5. 初等数学O 集合论基础 第四节 二元关系、等价类与运算
  6. UA MATH571B 试验设计 Quarter 2-level析因设计
  7. Mathematica 画图操作中的一些小惊喜
  8. CentOS搭建C++开发环境
  9. java单元测试总结
  10. Linux 内核安全模块学习总结