virtual DOM(虚拟节点的实现)

本文通过对比传统DOM操作与虚拟DOM的实现方式,详细介绍了Snabbdom库的使用,包括h()与patch()核心方法,展示了如何利用虚拟DOM提高页面渲染效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

virtual DOM(虚拟节点的实现)

1 利用JS操作真实DOM节点实现页面渲染

  • 任务:在一个容器渲染(利用用户信息生成的)表格,再点击按钮进行数据修改,修改后再重新渲染修改后的信息。
  • 方式:利用jQuery操作DOM节点,一开始清空容器的内容,再生成表格,渲染表格。
  • 弊端:操作DOM很耗费性能,这种方式是直接替换容器中的所有节点,重复的节点无法复用,降低浏览器性能。
  • HTML代码:
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <!-- 用于存放表格的容器 -->
  <div id="container"></div>
  <!-- 用于修改数据的按钮 -->
  <button id="btn-change">change</button>
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  <!-- 此处要引入下边的代码 -->
  <scipt src="domReader.js"></script>
</body>
</html>
  • domReader.js:
//用户数据
const userData = [{
  name: '张三',
  age: 20,
  address: '北京'
}, {
  name: '蔡徐坤',
  age: 24,
  address: '广东'
}, {
  name: '蔡明',
  age: 26,
  address: '河南'
}]
//用于渲染的函数
function render(data){
  // 清空容器再创建,最后在操作DOM,耗费性能
  const $container = $('#container');
  $container.html('')
  var $table = $('<table>')

  // 遍历数据
  data.forEach((item,i) => {
  	// 第一行加上表头
    if (i===0) {
    $table.append(`
    <tr>
    <td>name</td>
    <td>age</td>
    <td>address</td>
    </tr>
    `)
    }
    $table.append(`
    <tr>
    <td>${item.name}</td>
    <td>${item.age}</td>
    <td>${item.address}</td>
    </tr>
    `)
  })
  // 将table添加到容器中
  $container.append($table)
}
// 一开始要执行一次初始化
render(userData)
$('#btn-change').click(() => {
  userData[0].name = '谢广坤'
  userData[1].age = 30
  userData[2].address = '深圳'
  render(userData)
})

2 利用snabbdom实现虚拟DOM

  • 核心方法:h()、patch()
  • h():用于生成虚拟节点树。
    • 使用方式:const vnode = h('标签名+id/class', {定义事件}, [存放子节点]或者内容)
  • patch(curNode, newNode):传入新旧节点,对比后自动实现替换、修改、移动节点等操作。
const  vnode = h('ul#list', { on: { click:handleClick }, [
	h('li', {}, 'item1'),
	h('li', {}, 'item2'),
	h('li', {}, 'item3')
]}
const newNode = h('ul#list', {}, '已清空所有的li')
path(vnode, newNode);
  • 核心方法的引入方式:
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.min.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>
  <script>
  	// 初始化snabbdom
    var snabbdom = window.snabbdom;
    var patch = snabbdom.init({
      snabbdom_class,
      snabbdom_props,
      snabbdom_style,
      snabbdom_eventlisteners
    })
    var h = snabbdom.h
 </script>
  • 虚拟DOM的实现:
  <script>
    // snabbom的必要引入和初始化操作
    const snabbdom = window.snabbdom;
    const patch = snabbdom.init({
      snabbdom_class,
      snabbdom_props,
      snabbdom_style,
      snabbdom_eventlisteners
    })
    const h = snabbdom.h
    // 用户数据
    const userData = [{
      name: '张三',
      age: 20,
      address: '北京'
    }, {
      name: '蔡徐坤',
      age: 24,
      address: '广东'
    }, {
      name: '蔡明',
      age: 26,
      address: '河南'
    }];
    userData.unshift({
      name: '姓名',
      age: '年龄',
      address: '地址'
    })
    function render(data, curNode){
      const $table = $("<table>");
      // 用以保存所有表格内容(包括表头)
      const temp = [];
      data.forEach((item,i) => {
        let trans = h('tr', {}, [
          h('td', {}, item.name),
          h('td', {}, item.age),
          h('td', {}, item.address)
        ])
        temp.push(trans);
      })
      // 将表单所有内容放入到table标签中,作为其子节点,生成virtual node
      const newNode = h('table', {}, [...temp])
      // 利用patch(diff算法)比较新旧虚拟node树的差异,更新dom节点
      patch(curNode, newNode)
      return newNode
    }
    let container = $('#container')[0]
    // 初次渲染,第二个参数传入目标容器的节点
    let curNode = render(userData, container);
    $('#btn-change').on('click', function(){
      // 修改原始数据
      userData[1].name = '李四'
      userData[2].age = 66
      userData[3].address = '江西'
      //重新渲染
      newNode = render(userData, curNode)
      curNode = newNode
    })
  </script>

3 整体页面(可用于测试)

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="container"></div>
  <button id="btn-change">change</button>
</body>
  <script src="../node_modules/jquery/dist/jquery.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.min.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.js"></script>
  <script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>
  <script>
    // snabbom的必要引入和初始化操作
    const snabbdom = window.snabbdom;
    const patch = snabbdom.init({
      snabbdom_class,
      snabbdom_props,
      snabbdom_style,
      snabbdom_eventlisteners
    })
    const h = snabbdom.h
    // 用户数据
    const userData = [{
      name: '张三',
      age: 20,
      address: '北京'
    }, {
      name: '蔡徐坤',
      age: 24,
      address: '广东'
    }, {
      name: '蔡明',
      age: 26,
      address: '河南'
    }];
    userData.unshift({
      name: '姓名',
      age: '年龄',
      address: '地址'
    })
    function render(data, curNode){
      const $table = $("<table>");
      // 用以保存所有表格内容(包括表头)
      const temp = [];
      data.forEach((item,i) => {
        let trans = h('tr', {}, [
          h('td', {}, item.name),
          h('td', {}, item.age),
          h('td', {}, item.address)
        ])
        temp.push(trans);
      })
      // 将表单所有内容放入到table标签中,作为其子节点,生成virtual node
      const newNode = h('table', {}, [...temp])
      // 利用patch(diff算法)比较新旧虚拟node树的差异,更新dom节点
      patch(curNode, newNode)
      return newNode
    }
    let container = $('#container')[0]
    // 初次渲染,第二个参数传入目标容器的节点
    let curNode = render(userData, container);
    $('#btn-change').on('click', function(){
      // 修改原始数据
      userData[1].name = '李四'
      userData[2].age = 66
      userData[3].address = '江西'
      //重新渲染
      newNode = render(userData, curNode)
      curNode = newNode
    })
  </script>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值