vue面试有哪些插件_Vue实战面试知识点汇总

1. 双向绑定详解

2.computed 的实现原理

1)为什么需要computed?

template 使用大量复杂的逻辑表达式处理数据,使得代码维护性差,且相同数据重复计算对性能开销大

2)计算属性的实现原理?

1.初始化 data 和 computed,分别代理其 set 和 get 方法,对 data 中的所有属性生成唯一的 dep 实例

2.对 computed 中的 属性生成唯一的 watcher,并保存在 vm._computedWatchers 中

3.访问计算属性时,设置 Dep.target 指向 计算属性的 watcher,调用该属性具体方法

4.方法中访问 data 的属性,即会调用 data 属性的 get 方法,将 data 属性的 dep 加入到 计算属性的 watcher , 同时该 dep 中的 subs 添加这个 watcher

5.设置 data 的这个属性时,调用该属性代理的 set 方法,触发 dep 的 notify 方法

6.因为时 computed 属性,只是将 watcher 中的 dirty 设置为 true

7.最后,访问计算属性的 get 方法时,得知该属性的 watcher.dirty 为 true,则调用 watcher.evaluate() 方法获取新的值

综合以上:也可以解释了为什么有些时候当computed没有被访问(或者没有被模板依赖),当修改了this.data值后,通过vue-tools发现其computed中的值没有变化的原因,因为没有触发到其get方法。

3.为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?

1)Object.defineProperty 本身有一定的监控到数组下标变化的能力,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性,导致通过数组下标添加属性无法实时响应

2) Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象

3)Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性

补救:push();pop();shift();unshift();splice();sort();reverse(); Vue重写了数组这个7个方法,称为变异数组,添加了响应式,使得这7个方法添加的属性可以实时响应

4.Vue中的key有什么作用?

1)key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快,但会产生一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位) .diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.

2)更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug

3)更快速 : key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1),源码如下:

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;

}

5.谈一谈nextTick 的原理

vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行

microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕

考虑兼容问题,vue 做了 microtask 向 macrotask 的降级方案

Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更;Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替

6.vue 是如何对数组方法进行变异的 ?

1 const arrayProto =Array.prototype;2 export const arrayMethods =Object.create(arrayProto);3 const methodsToPatch =[4 "push",5 "pop",6 "shift",7 "unshift",8 "splice",9 "sort",10 "reverse"

11 ];12 /** * Intercept mutating methods and emit events*/

13 methodsToPatch.forEach(function(method) {14 //cache original method

15 const original =arrayProto[method];16 def(arrayMethods, method, functionmutator(...args) {17 const result = original.apply(this, args);18 const ob = this.__ob__; let inserted;19 switch(method) {20 case "push":21 case "unshift":22 inserted =args;23 break;24 case "splice":25 inserted = args.slice(2);26 break;27 }28 if(inserted)29 ob.observeArray(inserted);30 //notify change

31 ob.dep.notify();32 returnresult;33 });34 });35 /** * Observe a list of Array items.*/

36 Observer.prototype.observeArray = functionobserveArray(items) {37 for (var i = 0, l = items.length; i < l; i++) {38 observe(items[i]);39 }40 };

简单来说,Vue 通过原型拦截的方式重写了数组的 7 个方法,首先获取到这个数组的ob,也就是它的 Observer 对象,如果有新的值,就调用 observeArray 对新的值进行监听,然后手动调用 notify,通知 render watcher,执行 update

7.Vue 组件 data 为什么必须是函数 ?

new Vue()实例中,data 可以直接是一个对象,为什么在 vue 组件中,data 必须是一个函数呢?

因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染,产生副作用。

所以一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。new Vue 的实例是不会被复用的,因此不存在以上问题。

8.聊聊 keep-alive 的实现原理和缓存策略?

