Complier类

169 阅读3分钟

前言

接下来 我们进入到Complier类的实现

Compiler

  • 功能

    • 负责编译模板,解析指令/差值表达式
    • 负责页面的首次渲染
    • 当数据变化后重新渲染视图
  • 结构

    image.png

判断元素属性是否指令

这里判断很简单我们可以会议一下 vue的指令就是 v-xxx开头,所以我们可以这样使用

 // 判断元素属性是否指令
  isDirective(attrName) {
    // 这里判断很简单我们可以会议一下 vue的指令就是 v-xxx开头,所以我们可以这样使用
    return attrName.startsWith('v-')
  }

接着判断节点是否是文本节点、元素节点

该如何进行判断呢?

nodeType 属性返回节点类型。

  • 如果节点是一个元素节点,nodeType 属性返回 1。
  • 如果节点是属性节点, nodeType 属性返回 2。
  • 如果节点是一个文本节点,nodeType 属性返回 3。
  • 如果节点是一个注释节点,nodeType 属性返回 8。
  • 该属性是只读的。 那这样就很简单了

判断节点是否是文本节点

isTextNode(node) {
    return node.nodeType === 3
  }

判断节点是否是元素节点

isElemenetNode(node) {
    // nodeType === 1 就是元素节点
    return node.nodeType === 1
  }

complier 方法

有了上面的几个基础方法打底,我们可以开始complier方法的编译了

  1. 我们先获取 el 下的所有节点
let childNodes = el.childNodes
  1. 需要遍历 el 下的所有节点
 Array.from(childNodes).forEach
  1. 判断节点的类型
    • 如果是文本节点我们需要调用 文本节点的编译方法,处理差值表达式

      if (this.isTextNode(node)) {
          // 如果是文本节点我们需要调用 文本节点的编译方法,处理差值表达式
          this.complierText(node)
      }
      
    • 如果是元素节点 我们需要调用元素节点的编译方法,处理元素节点

      else if (this.isElemenetNode(node)) {
          // 如果是元素节点 我们需要调用元素节点的编译方法,处理元素节点
          this.complierElement(node)
      }
      
  2. 判断node节点是否有子节点,如果有子节点,要递归调用 complier
if(node.childNodes && node.childNodes.length){
    this.complier(node)
}

完整代码

// 编译模板,处理文本节点和元素节点
  complier(el) {
    // 我们先获取 el 下的所有节点
    let childNodes = el.childNodes
    // 我们需要遍历 el 下的所有节点
    Array.from(childNodes).forEach(node => {
      // 接下来我们需要判断节点的类型
      if (this.isTextNode(node)) {
        // 如果是文本节点我们需要调用 文本节点的编译方法,处理差值表达式
        this.complierText(node)
      } else if (this.isElemenetNode(node)) {
        // 如果是元素节点 我们需要调用元素节点的编译方法,处理元素节点
        this.complierElement(node)
      }
      // 判断node节点是否有子节点,如果有子节点,要递归调用 complier
      if(node.childNodes && node.childNodes.length){
        this.complier(node)
      }
    })
  }

complierText 实现

  1. 首先先用正则表达式获取插值表达式中的值
    1. {{ }} 先解析花括号,利用/\{\{ \}\}/ 正则先解析花括号
    2. 获取其中的值,/\{\{(.+?)\}\}/
 // 编译文本节点,处理差值表达式
  complierText(node) {
    // 正则表达式 解析插值表达式 
    let reg = /\{\{(.+?)\}\}/
    // 文本节点的内容
    let value = node.textContent
    if (reg.test(value)) {
      const key = RegExp.$1.trim()
      node.textContent = value.replace(reg, this.vm[key])
    }
  }

编译元素节点,处理指令

// 编译元素节点,处理指令
  complierElement(node) {
    // 遍历所有的属性节点
    Array.from(node.attributes).forEach(attr => {
      // 判断是否是指令
      let attrName = attr.name
      if (this.isDirective(attrName)) {
        // 对指令做处理
        attrName = attrName.substr(2)
        let key = attr.value
        this.update(node, key, attrName)
      }
    })

  }
  update(node, key, attrName) {
    let updateFn = this[`${attrName}Updater`]
    updateFn && updateFn(node, this.vm[key])
  }
  // 处理 v-text 指令
  textUpdater(node, value) {
    node.textContent = value
  }
  modelUpdater(node, value) {
    node.value = value
  }