8.1.4 Virtual DOM 的实现原理

本文为拉勾网大前端高薪训练营第一期笔记


8.1.4 Virtual DOM 的实现原理

虚拟DOM

虚拟DOM就是js对象描述dom对象,成本比真实DOM低很多,因为真实DOM的属性特别多

以snabbdom为例,导入时需要这样写

import {init, h, thunk} from 'snabbdom'

创建脚手架

md snabbdom-demo
cd snabbdom-demo
yarn init -y
yarn add parcel-bundler
//package.json
scripts:
	dev: parcel index.html --open
	build: parcel build index.html

snabbdom基本用法例子

import { h, init } from 'snabbdom'

// 1. hello world
// 参数:数组,模块
// 返回值:patch函数,作用对比两个vnode的差异更新到真实DOM
let patch = init([])
// 第一个参数:标签+选择器
// 第二个参数:如果是字符串的话就是标签中的内容
let vnode = h('div#container.cls', { 
  hook: {
    init (vnode) {
      console.log(vnode.elm)
    },
    create (emptyVnode, vnode) {
      console.log(vnode.elm)
    }
  }
}, 'Hello World')

let app = document.querySelector('#app')
// 第一个参数:可以是DOM元素,内部会把DOM元素转换成VNode
// 第二个参数:VNode
// 返回值:VNde
let oldVnode = patch(app, vnode)
//此时页面上是Hello World

vnode = h('div', 'Hello Snabbdom')

patch(oldVnode, vnode)
//此时页面上是Hello Snabbdom,对比了oldVnode和vnode的差异,然后更新

snabbdom设置子元素

// 2. div中放置子元素 h1,p
import { h, init } from 'snabbdom'

let patch = init([])

let vnode = h('div#container', [
  h('h1', 'Hello Snabbdom'),
  h('p', '这是一个p标签')
])

let app = document.querySelector('#app')

let oldVnode = patch(app, vnode)

setTimeout(() => {
  vnode = h('div#container', [
    h('h1', 'Hello World'),
    h('p', 'Hello P')
  ])
  patch(oldVnode, vnode)

  // 清空页面元素 -- 错误
  // patch(oldVnode, null)
	// 传注释节点来清空页面元素
  patch(oldVnode, h('!'))
}, 2000);

使用Snabbdom模块

官方提供了6个模块

  • attributes
    • 设置DOM元素的属性,使用setAttribute()
    • 处理布尔类型的属性
  • props
    • 和attributes模块相似,设置DOM元素的属性element[attr] = value
    • 不处理布尔类型的属性
  • class
    • 切换类样式
    • 注意:给元素设置样式是通过sel选择器
  • dataset
    • 设置data-*的自定义属性
  • eventlisteners
    • 注册和移除事件
  • style
    • 设置行内样式,支持动画
    • delayed/remove/destroy

以style和eventlisteners为例

import { init, h } from 'snabbdom'
// 1. 导入模块
import style from 'snabbdom/modules/style'
import eventlisteners from 'snabbdom/modules/eventlisteners'
// 2. 注册模块
let patch = init([
  style,
  eventlisteners
])
// 3. 使用 h() 函数的第二个参数传入模块需要的数据(对象)
let vnode = h('div', {
  style: {
    backgroundColor: 'red'
  },
  on: {
    click: eventHandler
  }
}, [
  h('h1', 'Hello Snabbdom'),
  h('p', '这是p标签')
])

function eventHandler () {
  console.log('点击我了')
}

let app = document.querySelector('#app')

let oldVnode = patch(app, vnode)

vnode = h('div', 'hello')
patch(oldVnode, vnode)

Snabbdom

  • patch(oldVnode, newVnode)
  • 打补丁,把新节点中变化的内容渲染到真实DOM,最后返回新节点作为下一次处理的旧节点
  • 对比新旧VNode是否相同节点(节点的key和sel相同)
  • 如果不是相同节点,删除之前的内容,重新渲染
  • 如果是相同节点,再判断新的VNode是否有text,如果有并且和oldVnode的text不同,直接更新文件内容
  • 如果新的VNode有children,判断子节点是否有变化,判断子节点的过程使用的就是diff算法
  • diff过程只进行同层级比较

createElm

8.1.4 Virtual DOM 的实现原理_第1张图片

patchVnode

8.1.4 Virtual DOM 的实现原理_第2张图片

updateChildren

对比节点时是对比sel key,如果没有key,就认为两个li是一样的,此时直接更新text

  • 对比新旧开始节点
  • 如果不同,对比新旧结束节点
  • 如果不同,对比旧开始,新结束节点
  • 如果不同,对比旧结束,新开始节点

你可能感兴趣的:(vue.js,vue.js)