1 export default{2 name: "keep-alive",3 abstract: true,4 //抽象组件属性 ,它在组件实例建立父子关系的时候会被忽略,发生在 initLifecycle 的过程中

5 props: {6 include: patternTypes,7 //被缓存组件

8 exclude: patternTypes,9 //不被缓存组件

10 max: [String, Number] //指定缓存大小

11 },12 created() {13 this.cache = Object.create(null); //缓存

14 this.keys = []; //缓存的VNode的键

15 },16 destroyed() {17 for (const key in this.cache) {18 //删除所有缓存

19 pruneCacheEntry(this.cache, key, this.keys);20 }21 },22 mounted() {23 //监听缓存/不缓存组件

24 this.$watch("include", val =>{25 pruneCache(this, name =>matches(val, name));26 });27 this.$watch("exclude", val =>{28 pruneCache(this, name => !matches(val, name));29 });30 },31 render() {32 //获取第一个子元素的 vnode

33 const slot = this.$slots.default;34 const vnode: VNode =getFirstComponentChild(slot);35 const componentOptions: ?VNodeComponentOptions = vnode &&vnode.componentOptions;36 if(componentOptions) {37 //name不在inlcude中或者在exlude中 直接返回vnode

38 //check pattern

39 const name: ?string =getComponentName(componentOptions);40 const { include, exclude } = this; if(41 //not included

42 (include && (!name || !matches(include, name))) ||

43 //excluded

44 (exclude && name &&matches(exclude, name))45 ) { returnvnode; }46 const { cache, keys } = this;47 //获取键,优先获取组件的name字段,否则是组件的tag

48 const key: ?string = vnode.key == null ?

49 //same constructor may get registered as different local components

50 //so cid alone is not enough (#3269)

51 componentOptions.Ctor.cid +

52 (componentOptions.tag ? `::${componentOptions.tag}` : "") : vnode.key;53 //命中缓存,直接从缓存拿vnode 的组件实例,并且重新调整了 key 的顺序放在了最后一个

54 if(cache[key]) {55 vnode.componentInstance =cache[key].componentInstance;56 //make current key freshest

57 remove(keys, key); keys.push(key);58 }59 //不命中缓存,把 vnode 设置进缓存

60 else{61 cache[key] =vnode; keys.push(key);62 //prune oldest entry

63 //如果配置了 max 并且缓存的长度超过了 this.max,还要从缓存中删除第一个

64 if (this.max && keys.length > parseInt(this.max)) {65 pruneCacheEntry(cache, keys[0], keys, this._vnode);66 }67 }68 //keepAlive标记位

69 vnode.data.keepAlive = true;70 } return vnode || (slot && slot[0]);71 }72 };

原理

获取 keep-alive 包裹着的第一个子组件对象及其组件名

