vue模板实现4-高效更新

  • 这次的改动大一些,目标是希望在数据更改时不必重新遍历 dom,只更新对应的文本。
  • 思路是首先遍历 dom,将所有文本元素 ( nodeName === '#text' ) 和对应的数据绑定起来,即根据数据可以直接对应到 dom 节点。
  • 接下来的工作就是监控数据变化了,数据变化时需要知道被改的数据是哪个,但 defineProperty 不能做到,因为它的 set 中参数只有新的 value。所以使用了proxy,proxy 可以直接对整个 json 进行代理,使用起来更加方便。
  • 总的流程:
    先用 proxy 对 data 代理,在 set 中设置 update( ),数据更新时,直接更新对应文本节点
    遍历 dom,将数据和对应文本节点绑定起来
    遍历 data,将所有的 {{ }} 替换
class Vue {                                            // Vue 类
  constructor(obj) {
    let {el, data} = obj
    const node = document.querySelector(el)
    const dataBindNode = {}

    data = new Proxy(data, {
      get(tar, key){
        return tar[key]
      },
      set(tar, key, val){
        tar[key] = val
        updateText(dataBindNode[key], data)
      }
    })

    this.data = data

    traverseNode(node, data, dataBindNode)

    for(let key in dataBindNode) {          // 第一次更新
      updateText(dataBindNode[key], data)
    }
  }
}

const updateText = (node, data) => {                    // 更新数据对应的文本节点
  const reg = /{{\w+}}/g
  const arr = node.text.match(reg) || []
  let text = node.text

  arr.forEach((item)=>{
    const name = item.slice(2, -2)

    if(data[name]) {
      node.node.textContent = text = text.replace(item, data[name])
    }
  })
}
const traverseNode = (node, data, dataBindNode) => {      // 遍历
  node.childNodes.forEach(item => {
    if(item.nodeName === '#text') {
      bindNode(item, data, dataBindNode)
    }
    else {
      traverseNode(item, data, dataBindNode)
    }
  });
}

const bindNode = (node, data, dataBindNode) => {          // 绑定
  const reg = /{{\w+}}/g
  const arr = node.textContent.match(reg) || []

  arr.forEach((item)=>{
    const key = item.slice(2, -2)
    if(data[key]) {
      dataBindNode[key] = {}
      dataBindNode[key].node = node
      dataBindNode[key].text = node.textContent
    }
    else { }  // 数据未定义却使用
  })
}

你可能感兴趣的:(vue模板实现4-高效更新)