Vue 模拟源码实现响应式原理——实现compiler.js(第3部分)

34 篇文章 0 订阅

实现Vue响应式原理 之 compiler.js部分

结构介绍

Vue 整体结构

Compiler功能

1.负责编译模板,解析指令/插值表达式
2.负责页面的首次渲染
3.当数据变化后重新渲染视图

Compiler结构

在这里插入图片描述

代码实现

// 负责解析指令/插值表达式
class Compiler {
    constructor(vm) {
        this.vm = vm
        this.el = vm.$el
        // 立即编译模板
        this.compile(this.el)
    }
    // 编译模板,处理文本节点和元素节点
    compile(el) {
        const nodes = el.childNodes
        Array.from(nodes).forEach(node => {
            // 判断是文本节点还是元素节点
            if (this.isTextNode(node)) {
                // 处理文本节点
                this.compileText(node)
            } else if (this.isElementNode(node)) {
                // 处理元素节点
                this.compileElement(node)
            }
            // 判断node节点,是否有子节点,如果有子节点,要递归调用compile
            if (node.childNodes && node.childNodes.length) {
                // 当前节点中还有子节点,递归编译
                this.compile(node)
            }
        })
    }
    // 判断节点是否是文本节点
    isTextNode(node) {
        return node.nodeType === 3
    }
    // 判断节点是否是元素(属性)节点
    isElementNode(node) {
        return node.nodeType === 1
    }
    // 判断元素属性是否是指令 即,是否是以 v- 开头的指令
    isDirective(attrName) {
        return attrName.startsWith('v-')
    }
    // 编译文本节点,处理差值表达式
    compileText(node) {
        // 1. 匹配差值表达式 {{ msg }} 2. 要不花括号中变量的名字提取出来,后续要获取到这个变量的值
        const reg = /\{\{(.+?)\}\}/ //.+后面的?号 表示非贪婪模式,尽可能早的结束匹配
        // 获取文本节点的内容
        const value = node.textContent
        if (reg.test(value)) {
            // 插值表达式中的值就是我们要的属性名称
            const key = RegExp.$1.trim() // RegExp是正则的构造函数,RegExp.$1表示获取正则表达式中的第一个分组就是小括号中的内容
            // 把插值表达式替换成具体的值
            node.textContent = value.replace(reg, this.vm[key])
        }
    }
    // 编译元素节点,处理指令
    compileElement(node) {
        // 遍历元素节点中的所有属性,找到指令
        Array.from(node.attributes).forEach(attr => {
            // 获取元素属性的名称
            let attrName = attr.name
            // 判断当前的属性名称是否是指令
            if (this.isDirective(attrName)) {
                // attrName 的形式 v-text v-model
                // 截取属性的名称,获取 text model
                attrName = attrName.substr(2)
                // 获取属性的名称,属性的名称就是我们数据对象的属性 v-text="name",获取的是name
                const key = attr.value
                // 处理不同的指令
                this.update(node, key, attrName)
            }
        })
    }
    // 负责更新 DOM
    // 创建 Watcher
    update(node, key, attrName) {
        // node 节点,key 数据的属性名称,attrName 指令的后半部分
        const updaterFn = this[attrName + 'Updater']
        updaterFn && updaterFn(node, this.vm[key])
    }
    // v-text 指令的更新方法 处理v-text指令把值取出来赋给对应DOM元素
    textUpdater(node, value) {
        node.textContent = value
    }
    // v-model 指令的更新方法
    modelUpdater(node, value) {
        node.value = value
    }
}

梳理一下文件的结构

  • html文件结构
    在这里插入图片描述
  • node节点结构
    在这里插入图片描述
  • 节点属性结构
    在这里插入图片描述

RegExp

上面代码实现中使用到的RegExp,是正则的构造函数,RegExp.$1表示获取正则表达式中的第一个分组就是小括号中的内容 => 用于保存最近一次exec或test产生的匹配结果中,括号内的内容。详细可网上查阅了解。

总的梳理一下流程

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值