博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入了解virtual dom
阅读量:6279 次
发布时间:2019-06-22

本文共 4551 字,大约阅读时间需要 15 分钟。

前言

vdom 是 vue 和 React 的核心,先讲哪个都绕不开它

vdom 比较独立,使用也比较简单

如果面试问到 vue 和 React 和实现,免不了问 vdom,带着三个问题去深入了解

问题

  • vdom 是什么?为何会存在 vdom?
  • vdom 的如何应用,核心 API 是什么
  • 介绍一下 diff 算法

1、vdom 是什么?为何会存在 vdom?

  • virtual dom,虚拟 DOM
  • 用JS模拟DOM的结构
  • DOM 变化的对比,放在 JS 层来做(图灵完备语言)
  • 提高重绘性能

DOM结构

  • item1
  • item2
复制代码

JS模拟

{    tag:'ul',    attrs:{        id:'list'    },    children:[        {            tag:'li',            attrs:{                className: 'item'            },            children:['item1']        },{            tag:'li',            attrs:{                className: 'item'            },            children:['item2']        }    ]}复制代码

设计一个需求场景

用jQuery实现

遇到的问题

  • DOM的操作是“昂贵”的,js运行效率高
  • 尽量减少DOM的操作,而不是推倒重来
  • 项目越复杂,影响越严重
  • vdom即可解决这些问题

问题解答

  • virtual dom , 虚拟 DOM
  • 用 JS 模拟 DOM 结构
  • DOM 操作非常“昂贵”
  • 将 DOM 对比操作放在 JS 层,提高效率

2、vdom 的如何应用,核心 API 是什么

  • 介绍 snabbdom (vdom的一个库)
  • 重做之前的 demo
  • 核心 API

snabbdom 一个注重简单性、模块化、强大功能和性能的虚拟DOM库。

介绍 snabbdom - h 函数

介绍 snabbdom - patch 函数

重做demo

// // // // // // var snabbdom = window.snabbdom// 定义 patchvar patch = snabbdom.init([  snabbdom_class,  snabbdom_props,  snabbdom_style,  snabbdom_eventlisteners])// 定义 hvar h = snabbdom.hvar container = document.getElementById('container')// 生成 vnodevar vnode = h('ul#list', {}, [  h('li.item', {}, 'Item 1'),  h('li.item', {}, 'Item 2')])patch(container, vnode)document.getElementById('btn-change').addEventListener('click', function () {  // 生成 newVnode  var newVnode = h('ul#list', {}, [    h('li.item', {}, 'Item 1'),    h('li.item', {}, 'Item B'),    h('li.item', {}, 'Item 3')  ])  patch(vnode, newVnode) // 找出差异,渲染差异})复制代码
// jquery例子改造var snabbdom = window.snabbdom// 定义关键函数 patchvar patch = snabbdom.init([  snabbdom_class,  snabbdom_props,  snabbdom_style,  snabbdom_eventlisteners])// 定义关键函数 hvar h = snabbdom.h// 原始数据var data = [{    name: '张三',    age: '20',    address: '北京'  },  {    name: '李四',    age: '21',    address: '上海'  },  {    name: '王五',    age: '22',    address: '广州'  }]// 把表头也放在 data 中data.unshift({  name: '姓名',  age: '年龄',  address: '地址'})var container = document.getElementById('container')// 渲染函数var vnodefunction render(data) {  var newVnode = h('table', {}, data.map(function (item) {    var tds = []    var i    for (i in item) {      if (item.hasOwnProperty(i)) {        tds.push(h('td', {}, item[i] + ''))      }    }    return h('tr', {}, tds)  }))  if (vnode) {    // re-render    patch(vnode, newVnode)  } else {    // 初次渲染    patch(container, newVnode)  }  // 存储当前的 vnode 结果  vnode = newVnode}// 初次渲染render(data)var btnChange = document.getElementById('btn-change')btnChange.addEventListener('click', function () {  data[1].age = 30  data[2].address = '深圳'  // re-render  render(data)})复制代码

  • 使用 data 生成 vnode
  • 第一次渲染,将 vnode 渲染到 #container 中
  • 并将 vnode 缓存下来
  • 修改 data 之后,用新 data 生成 newVnode
  • 将 vnode 和 newVnode 对比

