IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue源码阅读(15):虚拟DOM -> 正文阅读

[JavaScript知识库]Vue源码阅读(15):虚拟DOM

虚拟DOM 是 Vue 中一个很重要的知识点,今天和大家分享一下我的的理解。

1,虚拟DOM 解决了什么问题

在 web 页面中,操作真实 DOM 的成本是很高的,所以一款优秀的 MVVM 框架必须尽可能的在状态发生变化的时候,最少量的操作真实 DOM,以此提高 web 页面的性能。

在 Vue 中,如果组件中的状态发生变化,这个组件就会被重新渲染,组件重新渲染最简单的方式是不管是哪一个状态发生了变化,只要将组件对应的真实 DOM 删除,再根据最新的状态生成一份最新的真实 DOM,并将其显示到页面上即可。这种方式非常的简单,但是缺点也是显而易见的,它大量的操作了真实 DOM,性能表现很不好,一个状态改变,只重新渲染这个状态所关联的真实 DOM 即可,没必要把其他的无关真实 DOM 也重新渲染。

所以 Vue 需要另外一种组件渲染的方案,这种方案要保证状态发生变化的时候,只重新渲染与这个状态相关联的真实 DOM,Vue 的解决方案就是虚拟DOM。

2,虚拟DOM 的运行流程总览

看这一小节之前,最好先回顾一下前面模板编译和响应式的知识,博客链接如下:

模板编译:

Vue源码阅读(11):聊聊模板编译

Vue源码阅读(12):解析器

Vue源码阅读(13):优化器

Vue源码阅读(14):代码生成器

响应式系统:

Vue源码阅读(6):响应式原理讲解的前期准备

Vue源码阅读(7):将数据转换成响应式的

Vue源码阅读(8):以组件的渲染为例看依赖收集和变化侦测


2-1,通过 render 函数生成 vnode

redner 函数的作用是根据最新的状态生成最新的 vnode,vnode 也不是什么神奇的东西,可以把它看作是真实 DOM 的 Javascript 对象字面量的表示,通过这个对象字面量可以创建出对应的真实DOM节点,也就是说这个对象字面量中的属性可以很好的描述一个DOM节点。

2-2,初次渲染组件的时候

在初次渲染组件到页面的时候,虚拟DOM 无需进行 vnode 的对比工作(diff 算法),直接通过 vnode 创建出对应的真实DOM,然后显示在页面上即可。

// export const patch: Function = createPatchFunction({ nodeOps, modules })
// createPatchFunction 的作用:根据当前的运行环境,创建出对应的 patch 方法,并返回。
export function createPatchFunction (backend) {
  ......
  // 在首次渲染和组件更新时调用
  return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) {
    if (isUndef(oldVnode)) {
      // 首次渲染时,会执行到这里。
      // empty mount (likely as component), create new root element
      isInitialPatch = true
      createElm(vnode, insertedVnodeQueue, parentElm, refElm)
    } else {
      // 组件更新,重新渲染,会执行到这里。
      ......
    }
  }
}

2-3,状态改变,组件重新渲染

当组件使用的状态发生改变的时候,会触发该组件的重新渲染,这一功能是借助响应式系统实现的,这一块不了解或者忘记了的朋友可以看我在上面列出来的文章,关键代码如下所示:

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  // 将 el 设值到 vm 中的 $el
  vm.$el = el

  // 触发执行 beforeMount 生命周期函数(挂载之前)
  callHook(vm, 'beforeMount')

  // 一个更新渲染组件的方法
  let updateComponent = () => {
    // vm._render() 函数的执行结果是一个 VNode
    // vm._update() 函数执行虚拟 DOM 的 patch 方法来执行节点的比对与渲染操作
    vm._update(vm._render(), hydrating)
  }

  // 这里的 Watcher 实例是一个渲染 Watcher,组件级别的
  vm._watcher = new Watcher(vm, updateComponent, noop)

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}

组件中的数据发生变化的时候,会触发执行该组件对应渲染 Watcher 的 updateComponent 方法,在 updateComponet 方法中,会执行 render 函数生成最新的 vnode,然后通过 _update 更新渲染页面。

2-4,在 _update 方法中,调用 patch 方法对比新老 vnode,然后更新视图

patch 函数能够对比新老 vnode,找出两者之间的不同点,然后针对这些不同点对?web 页面中的真实 DOM 进行更新。

这么做的原因是:Javascript 操作对象字面量的速度比操作真实 DOM 的速度快得多,因此可以把大量的 DOM 操作转移到操作对象字面量中,在这一环节就计算出哪些真实 DOM 需要更新,然后在操作真实 DOM 的时候,直接更新对应的 DOM 即可,这样就可以最大限度的减少 DOM 的操作,提高性能。

对应的源码如下所示:

// _update 方法的作用是将 VNode 映射成真实的 DOM
  // 调用的时机有两个:(1)刚渲染时;(2)依赖的数据更新时(页面需要重新渲染)
  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
    const vm: Component = this
    if (vm._isMounted) {
      // 如果当前的 vm 已经挂载了的话,说明当前是第(2)中情况,所以需要执行 beforeUpdate 回调函数
      callHook(vm, 'beforeUpdate')
    }
    const prevEl = vm.$el
    // 上一次渲染时的 VNode
    // 第一次渲染时,为空
    const prevVnode = vm._vnode
    vm._vnode = vnode
    if (!prevVnode) {
      // 首次渲染
      ......
    } else {
      // 依赖的数据更新时,页面需要重新渲染,调用 patch 进行 diff 算法比对新老 vnode
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
  }

2-5,虚拟DOM 运行流程总结

?3,总结

虚拟DOM 主要做了两件事:

  • 提供了能够描述真实DOM 的 vnode
  • 借助 diff 算法对新老 vnode 进行对比,然后只更新有差异的地方

这一篇文章对虚拟DOM 总体做了介绍,让大家对总体知识有所了解,接下来,我将针对上面提到的两点各写一片文章进行详细的讲解,分别是 vnode 和 diff 算法。

?

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:09:16  更:2021-08-09 10:11:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 11:50:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码