vue面试题汇总

1、如何理解MVVM原理?

在这里插入图片描述

先解释MVVM是什么:也就是当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改,反之修改V层则会通知M层数据进行修改,以此也实现了视图与模型层的相互绑定;

2、v-model实现原理?

model只不过是一个语法糖而已,真正的实现靠的还是
v-bind:绑定响应式数据
触发oninput事件并传递数据

<input v-model="sth" />
//  等同于
<input :value="sth" @input="sth = $event.target.value" /> //自html5开始,input每次输入都会触发oninput事件,所以输入时input的内容会绑定到sth中,于是sth的值就被改变;
//$event 指代当前触发的事件对象;
//$event.target 指代当前触发的事件对象的dom;
//$event.target.value 就是当前dom的value值;
3、响应式数据的原理是什么?(双向数据绑定原理?)
  • 核心点: Object.defineProperty
  • 默认 Vue 在初始化数据时,会给 data 中的属性使用 Object.defineProperty 重新定义所有属
    性(只会劫持已经存在的属性)多层对象通过递归实现劫持,当页面取到对应属性时,会进行依赖收集(收集当前组件的watcher) 如果属性发生变化会通知相关依赖进行更新操作。

在这里插入图片描述

4、vue是如何检测数组变化的?
  • 数组考虑到性能问题没有使用defineProperty对数组的没一项进行拦截,而是选择对数组的方法进行重写。
  • 在vue中修改数组的索引和长度是无法进行监控的。需要通过以上7中变异的方法修改数组才会触发数组对应的watcher进行更新。数组中如果有对象类型也会递归劫持。
    在这里插入图片描述
5、vue中模板编译原理?
  • 将template模板转化成ast语法树
  • 根据ast语法树生成render函数(字符串拼接 + new Function + with(this){ } )
  • 使用render函数生成虚拟dom,在根据虚拟DOM创建正式dom替换

为什么要使用虚拟dom?

  • 虚拟dom就是用js对象描述真实dom,是真实dom的抽象,直接操作dom性能低,但操作js层效率高
  • 可以使用diff算法对比差异来更新,减少真实DOM操作
6、vue中的diff原理?
7、生命周期钩子如何实现的?
  • Vue的生命周期钩子就是回调函数,当创建组件实例的过程中会调用响应的钩子方法。
  • 内部主要使用callHook方法来调用对应的方法。核心是一个发布订阅模式,将钩子订阅好(内部Vue.$options内部会把全局钩子和实例的钩子进行合并,采用数组方式存储),在对应的阶段进行发布!
import {mergeOptions} from '../utils/index'

export function initGlobalAPI(Vue){
    // 整合了所有全局相关的内容
    Vue.options = {}

    Vue.mixin = function(mixin){
        // this是Vue
        // mixin 合并 把mixin()混入进来的数据和Vue.options合并在一起
        this.options = mergeOptions(this.options,mixin)
    }
    // 使用  
    // Vue.mixin({
    //     a:1,
    //     beforeCreate() {
    //         console.log('mixin1')
    //     }
    // })
    // Vue.mixin({
    //     b:2,
    //     beforeCreate() {
    //         console.log('mixin2')
    //     }
    // })

    // console.log(Vue.options)

}
-------------------------重点mergeOptions()
/ 合并策略  后续可以添加不同的策略
let strats = {}
// 添加生命周期的合并策略
LIFECYLE_HOOKS.forEach(hook=>{
    strats[hook] = mergeHook
})

// 生命周期的合并策略
function mergeHook(parentVal,childVal){
    if(childVal){  // 有新值
        if(parentVal){  // 有新值 也有老值
            return parentVal.concat(childVal)
        }else{  // 只有新值 没有老值
            return [childVal]
        }

    }else {  // 没有新值
        return parentVal
    }
}

// 合并
export function mergeOptions(parent,child){
    const options = {}
    for(let key in parent){  // 把parent的属性和child比较,并合并到options
        mergeField(key)
    }

    for(let key in child){   // 把child上没有合并过的属性 合并到options上
        if(!parent.hasOwnProperty(key)){
            mergeField(key)
        }
    }
    
    // 默认的合并合并策略 但是有些属性 需要有特殊的合并方式
    function mergeField(key){
        if(strats[key]){  // 合并策略上有指定合并策略就使用合并策略
            return options[key] = strats[key](parent[key],child[key])
        }
        // parent:{data:{name:'haha'}}   child:{data:{age:'lalal'}}  两者key都是对象
        if(typeof parent[key] === 'object' && typeof child[key] === 'object'){
            options[key] = {
                ...parent[key],
                ...child[key]
            }   // 有重复的属性,child覆盖parent的属性
        }else if(child[key] == null){  //child没有可以,使用parent的属性 
            options[key] = parent[key]
        }else{  // parent和child的属性值一个是对象一个是值或者两个都是值  使用儿子的覆盖父亲的
            options[key] = child[key]
        }
    }

    return options;
}
---------------------------------- 初始化时new的实例会和Vue类上的的option合并
// 将用户传递的options和全局的options合并到自己身上
        vm.$options=mergeOptions(vm.constructor.options,options)   // vue中使用 this.$options 指代的就是用户new Vue是传递进来的数据