核心API:h 函数、patch 函数

  • h(‘<标签名>’, {…属性…}, […子元素…])
  • h(‘<标签名>’, {…属性…}, ‘….’)
  • patch(container, vnode)
  • patch(vnode, newVnode)

介绍一下 diff 算法

什么是diff算法

  • linux diff 命令
  • git diff (对比两个文件之间差异)

去繁就简

  • diff 算法非常复杂,实现难度很大,源码量很大
  • 去繁就简,讲明白核心流程,不关心细节
  • 面试官也大部分都不清楚细节,但是很关心核心流程
  • 去繁就简之后,依然具有很大挑战性,并不简单

vdom 为何用 diff 算法

  • DOM 操作是“昂贵”的,因此尽量减少 DOM 操作
  • 找出本次 DOM 必须更新的节点来更新,其他的不更新
  • 这个“找出”的过程,就需要 diff 算法

diff 算法的实现流程

  • patch(container, vnode)
  • patch(vnode, newVnode)

核心逻辑:createElement 和 updateChildren

// diff 算法实现// code demofunction createElement(vnode) {  var tag = vnode.tag // 'ul'  var attrs = vnode.attrs || {}  var children = vnode.children || []  if (!tag) {    return null  }  // 创建真实的 DOM 元素  var elem = document.createElement(tag)  // 属性  var attrName  for (attrName in attrs) {    if (attrs.hasOwnProperty(attrName)) {      // 给 elem 添加属性      elem.setAttribute(attrName, attrs[attrName])    }  }  // 子元素  children.forEach(function (childVnode) {    // 给 elem 添加子元素    elem.appendChild(createElement(childVnode)) // 递归  })  // 返回真实的 DOM 元素  return elem}// vnode newVnode comparefunction updateChildren(vnode, newVnode) {  var children = vnode.children || []  var newChildren = newVnode.children || []  children.forEach(function (childVnode, index) {    var newChildVnode = newChildren[index]    if (childVnode.tag === newChildVnode.tag) {      // 深层次对比,递归      updateChildren(childVnode, newChildVnode)    } else {      // 替换      replaceNode(childVnode, newChildVnode)    }  })}function replaceNode(vnode, newVnode) {  var elem = vnode.elem // 真实的 DOM 节点  var newElem = createElement(newVnode)  // 替换}复制代码

  • 节点新增和删除
  • 节点重新排序
  • 节点属性、样式、事件变化
  • 如何极致压榨性能
  • ......

answer:

  • 知道什么是 diff 算法,是 linux 的基础命令
  • vdom 中应用 diff 算法是为了找出需要更新的节点
  • vdom 实现过程,createElement 和 updateChildren
  • 与核心函数 patch 的关系

转载地址:http://niiva.baihongyu.com/

你可能感兴趣的文章
thrift的使用—servlet服务器端与as3客户端通信
查看>>
Spring集成ActiveMQ
查看>>
理解 Keystone 核心概念 - 每天5分钟玩转 OpenStack(18)
查看>>
NP系列三十六--利用ODR实现公司总部和分支机构的通讯
查看>>
遭遇ARP欺骗
查看>>
【移动开发】Android中一些你可能不太知道的东西
查看>>
MongoDB mapReduce案例分析一
查看>>
asp.net 关于提示“当前上下文中不存在名称"XXX"”的一种情况的解决办法
查看>>
MOSS系列二 创建第一个SharePoint站点
查看>>
Detach Volume 操作 - 每天5分钟玩转 OpenStack(55)
查看>>
MySQL5.6 部署MHA
查看>>
DG配置网络,报ORA-12514: TNS:listener does not...
查看>>
hadoop开启webHDFS服务及测试
查看>>
DC学院学习笔记(十七):分类及逻辑回归
查看>>
Spring Aop(一)——Aop简介
查看>>
document.createElement
查看>>
Outlook Anywhere 客户端配置详解
查看>>
Go语言学习资料整理
查看>>
精进不休 .NET 4.0 (3) - asp.net 4.0 新特性之动态数据(Dynamic Data)增强
查看>>
麻将游戏
查看>>