Vue 常见面试题汇总(这些技巧你真的都掌握了吗?呕心沥血2w字整理(1)

const index = this.subs.indexOf(sub)

if(index > -1) return this.subs.splice(index, 1)

}

}

notify() {

this.subs.forEach(sub => {

sub.update()

})

}

}

class Watcher {

constructor(name) {

this.name = name

}

update() {

console.log(‘更新’)

}

}

手写发布订阅模式

与观察者模式相似,区别在于发布者和订阅者是解耦的,由中间的调度中心去与发布者和订阅者通信。Vue响应式原理个人更倾向于发布订阅模式。其中 Observer 是发布者,Watcher 是订阅者,Dep 是调度中心。

class EventEmitter {

constructor() {

this.events = {}

}

on(type, cb) {

if(!this.events[type]) this.events[type] = []

this.events[type].push(cb)

}

emit(type, …args) {

if(this.events[type]) {

this.events[type].forEach(cb => {

cb(…args)

})

}

}

off(type, cb) {

if(this.events[type]) {

const index = this.events[type].indexOf(cb)

if(index > -1) this.events[type].splice(index, 1)

}

}

}

组件中的data为什么是一个函数

数据以函数返回值形式定义,当每复用一次组件,就会返回一份新的data。即给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。对象在栈中存储的都是地址,函数的作用就是属性私有化,保证组件修改自身属性时不会影响其他复用组件。

生命周期

| 生命周期 | 描述 |

| — | — |

| beforeCreate | vue实例初始化后,数据观测(data observer)和事件配置之前。data、computed、watch、methods都无法访问。 |

| created | vue实例创建完成后立即调用 ,可访问 data、computed、watch、methods。未挂载 DOM,不能访问 、ref。 |

| beforeMount | 在 DOM 挂载开始之前调用。 |

| mounted | vue实例被挂载到 DOM。 |

| beforeUpdate | 数据更新之前调用,发生在虚拟 DOM 打补丁之前。 |

| updated | 数据更新之后调用。 |

| beforeDestroy | 实例销毁前调用。 |

| destroyed | 实例销毁后调用 。 |

调用异步请求可在createdbeforeMountmounted生命周期中调用,因为相关数据都已创建。在不涉及到DOM操作时,最好的选择是在created中调用。

具体细节参考文章

Vue 生命周期 详细介绍(面试必考,内附实例截图)_前端不释卷leo的博客-CSDN博客Vue的生命周期是不仅是面试必考点,理解它更能为我们在Vue项目开发带来促进作用。什么是Vue的生命周期?每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。简单理解就是Vue实例从创建到销毁的这么一个过程。如图(官网)所示:Vue生命周期函数(钩子)执行顺序从上图可知主要为八大生命周期钩子:befor外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传https://blog.csdn.net/qq_41809113/article/details/121684566?spm=1001.2014.3001.5502

父组件与子组件生命周期钩子执行顺序

加载渲染过程

速记:父先创建,才能有子;子创建完成,父才完整。

顺序:父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

组件更新过程

顺序:父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

销毁过程

顺序:父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

父组件监听子组件生命周期钩子的方式

以mounted为例

1、$emit

// 父组件

<Child @mounted=“doSomething”/>

//子组件

2、@hook

// 父组件

<Child @hook:mounted=“doSomething”/>

//子组件

组件通信方式

父子组件

1、props、$emit

2、 p a r e n t 、 parent、 parentchildren

跨级组件

1、 a t t r s 、 attrs、 attrslisteners

2、provide、inject

父子、跨级、兄弟

1、EventBus(事件总线)

2、Vuex(状态管理)

扩展

1、缓存

2、路由

具体细节参考文章

Vue组件间的通信方式(多种场景,通俗易懂,建议收藏)_前端不释卷leo的博客-CSDN博客_vue组件间通信以下是我在工作中用到过的vue组件之间的通信方式,不同的场景使用不同的方式,基本满足所有开发场景中的通信需求,话不多说直接开始,满满的干货,建议收藏。1、父子组件之间的通信父 >>> 子 (Props)一个组件里面引入另外一个组件,此时构成了一种“父子关系”,当前组件为“父”,引入的组件为“子”,如当前组件(父),在父组件中通过 “:message” 向子组件通信。 <div class="paren…外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传https://blog.csdn.net/qq_41809113/article/details/120384336?spm=1001.2014.3001.5502

