2022【前端】原理式相关凉解

104 阅读7分钟

第一部分分享

1.vue2和vue3响应式数据的理解

响应式:就是数据改变,对应的视图也会改变

  • vue2:是通过Object.defineProperty(),如果是多层就要递归
  • vue3:采用proxy,如果是多层次数据,用户不使用,就不会递归

2.vue2中是如何检测数组变化的

  • vue2中没有使用defineProperty对这个数组的每一项进行拦截,而是选择重写数组 {push}方法
  • 数组中如果是对象的数据类型,也继续递归
  • 数组的索引和长度变化是无法监控的

3.vue中如何进行依赖收集?

  • 每一个属性都有一个dep,存放我们我们所依赖的watcher,当属性变化后通知自己对应的watcher去更新
  • 默认在渲染的时候(获取这个响应式数据),此时就会触发属性收集依赖dep.depend()
  • 当属性发生变化时触发watcher通过dep.notify()
  • data => dep => ,在页面上使用 => watcher

4.nextTick在哪里使用?原理?

  • 使用nextTick中的回调函数 在下一次DOM更新循环结束之后 执行回调
  • 用于获取更新后的DOM
  • vue中的数据更新是异步的,使用nextTick方法可以保证用户定义的逻辑在更新之后执行

实现

1)作用:将回调延迟到下次DOM更新循环之后执行

2)原因:VUE在更新DOM时是异步的,vue检测到数据变化后,不会立即更新DOM,而是会开启一个事件队列,并缓冲同一时间循环中的所有数据变更,在下一次tick中,执行更新DOM。

3)js的运行机制:js是单线程的,基于事件循环,有宏任务和微任务。

4)内部原理:

  • 能力检测:Promise.then(微), MutationObserve(微),setImmediate(微),setTimeout(宏)
  • 将回调函数推入回调队列,锁上易步锁,执行回调。

5.vue为什么需要虚拟DOM

  • 如果直接操组真实DOM 性能低
  • vnode就是一个js对象,可以理解为他是真实DOM的抽象

6.vue的diff算法原理

  • vue2:深层递归+双指针 1)判断是不是同一元素,不是同一元素,直接替换

2)是同一元素 => 对比属性 => 对比儿子(

  • old有,new没有;
  • old没有,new有;
  • 都是文本的情况;
  • 都有children ) => 双指针: 头头,尾尾,头尾,尾头
  • vue3:采用最长递增子序列

7.key的作用和原理

  • vue中在patch过程中通过这个key可以判断两个vnode节点是否相同(可以复用老的节点)
  • 没有key导致更新的时候出错

8.作用域和作用域链

  • 作用域:就是一个规则,用来查找变量
  • 作用域链:多层嵌套作用域

9.怎么实现防抖

防抖和节流

  • 防抖:高频率触发的事件,在指定的单位时间内,只响应最后一次,如果在指定的时间在触发,则重新计算时间(后面触发的事件执行,替代了前面的事件)
  • 节流:高频率触发的事件,在指定的单位时间内,只响应第一次(前面触发的执行前,忽略后面的事件)

区别:  节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而防抖只是在最后一次事件后才触发一次函数。

防抖

  • 定时器
  • 高阶函数 高阶函数:1. 函数的返回值是一个函数;2.它的参数是一个函数

防抖的🌰

function change(val){
    console.log("click",val);
}
function delay(cb,time) {
    let timer // 这个变量会保存在内存,只会创建一次,使用闭包哦
    return function(){
        clearTimeout(timer);
        timer = setTimeout(() => {
            cb(1);
        },time)
    }
}
// 防抖
btn.addEventListener('click',delay(change,1000))

10.关于闭包

概念

  • 函数中返回一个函数
  • 函数声明的作用域和函数使用的作用域不同

用途

  • 1.获取私有作用域中的变量
  • 2.这些变量可以保存到内存中 关于2 的🌰:
function a() {
    let n = 0 // 保存到内存中
    function add() {
        n++;
        return n
    }
    return add
}
let getN = a();
console.log(getN()) //1
console.log(getN()) //2  

来道面试题🌰:

var fnArr = [];
for (var i = 0; i < 10; i++) {
    fnArr[i] = function(){
        return i
    }
}
console.log(i) // 10
console.log(fnArr[3]()) // 10 也是10i作用域全局

添加闭包保存🌰

var fnArr = [];
for (var i = 0; i < 10; i++) {
    fnArr[i] = (function(){
        // 闭包
        let j = i; // 保存变量
        return function(){
            return j
        }
    })()
}
console.log(fnArr[3]()) // 3