根据设定的 include/exclude(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例

根据组件 ID 和 tag 生成缓存 Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该 key 在 this.keys 中的位置(更新 key 的位置是实现 LRU 置换策略的关键)

在 this.cache 对象中存储该组件实例并保存 key 值,之后检查缓存的实例数量是否超过 max 的设置值,超过则根据 LRU 置换策略删除最近最久未使用的实例(即是下标为 0 的那个 key)

最后组件实例的 keepAlive 属性设置为 true,这个在渲染和执行被包裹组件的钩子函数会用到,这里不细说

LRU 缓存淘汰算法

LRU(Least recently used)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高

keep-alive 的实现正是用到了 LRU 策略,将最近访问的组件 push 到 this.keys 最后面,this.keys[0]也就是最久没被访问的组件,当缓存实例超过 max 设置值,删除 this.keys[0]

9.vm.$set()实现原理是什么?

受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 无法检测到对象属性的添加或删除。

由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。

对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。

那么 Vue 内部是如何解决对象新增属性不能响应的问题的呢?

1 export function set(target: Array |Object, key: any, val: any): any {2 //target 为数组

3 if (Array.isArray(target) &&isValidArrayIndex(key)) {4 //修改数组的长度, 避免索引>数组长度导致splice()执行有误

5 target.length =Math.max(target.length, key);6 //利用数组的splice变异方法触发响应式

7 target.splice(key, 1, val); returnval;8 }9 //target为对象, key在target或者target.prototype上 且必须不能在 Object.prototype 上,直接赋值

10 if (key in target && !(key inObject.prototype)) {11 target[key] = val; returnval;12 }13 //以上都不成立, 即开始给target创建一个全新的属性

14 //获取Observer实例

15 const ob =(target: any).__ob__;16 //target 本身就不是响应式数据, 直接赋值

17 if (!ob) { target[key] = val; returnval; }18 //进行响应式处理

19 defineReactive(ob.value, key, val);20 ob.dep.notify();21 returnval;22 }

如果目标是数组,使用 vue 实现的变异方法 splice 实现响应式

如果目标是对象,判断属性存在,即为响应式,直接赋值

如果 target 本身就不是响应式,直接赋值

如果属性不是响应式,则调用 defineReactive 方法进行响应式处理

10.Object.defineProperty和Proxy的区别?

Object.defineProperty

不能监听到数组length属性的变化;

不能监听对象的添加;

只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。

Proxy

可以监听数组length属性的变化;

可以监听对象的添加;

可代理整个对象,不需要对对象进行遍历,极大提高性能;

多达13种的拦截远超Object.defineProperty只有get和set两种拦截

11.你认为Vue的核心是什么?

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。

以上是官方原话,从中可以得知Vue的核心是模板语法和数据渲染。

12.Vue为什么要求组件模板只能有一个根元素?

当前的virtualDOM差异和diff算法在很大程度上依赖于每个子组件总是只有一个根元素。

13.ajax、fetch、axios这三都有什么区别?

ajax是最初出现的发送临时请求的技术,属于原生js标准,核心是使用XMLHttpRequest对象,使用并存并有先后顺序的话,容易产生地狱。

fetch号称可以代替ajax的技术,是基于es6中的Promise对象设计的,参数和jQuery中的ajax类似,它并不是对ajax的进一步封装,它属于原生js尺寸。没有使用XMLHttpRequest对象。

axios不是原生js,使用时需要进行进行安装,客户端和服务器端都可以使用,可以在请求和相应阶段进行拦截,基于promise对象。

14.如果想扩展某个现有的Vue组件时,怎么做呢?

用mixins混入

用extends,比mixins先触发

用高阶组件HOC封装

注意:extends使得组件能像面向对象变成一样便于拓展

extends会比mixins先执行。执行顺序:extends > mixins > 组件

extends只能暴露一个extends对象,暴露多个extends不会执行。

mixins能暴露多个

15.vue组件和插件的区别

组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue。

插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身。

简单来说,插件就是指对Vue的功能的增强或补充。

比如说,让你在每个单页面的组件里,都可以调用某个方法,或者共享使用某个变量,或者在某个方法之前执行一段代码等

就可以写一个插件,在Vue原型上扩展方法,要实现这个需求绝对没法写成组件。

1 let whatever ={2 install: function(Vue, options) {3 Vue.prototype.$whatever = function(){4 //do something

5 };6 }7 }

16.为什么Vue使用异步更新组件?

批量更新 收集当前的改动一次性更新 节省diff开销;关联this.$nextTick使用微任务方式得到更新后的dom

17.用vue怎么实现一个换肤的功能?

1.base.scss: 一些通用样式文件(使用scss定义颜色变量,通过@include 引入mixin中的变量)

#content{@include bg_color()}

2.varibale.scss: 颜色,字体,背景的配置文件

$background-color-them1:red

$background-color-them2:blue

3.mixin.scss: 定义mixin方法的文件(通过判断HTML上属性,识别加载模板)

@import "./variable";/*引入配置*/@mixin font_size($size){/*通过该函数设置字体大小,后期方便统一管理;*/@include font-dpr($size);}@mixin bg_color($color){/*通过该函数设置主题颜色,后期方便统一管理;*/background-color:$color;[data-theme="theme1"] & {

background-color:$background-color-theme1;

}[data-theme="theme2"] &{background-color:$background-color-theme2;

}[data-theme="theme3"] &{background-color:$background-color-theme3;

}}

主要原理:

通过设置html的attribute属性在封装的函数中进行判断,进行相应的设置不同的颜色

css中 [ ] 可以识别到在html标签上设置的属性,所以在html上对应属性发生变化时,就会执行相应的样式,

这一步有点类似于平时给div添加一个.active属性,css自动执行相应样式

18.对于 vue3.0 特性你有什么了解的吗?

1.使用porxy替换object.defineProperty

只能监测属性,不能监测对象

检测属性的添加和删除;

检测数组索引和长度的变更;

支持 Map、Set、WeakMap 和 WeakSet。

2.模板

模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能

同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。

3.对typescript结合使用更容易

4.其他改变:

• 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。

• 支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。

• 基于 treeshaking 优化,提供了更多的内置功能

19.key除了在v-for中使用,还有什么作用?

还可以强制替换元素/组件而不是重复使用它。在以下场景可以使用

完整地触发组件的生命周期钩子

触发过渡

{{ text }}

当 text 发生改变时,会随时被更新,因此会触发过渡

不要使用对象或数组之类的非基本类型值作为key,请用字符串或数值类型的值;

不要使用数组的index作为key值,因为在删除数组某一项,index也会随之变化,导致key变化,渲染会出错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值