v-on监听多个方法

鼠标进来1

常用修饰符

表单修饰符

1、lazy::失去焦点后同步信息

2、trim:自动过滤首尾空格

3、number:输入值转为数值类型

事件修饰符

1、stop:阻止冒泡(js中stopPropagation)

2、prevent:阻止默认行为(js中preventDefault)

3、self:仅绑定元素自身触发

4、once:只触发一次

class、style的动态绑定方式

对象方式

数组方式

v-show与v-if的区别

相同点

都是控制页面元素的显示与隐藏

不同点

1、v-show 控制的是元素的CSS(v-show="false"相当于display:none);v-if 是控制元素本身的添加或删除(即是否出现在DOM结构中)

2、v-show 由 false 变为 true 的时候不会触发组件的生命周期。v-if 由 false 变为 true 则会触发组件的beforeCreatecreatebeforeMountmounted钩子,由 true 变为 false 会触发组件的beforeDestorydestoryed方法

3、频繁切换时,使用v-show;不频繁切换、加快页面渲染、需要销毁元素使用v-if

为什么v-if不能和v-for一起使用

性能浪费,每次渲染都要先循环再进行条件判断,考虑用计算属性替代。

Vue2.x中v-forv-if更高的优先级。

Vue3.x中v-if 比 v-for 更高的优先级。

computed与watch的区别与使用场景

computed

计算属性,依赖其他属性值,且值具备缓存的特性。只有它依赖的属性值发生改变,下一次获取的值才会重新计算。

适用于数值计算,并且依赖于其他属性时。因为可以利用缓存特性,避免每次获取值,都需要重新计算。

watch

观察属性,监听属性值变动。每当属性值发生变化,都会执行相应的回调。

适用于数据变化时执行异步或开销比较大的操作。

具体细节参考文章

上手Vue:深度理解computed、watch及其区别_czjl6886的博客-CSDN博客_vue中computed和watch的区别computed(计算属性)与watch(侦听器),是Vue中常用的属性,那么什么时候该如何computed,什么时候该使用watch呢?外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传https://blog.csdn.net/czjl6886/article/details/121454266?spm=1001.2014.3001.5502

slot插槽的理解与使用

slot 插槽,可以理解为slot在组件模板中提前占据了位置。当复用组件时,使用相关的slot标签时,标签里的内容就会自动替换组件模板中对应slot标签的位置,作为承载分发内容的出口。

具体细节参考文章

Vue 插槽(slot)详细介绍(对比版本变化,避免踩坑)_前端不释卷leo的博客-CSDN博客Vue中的插槽(slot)在项目中用的也是比较多的,今天就来介绍一下插槽的基本使用以及Vue版本更新之后的插槽用法变化。插槽是什么?插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。简单理解就是子组件中留下个“坑”,父组件可以使用指定内容来补“坑”。以下举例子帮助理解。怎么使用插槽?基本用法外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传https://blog.csdn.net/qq_41809113/article/details/121640035?spm=1001.2014.3001.5502

Vue.$delete和delete的区别

Vue.$delete 是直接删除了元素,改变了数组的长度;delete 是将被删除的元素变成内 undefined ,其他元素键值不变。

Vue.$set如何解决对象新增属性不能响应的问题

Vue.$set的出现是由于Object.defineProperty的局限性:无法检测对象属性的新增或删除。

源码位置:vue/src/core/observer/index.js

