zoukankan      html  css  js  c++  java
  • 实现一个最小版本vue(三)之compiler

    compiler

    功能

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

    实现思路

    1. 根据图示,这个类有两个属性以及一些操作属性的方法
    2. compiler类的构造函数,传入vue的实例,定义el和vm两个属性,分别记住模板和vue实例,紧接着调用this.compile编译模板,这里调用这个方法的目的是,当调用new Compile()时,会立即开始编译模板
    3. compile方法里,处理所有文本节点的差值表达式和元素节点的指令
      1. 首先编译el里的所有子节点
      2. 判断是文本节点还是元素节点,调用相对应的处理方法
      3. 这里仅仅处理了el下第一层子节点,还要判断子节点下有没有子节点,进行递归调用
    4. compileText方法,编译文本节点,处理差值表达式
      1. 定义正则,匹配类似{{ msg }}这种差值表达式
      2. 获取文本节点里内容
      3. 如果内容匹配到正则,匹配到的值,就是vue实例中,data属性的key
      4. 有了这个key,就可以通过this.vm[key]获取对应的值,最后替换到文本节点里
    5. compileElement方法,编译元素节点,处理指令
      1. 遍历所有节点的属性
      2. 获取节点属性的名称
      3. 通过isDirective()方法判断当前属性是否是指令(v-开头)
      4. 截取掉v-,只保留功能单词。v-text -> text
      5. 获取节点属性的value值,这个value也是vue实例data属性里的key
      6. 调用update处理指令相关功能
    6. update
      1. 需要3个参数,节点、key(vue实例data属性里的key)、指令去掉v-后的内容
      2. 通过字符串拼接,获取指令对应的方法,并执行
      3. 这样做的好处是:以后即使有新的指令需要处理,只需新定义处理指令的方法(xxxUpdater)

    代码

    class Compiler {
      constructor (vm) {
        this.el = vm.$el
        this.vm = vm
        this.compile(this.el)
      }
    
      // 编译模板,处理文本节点和元素节点
      compile (el) {
        // children子元素,要找子节点childNodes
        let childNodes = el.childNodes
        // 转化伪数组
        Array.from(childNodes).forEach(node => {
          // 处理文本节点
          if (this.isTextNode(node)) {
            this.compileText(node)
          }
          // 处理元素节点
          if (this.isElementNode(node)) {
            this.compileElement(node)
          }
          // node节点是否有子节点,如果有,递归调用compile
          if (node.childNodes && node.childNodes.length) {
            this.compile(node)
          }
        })
      }
    
      update (node, key, attrName) {
        let updateFn = this[attrName + 'Updater']
        updateFn && updateFn.call(this, node, this.vm[key], key)
      }
    
      // 处理v-text指令
      textUpdater (node, value, key) {
        node.textContent = value
        new Watcher(this.vm, key, newValue => {
          node.textContent = newValue
        })
      }
    
      // 处理v-model
      modelUpdater (node, value, key) {
        node.value = value
        new Watcher(this.vm, key, newValue => {
          node.value = newValue
        })
        // 给表单元素注册事件,实现双向绑定
        // node就是input元素
        // 触发input事件后,事件处理函数会把文本框的值取出,并重新赋值vm[key] = 'xxx'
        // 给vm[key]赋值的时候,会触发响应式机制
        // 响应式机制是,当数据发生变化,会重新更新视图
        node.addEventListener('input', () => {
          this.vm[key] = node.value
        })
      }
    
      // 编译元素节点,处理指令
      compileElement (node) {
        // 获取所有属性节点
        // 遍历所有属性节点
        Array.from(node.attributes).forEach(attr => {
          // 判断是否是指令
          let attrName = attr.name
          if (this.isDirective(attrName)) {
            // v-text -> text
            attrName = attrName.substr(2)
            let key = attr.value
            this.update(node, key, attrName)
          }
        })
      }
    
      // 编译文本节点,处理差值表达式
      compileText (node) {
        // console.dir(node)
        let reg = /{{(.+?)}}/
        // 获取文本节点内容
        let value = node.textContent
        if (reg.test(value)) {
          let key = RegExp.$1.trim()
          node.textContent = value.replace(reg, this.vm[key])
          // 创建watcher对象,当数据改变更新视图
          new Watcher(this.vm, key, newValue => {
            node.textContent = newValue
          })
        }
      }
    
      // 判断元素属性是否是指令
      isDirective (attrName) {
        // 判断是否v-开头即可
        return attrName.startsWith('v-')
      }
    
      // 判断节点是否是文本节点
      isTextNode (node) {
        // nodeType 3=文本节点 1=元素节点
        return node.nodeType === 3
      }
    
      // 判断节点是否是元素节点
      isElementNode (node) {
        return node.nodeType === 1
      }
    }
    
  • 相关阅读:
    SQL Server 2008R2中取得详细日期到毫秒级
    SQL Server Analysis Services无法启动
    SQL Server 2008R2编写脚本时智能提示功能丢失的处理
    SQL Server使用Linkserver连接Oracle
    好用的eclipse快捷键
    今天项目出现的问题总结
    8.2微信小页面问题总结
    SpringBoot整合rabbitmq
    初学Netty(杰哥好久不见)
    消息队列RabbitMQ学习
  • 原文地址:https://www.cnblogs.com/Evo1uti0n/p/13257646.html
Copyright © 2011-2022 走看看