详解Angular中的变更检测(十五)- 扩展知识2

239 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情

我们发现在Vue和React中都有虚拟DOM的概念,那Angular中有吗?本来就来了解一下各种DOM。

各种DOM

DOM

DOM是JS操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个JS对象,从而可以用脚本进行各种操作(比如增删内容)。 DOM提供了一种表述形式将文档作为一个结构化的节点组以及包含属性和方法的对象。从本质上说,它将web页面和脚本或编程语言连接起来了。

这个比较简单,这里不在赘述。

虚拟DOM

Vue与React为了提高渲染性能,都不约而同的使用使用了虚拟Dom,而虚拟DOM并不是Vue和React的专属概念,它是完全可以独立于具体框架而存在的。它的出现要比这些框架要早。 image.png

什么是虚拟DOM

从react到Vue,虚拟DOM为这两个框架带来了跨平台的能力(react-nativewexx)。实际上它只是一层对真实DOM的抽象,以 javascript 对象(VNode节点)作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作是这棵树映射到真实环境上 在 javascript 对象中,虚拟 DOM 标线为一个 Object 对象。创建虚拟DOM就是为了更好将虚拟的节点渲染到页面是图中,所以虚拟DOM对象的节点与真实DOM的属性一一对应。

为什么需要虚拟DOM

我们知道虚拟DOM和真实的DOM对象存在一一对应的关系,和我们为何不直接使用真实DOM对象呢?前面说过,使用虚拟DOM是为了提高渲染性能,而虚拟DOM介于真实DOM对象和View之间,如果没有虚拟DOM,那么我们直接通过DOM对象,来更新视图就可以了。现在而且要先构造出虚拟DOM,通过虚拟DOM去更新View,多出了一步,效率岂不是更低吗?

DOM 是很慢的,其元素非常庞大,真实的DOM节点,一个最简单的 div 也包含着几百个属性,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验。

耗损时间对比

  • 使用虚拟DOM算法的损耗计算: 总损耗 = 虚拟DOM增删改+(与Diff算法效率有关)真实DOM差异增删改+(较少的节点)排版与重绘;
  • 直接操作真实DOM的损耗计算: 总损耗 = 真实DOM完全增删改+(可能较多的节点)排版与重绘;

很多人认为虚拟DOM最大的优势就是 diff 算法,减少Javascript操作真实DOM带来的性能消耗。虽然遮一个虚拟DOM带来的一个优势,但并不是全部。虚拟DOM最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅是局限于浏览器的DOM,可有是安卓和IOS的原生组件,可以是很火的小程序,也可以是各种GUI

如何实现虚拟DOM

虚拟 DOM 标线为一个 Object 对象。并且最少包含标签吗(tag)、属性(attr)和子元素对象(children)三个属性,不同框架对这三个属性的命名可能会有差别。 但实际上,虚拟DOM的结构并不是只能是上面说的这种结构。Vue3.0中的虚拟DOM就和2.0中的虚拟DOM有一些区别。

React也有其自己独特的虚拟DOM结构,尤其是采用Fiber架构之后。这里不在详细展开了。

Shadow DOM

之前的文章有介绍过,这里不在赘述。

增量DOM

Angular中的增量Dom的主要概念是将组件编译成一系列的指令,这些指令去创建DOM树并在数据更改时就地的更新它们。

image.png

@Component({
  selector: 'todos-cmp',
  template: `
    <div *ngFor="let t of todos|async">
        {{t.description}}
    </div>
  `
})
class TodosComponent {
  todos: Observable<Todo[]> = this.store.pipe(select('todos'));
  constructor(private store: Store<AppState>) {}
}

编译后:

var TodosComponent = /** @class */ (function () {
  function TodosComponent(store) {
    this.store = store;
    this.todos = this.store.pipe(select('todos'));
  }

  TodosComponent.ngComponentDef = defineComponent({
    type: TodosComponent,
    selectors: [["todos-cmp"]],
    factory: function TodosComponent_Factory(t) {
      return new (t || TodosComponent)(directiveInject(Store));
    },
    consts: 2,
    vars: 3,
    template: function TodosComponent_Template(rf, ctx) {
      if (rf & 1) { // create dom
        pipe(1, "async");
        template(0, TodosComponent_div_Template_0, 2, 1, null, _c0);
      } if (rf & 2) { // update dom
        elementProperty(0, "ngForOf", bind(pipeBind1(1, 1, ctx.todos)));
      }
    },
    encapsulation: 2
  });

  return TodosComponent;
}());

增量DOM的优点

1.降低编译后的体积; 2 占用较低的内存;

与虚拟 DOM 不同,增量 DOM 在重新呈现应用程序 UI 时不会生成真实 DOM 的副本。此外,如果应用程序 UI 没有变化,增量 DOM 就不会分配任何内存。大多数情况下,我们都是在没有任何重大修改的情况下重新呈现应用程序 UI。因此,按照这种方法可以极大的减少设备内存使用。