export function set(target, key, val) {

// 数组

if(Array.isArray(target) && isValidArrayIndex(key)) {

// 修改数组长度,避免索引大于数组长度导致splice错误

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.ob

// target 不是响应式数据,直接赋值

if(!ob) {

target[key] = val

return val

}

// 响应式处理属性

defineReactive(ob.value, key, val)

// 派发更新

ob.dep.notify()

return val

}

具体原理

1、若是数组,直接使用数组的 splice 方法触发响应式

2、若是对象,判断属性是否存在,对象是否是响应式

3、以上都不满足,最后通过 defineReactive 对属性进行响应式处理

具体细节参考文章

Vue 全局API 详细介绍(nextTick、set、delete、…)_前端不释卷leo的博客-CSDN博客Tips:Vue全局(内置)API,在实例中对应使用方式this.$apiName。Vue.extend(options)使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。data选项是特例,需要注意 - 在Vue.extend()中它必须是函数。// 创建构造器var Greet = Vue.extend({ template: ‘

{{firstName}} {{lastName}} say {{alias}}

’,… 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传https://blog.csdn.net/qq_41809113/article/details/121581605?spm=1001.2014.3001.5502

Vue.$nextTick的原理

nextTick:在下次 DOM 更新循环结束之后执行延迟回调。常用于修改数据后获取更新后的DOM。

源码位置:vue/src/core/util/next-tick.js

import { noop } from ‘shared/util’

import { handleError } from ‘./error’

import { isIE, isIOS, isNative } from ‘./env’

// 是否使用微任务标识

export let isUsingMicroTask = false

// 回调函数队列

const callbacks = []

// 异步锁

let pending = false

function flushCallbacks () {

// 表示下一个 flushCallbacks 可以进入浏览器的任务队列了

pending = false

// 防止 nextTick 中包含 nextTick时出现问题,在执行回调函数队列前,提前复制备份,清空回调函数队列

const copies = callbacks.slice(0)

// 清空 callbacks 数组

callbacks.length = 0

for (let i = 0; i < copies.length; i++) {

copiesi

}

}

let timerFunc

// 浏览器能力检测

// 使用宏任务或微任务的目的是宏任务和微任务必在同步代码结束之后执行,这时能保证是最终渲染好的DOM。

// 宏任务耗费时间是大于微任务,在浏览器支持的情况下,优先使用微任务。

// 宏任务中效率也有差距,最低的就是 setTimeout

if (typeof Promise !== ‘undefined’ && isNative(Promise)) {

const p = Promise.resolve()

timerFunc = () => {

p.then(flushCallbacks)

if (isIOS) setTimeout(noop)

}

isUsingMicroTask = true

} else if (!isIE && typeof MutationObserver !== ‘undefined’ && (

isNative(MutationObserver) ||

MutationObserver.toString() === ‘[object MutationObserverConstructor]’

)) {

let counter = 1

const observer = new MutationObserver(flushCallbacks)

const textNode = document.createTextNode(String(counter))

observer.observe(textNode, {

characterData: true

})

timerFunc = () => {

counter = (counter + 1) % 2

textNode.data = String(counter)

}

isUsingMicroTask = true

} else if (typeof setImmediate !== ‘undefined’ && isNative(setImmediate)) {

timerFunc = () => {

setImmediate(flushCallbacks)

}

} else {

timerFunc = () => {

setTimeout(flushCallbacks, 0)

}

}

export function nextTick (cb?: Function, ctx?: Object) {

let _resolve

// 将 nextTick 的回调函数用 try catch 包裹一层,用于异常捕获

// 将包裹后的函数放到 callback 中

callbacks.push(() => {

if (cb) {

try {

cb.call(ctx)

} catch (e) {

handleError(e, ctx, ‘nextTick’)

}

} else if (_resolve) {

_resolve(ctx)

}

})

// pengding 为 false, 执行 timerFunc

if (!pending) {

// 关上锁

pending = true

timerFunc()

}

if (!cb && typeof Promise !== ‘undefined’) {

return new Promise(resolve => {

_resolve = resolve

})

}

}

总结:

1、运用异步锁的概念,保证同一时刻任务队列中只有一个 flushCallbacks。当 pengding 为 false 的时候,表示浏览器任务队列中没有 flushCallbacks 函数;当 pengding 为 true 的时候,表示浏览器任务队列中已经放入 flushCallbacks;待执行 flushCallback 函数时,pengding 会被再次置为 false,表示下一个 flushCallbacks 可进入任务队列

2、环境能力检测,选择可选中效率最高的(宏任务/微任务)进行包装执行,保证是在同步代码都执行完成后再去执行修改 DOM 等操作

3、flushCallbacks 先拷贝再清空,为了防止nextTick嵌套nextTick导致循环不结束

具体细节参考如上文章

虚拟DOM的理解

虚拟 DOM 的出现解决了浏览器的性能问题。虚拟 DOM 是一个用 JS 模拟的 DOM 结构对象(Vnode),用于频繁更改 DOM 操作后不立即更新 DOM,而是对比新老 Vnode,更新获取最新的Vnode,最后再一次性映射成真实的 DOM。这样做的原因是操作内存中操作 JS 对象速度比操作 DOM 快很多。

举个例子

real dom

    • 28
      点赞
    • 8
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值