当回答面试官问及的Vue问题,咱们除了照本宣科的回答外,其实还能够根据少许的源码来秀一把,来体现出你对Vue的深度了解。前端
本文会陆续更新,这次涉及如下问题:vue
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-router
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 self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
// ...忽略
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
复制代码
initProxy
,做用域代理,拦截组件内访问其它组件的数据。initLifecycle
, 创建父子组件关系,在当前实例上添加一些属性和生命周期标识。如:$children
、$refs
、_isMounted
等。initEvents
,用来存放除@hook:生命周期钩子名称="绑定的函数"
事件的对象。如:$on
、$emit
等。initRender
,用于初始化$slots
、$attrs
、$listeners
initInjections
,初始化inject
,通常用于更深层次的组件通讯,至关于增强版的props
。用于组件库开发较多。只要在上一层级的声明的provide,那么下一层级不管多深都可以经过inject来访问到provide的数据。这么作也是有明显的缺点:在任意层级都能访问,致使数据追踪比较困难,不知道是哪个层级声明了这个或者不知道哪一层级或若干个层级使用。vue-cli
initState
,是不少选项初始化的汇总,包括:props、methods、data、computed 和 watch
等。initProvide
,初始化provide
。vm.$mount
,挂载实例。这个回答能够从beforeCreate
以及 created
的调用时机谈起,咱们根据上面的概述,来简化下代码:api
callHook(vm, 'beforeCreate')
// 初始化 inject
// 初始化 props、methods、data、computed 和 watch
// 初始化 provide
callHook(vm, 'created')
// 挂载实例 vm.$mount(vm.$options.el)
复制代码
因此当面试官问你:数组
beforeCreate
以及 created
调用时,哪些数据能用与否?created
以后才挂载实例?知道怎么回答了吧。浏览器
常规回答这里就不说了,来稍微深刻点的:缓存
created/mounted/updated/destroyed
,以及对应的before
钩子。分别是建立=>挂载=>更新=>销毁。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 以上版本有的一个钩子,用于处理错误)这三个。bash
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 action
return this.$store.dispatch('fetchItem', this.$route.params.id)
}
}
}
</script>
复制代码
v2.6.10
的变化,啧啧...面试官确定更加欣赏你。拿callHook(vm, 'created')
讲,先判断组件的选项中有无对应名字的生命周期钩子,再判断是否有 parentVal(vm)
。若存在parentVal(vm)
且都有对应的生命周期钩子,则会将二者concat
为一个数组(parentVal.concat(childVal
))。因此,生命周期钩子实际上是能够写成数组。如:
created: [
function () {
console.log('first')
},
function () {
console.log('second')
},
function () {
console.log('third')
}]
复制代码
钩子函数将按顺序执行。
三种 "hash" | "history" | "abstract"
,通常人只知道两种"hash" | "history"
。
这里贴出源码:
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid 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
,路由会自动强制进入这个模式.keep-alive
的了解?”先贴一个常规回答:
keep-alive是 Vue 内置的一个组件,可使被包含的组件保留状态,或避免从新渲染。 在vue 2.1.0 版本以后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。
而后你能够开始骚了:
<keep-alive>
是 Vue
源码中实现的一个全局抽象组件,经过自定义 render
函数而且利用了插槽来实现数据缓存和更新。它的定义在src/core/components/keep-alive.js
中:export default {
name: 'keep-alive',
abstract: true,
...
}
复制代码
abstract
选项来声明的。抽象组件不渲染真实DOM
,且不会出如今父子关系的路径上(initLifecycle
会忽略抽象组件),相关代码片断:if (parent && !options.abstract) {
// abstract 即 `ptions.abstract`
// while 循环查找第一个非抽象的父组件
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
复制代码
Vue2.6+
新全局API
:Vue.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
}
复制代码
再看看它import
的observe
,最近一次提交在12/1/2018
,唔。。。。
observe(obj)
观测后的数据,代码啥都没改。懂了吧?
目前本人在准备跳槽,但愿各位大佬和HR小姐姐能够内推一份靠谱的深圳前端岗位!
huab119
454274033@qq.com