--------------------------- callHook调用指定生命周期
export function callHook(vm,hook){
    const handlers = vm.$options[hook]
    if(handlers){  // 找到对应生命钩子执行执行
        for(let i=0;i<handlers.length;i++){
            // call 为了保证当前生命周期内this指向当前实例
            handlers[i].call(vm)
        }
    }
}
8、如何进行依赖收集的?
  • 使用dep类来进行依赖管理,depend()方法用来收集依赖,notify()用来通知watcher更新
  • vue在渲染的时候会new watcher来渲染,渲染中会对变量进行取值触发get进行依赖收集,更新时触发set去通知weather更新视图
9、为何Vue采用异步渲染?

因为如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染.所以为了性能考虑。 Vue会在本轮数据更新后,再去异步更新视图.内部调用的是nextTick实现延迟更新.

在这里插入图片描述

10、nextTick在哪里使用?原理是?
  • nextTick的回调是dom更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,可以获取更新后的dom。原理:就是异步方法(promise/mutationObserver/setImmediate/setTimeout)
  • 补充回答:vue多次更新数据,最终会进行批量更新。内部调用的是nextTick实现延迟更新,用户自定义的nextTick回调会被延迟到更新完后调用,从而获取到更新后的dom
// 数据更新会触发update()方法,update()方法里面会调用queueWatcher(this)传入当前watcher

//schedular.js

let queue = []  // 存放watcher
let has = {}

import {nextTick} from '../utils/next-tick'

function flushSchedularQueue(){
    queue.forEach(watcher=>watcher.run())
    queue = []
    has = {}
}

export function queueWatcher(watcher){
    const id = watcher.id
    if(has[id] == null){
        queue.push(watcher)
        has[id] = true

        // vue里面使用Vue.nextTick
        // Vue.nextTick = promise/mutationObserver/setImmediate/setTimeout

        nextTick(flushSchedularQueue)

        // setTimeout(function(){
        //     queue.forEach(watcher=>watcher.run())
        //     queue = []
        //     has = {}
        // },0)
    }
-----------------------
//next-tick.js
// nextTick 在修改数据之后立即使用这个方法,获得更新后的dom
// 所以我们 延迟执行 

let callbacks = []   // 任务队列  [flushSchedularQueue,usenextTick]
let waiting = false
function flushCallback(){
    callbacks.forEach(cb=>cb())
    waiting = false
    callbacks=[]
}

export function nextTick(cb){  
    // 第一次调用nextTick的肯定是我们vue中数据更新后调用的
    // 之后 用户在多次调用 nextTick 是在修改数据之后
    // 我们先执行 flushSchedularQueue 进行更新渲染,在执行usenextTick用户的回调

    // 多次调用nextTick 如果没有刷新的时候就先把他放到数组中
    // 刷新后更改 waiting
    callbacks.push(cb)
    if(waiting===false){
        setTimeout(flushCallback,0)
        waiting = true
    }
}
11、vue的优缺点

优点:

  • 轻量级框架
  • 简单易学
  • 数据驱动视图,不用再重复大量操作dom
  • 组件化开发

缺点:

不适于seo优化,而且封装的比较厉害,可扩展性稍差,适合单人开发,适合中小型项目

12、vue中可以做的性能优化
  • data中的数据尽可能扁平化以免vue初始化时深度递归监测

  • 不改变的数据使用object.freeze冻结,不让vue设置为响应式的数据

  • 根据场景来选择v-if和v-show

  • 对于没有使用vue语法的静态结构使用v-pre不让vue去解析

  • 长列表中,不去直接渲染,而采用虚拟列表渲染 插件 - vue-virtual-scroll-list

  • 组件懒加载 + 骨架屏
    (组件懒加载)异步组件写法:
    在这里插入图片描述
    异步组件+骨架屏 (骨架屏同步导入的)
    在这里插入图片描述

  • 对于图片采用图片懒加载 插件 - vue-lazyload

  • UI组件库按需加载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值