第二部分分享

1.vue2 双向绑定

view和model相互实时更新原理:

Object.defineProperty数据劫持+发布者-订阅者(依赖收集)模式

observer,compile,watcher

  • observe是一个数据监听器,核心方法是Object.defineProperty
  • watcher是一个订阅者,将Observer发来的update消息处理,执行更新
  • compile是一个指令解析器,对需要监听的节点和属性进行扫描和解析。

此模式的优点:不需要显式调用,可以直接通知变化,更新视图;劫持了属性setter,不需要额外的diff操作

Object.defineProperty缺点

  • 不能监听数组
  • 不能监听整个对象,只能监听属性
  • 不能监听属性的增删,只能监听变化

3.0版本使用Proxy

  • 可以监听数组
  • 可直接监听整个对象,不用层层递归属性
  • get和set时候直接有参数,不需要单独存储变量
  • new Proxy()会返回一个新对象,不会污染源对象。

2.数据不更新问题

1)更新的原理:

在数据读取时收集依赖,在赋值时通知依赖更新。

2)object有defineProperty方法,通过getter,setter只监听了属性的读取和赋值,但是新增属性和删除属性没有检测,所以专门提供了$set$delete来实现

3)array,没有defineProperty方法,没有setter,通过get和新建数组方法拦截器修改原生方法push,pop,shift,unshift,splice,sort,reserve来实现监听,而通过修改数组下标操作数组的不会被检测,所以专门提供了$set$delete来实现

4)$set(target, key, value)$delete(target, propertyName/index)方法原理

  • 判断target是否是undefined,null,或者原始类型,或者vue实例,或者vue实例的跟数据对象
  • target为数组,则还是通过调用splice操作索引更新数据
  • target为对象,且为响应式,则调用defineReactive操作数据
  • 更新完数据后通知依赖更新

3.computed和watch和methods

computed

  • 设计初衷:为了使模板中的逻辑运算更简单
  • 适用于数据被重复使用或者计算复杂费时的场景; 依赖其他数据的场景
  • 读取缓存,依赖不变,则不需重新计算。(根据dirty标志判断)

watch是对数据的监听回调

computed和watch的区别

相同点:都会观察页面的数据变化

不同点:

  • computed是读取缓存,watch每次都要重新执行;
  • watch更适合数据变化时的异步操作和开销较大的操作。

computed和methods的区别

computed依赖缓存,可以定义getter和setter,但是methods不行

4.vue-router的模式区别

1)abstract:非浏览器环境下使用

2)hash:

  • 默认。监听hashchange实现。
  • 优点,兼容性好,ie8支持
  • 缺点:看起来奇怪 3)history:
  • h5新增的。允许开发者直接修改前端路由而不重新触发请求页面
  • 实现原理:监听popstate事件。能监听到用户点击浏览器的前进后退事件或者手动调用go,back,forward事件;不能监听到pushState和replaceState事件。
  • 为了避免浏览器刷新出现的404页面,需要在服务端配置兼容。
  • 如果浏览器不支持,会降级到hash模式

通过vue.use插件机制和vue.mixin将store在beforeCreate和destroyed生命周期进行混入。

5.vuex

  • vuex解决了vue项目中的数据状态管理问题
  • 是组件通信的一种方式。
  • 原理:创建了单一的状态树,包含state,mutation,action,getter,module。
  • view(dispatch)action(commit)mutation(mutate)state(render)view
  • 通过vue的data和computed,让state变成响应式,
  • 通过vue.use插件机制和vue.mixin将store在beforeCreate生命周期进行混入。

6.keep-alive内置组件和LRU算法(队列)

1)自身不会渲染成DOM,没有常规的<template>标签,是个函数组件,被他包裹的组件,切换时会被缓存在内存中,而不是销毁。

  • 可以有条件的缓存:include(匹配到的缓存),exclude(匹配到的不缓存),max(最多可以缓存多少组件实例)

2)原理:

内部维护了this.cache(缓存的组件对象)和this.keys(this.cache中的key),运用LRU策略。

  • 命中了缓存的组件要调整组件key的顺序。
  • 缓存的组件数量如果超过this.max时,要删除第一个缓存组件。
  • LRU(Least recently used,最近最少使用):根据数据的历史访问记录来进行淘汰数据。“如果数据最近被访问过,那么将来被访问的几率也更高。”

3)生命周期钩子:

activated和deactivated,被keep-alive包括的组件激活和停用时调用。先停用组件的deactivated,再激活组件的activated