🎀 基本概念

一、MVVM 是一种软件架构模式,具体指什么

  • Model 是指数据模型,泛指后端的各种业务逻辑处理和数据操控,对于前端而言主要是后端提供的 api 接口
  • View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建
  • ViewModel 是视图数据层,完全解耦了 View 层和 Model 层
    • ViewModel 负责与 Model 层交互,所封装出来的数据模型包括视图的状态和行为两部分。Model 层的数据模型只包含视图的状态
    • MVVM 采用 **双向数据绑定**,ViewModel 的内容会实时展现在 View 层

二、真实DOM的解析过程? 虚拟DOM实现原理

1、浏览器渲染引擎工作流程:创建 DOM 树 —> 创建 Style Rules -> 构建 Render 树 —> 布局 Layout -—> 绘制 Painting

  • 构建 DOM 树:用 HTML 解析器,解析 HTML 元素,构建一棵 DOM 树;
  • 生成样式表:用 CSS 解析器,解析 CSS 文件和元素上的 inline 样式,生成页面的样式表
  • 构建 Render 树:将 DOM 树和样式表关联起来(Attachment),每个 DOM 节点都有 attach 方法,接受样式信息,返回一个 render 对象(aka renderer),这些 render 对象最终会被构建成一棵 Render 树
  • 布局(确定节点坐标):根据 Render 树结构,为每个 Render 树上的节点确定一个在显示屏上出现的精确坐标
  • 绘制页面:根据 Render 树和节点显示坐标,调用每个节点的 paint 方法,调用 GPU 绘制,合成图层
    框架进阶 - 图1

2、Virtual DOM 优缺点及算法主要实现

  • 优点:保证性能下限;无需手动操作 DOM;跨平台
  • 缺点:无法进行极致优化
  • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象
  • diff 算法 — 比较两棵虚拟 DOM 树的差异
  • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树渲染

三、Event Loop 原理分析

1、背景知识:浏览器渲染进程(浏览器内核)

  • GUI渲染线程:负责渲染浏览器界面,解析 HTML、CSS、构建 DOM 树和 RenderObject 树,布局和绘制等。可用于重绘(Repaint)、重排(Reflow)
  • JS引擎线程:js 内核,负责处理 javascript 脚本程序(V8 引擎)
  • 事件触发线程:用来控制事件循环,当对应的事件符合触发条件被触发时,事件线程会把事件添加到待处理事件队列的队尾,等待 js 引擎的处理
  • 定时触发器线程:负责执行异步定时器一类的函数的线程,如: setTimeout,setInterval
    • 主线程依次执行代码时,遇到定时器,会将定时器交给该线程处理,当计数完毕后,事件触发线程会将计数完毕后的事件加入到任务队列的尾部,等待 JS 引擎线程执行
  • 异步http请求线程:负责执行异步请求一类的函数的线程,如: Promise,axios,ajax 等
    • 主线程依次执行代码时,遇到异步请求,会将函数交给该线程处理,当监听到状态码变更,如果有回调函数,事件触发线程会将回调函数加入到任务队列的尾部,等待 JS 引擎线程执行。

注意:GUI 渲染线程和 JS 引擎线程是互斥的,后者执行时前者会被挂起;JS 执行时间过长,会造成页面渲染不连贯,导致页面渲染加载阻塞
2、Event Loop机制
JS 是单线程的,事件环可以理解为实现异步的一种方式。
框架进阶 - 图2

  • 主线程(main thread):JS 引擎线程,主线程的执行过程是一个 tick
  • 执行栈(execution stack):同步任务在主线程上运行,会形成一个执行栈
  • 任务队列(task queue):由事件触发线程管理,含宏任务/微任务队列
    • 异步任务触发时,将异步线程(如定时触发器线程异步http请求线程)提供的回调事件,缓存到任务队列中
    • 异步任务执行时(即主线程空闲时,同步任务执行完毕),将任务队列中的异步任务回调事件,提供给主线程读取,执行栈执行
  • 宏任务:macrotask,也叫 tasks。
    • script(主代码块)
    • setTimeout、setInterval、setImmediate、MessageChannel
    • requestAnimationFrame(rAF)、UI Rendering、I/O
  • 微任务:microtask,也叫 jobs。当前宏任务执行后立即执行(渲染前)的任务
    • Promise(Async/await)、MutationObserver(监听 DOM 修改事件)
    • Process.nextTick(Node 独有)
  • 区别:事件环迭代开始后,若宏任务队列中安排了新任务,则直到下一次迭代才会运行。每次宏任务退出且执行上下文堆栈为空时,微任务队列中的每个微任务都会按顺序执行,若微任务队列中安排了新任务,微任务会继续执行直到队列为空。

注意:

  • 等待delay时间后,setTimeout、setInterval 的回调事件才放入任务队列中
  • new Promise() 构造函数是宏任务的同步代码,而非微任务
  • HTML5 中规定 setTimeout 的最小时间延迟是 4ms
  • 动画回调(rAF 回调队列):渲染前使用 rAF

框架进阶 - 图3

💞 Vue 进阶

1、Vue 如何实现双向数据绑定

  • View => Data:视图变化更新数据,通过事件监听的方式实现
  • Data => View:数据变化更新视图,数据劫持+发布订阅模式
    • 监听器Observer:劫持和监听所有属性,属性发生变化,就通知订阅者
      • 遍历数据对象所有的属性,并使用Object.defineProperty把这些属性全部转为 getter/setter,在属性被访问和修改时通知变更
    • 订阅器Dep:收集订阅者、数据变化时通知订阅者
    • 订阅者Watcher:收到属性的变化通知,执行相应的方法来更新视图
      • 在组件渲染的过程中触发getter进行依赖dep(数据属性记录)收集,把 Watcher 实例存放到对应的 Dep 对象中去
      • 依赖项depsetter触发时,通知watcher使关联组件重新渲染
    • 解析器Compiler:解析模板指令,初始化视图;将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器

框架进阶 - 图4

2、Proxy 与 Object.defineProperty 优劣对比

  • Object.defineProperty
    • 缺点
      • 对于对象:只能对属性进行数据劫持,所以需要深度遍历整个对象
      • 对于数组:不能监听到数据的变化,需要遍历数组
    • 优点:兼容性好,支持 IE9
  • Proxy
    • 优点
      • 对于对象:可以直接监听对象而非属性
      • 对于数组:可以直接监听数组的变化
      • 多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的
    • 缺点:存在浏览器兼容性问题,而且无法用 polyfill 磨平

3、NextTick 原理分析

  • Vue.js 默认使用异步执行 DOM 更新
  • Vue.nextTick(vm.$nextTick):在下次 DOM 更新循环结束之后执行延迟回调,用于获取更新后的 DOM。实现方式(优雅降级):
    • macrotask的实现
      • setImmediate
      • MessageChannel:创建一个消息通道,通过其两个 MessagePort 属性发送数据
      • setTimeout(fn,0)
    • microtask的实现:promise,不支持的话直接指向 macro task 的实现

4、Vue 中的 key 有什么作用

key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的

  • 准确: 如果不加 key,那么 vue 会选择复用节点(Vue 的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的 bug
  • 快速: key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1)

🍵 React 进阶

1、父子兄弟组件生命周期