mini-vue实现之compiler类

276 阅读1分钟

mini-vue代码已放到github上。

compiler类功能

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

实现compiler类

下面我们将这个类的基本方法实现

class Compiler {
    constructor(vm) {
        this.el = vm.$el;
        this.vm = vm;
        this.complie(this.el);
    }
    
    // 编译模版,处理文本节点以及元素节点
    compile(el) {
    
    }
    // 编译文本节点以及差值表达式
    compileText(node) {
    
    }
    
    // 编译元素节点,处理指令
    compileNodeElement() {
    
    }
    
    // 判断是否为指令
    isDirective(attrName) {
        return attrName && attrName.startsWith('v-');
    }
    
    // 判断是否为文本节点
    isTextNode(node) {
        return node.nodeType === 3
    }
    
    isElementNode(node) {
        return node.nodeType === 1
    }
}

compile方法实现

首先我们将el下面的childNodes拿到,之后循环处理childNodes,通过node的类型来判断,如果是text类型则调用compileText方法,如果是元素类型则调用compileNodeElement方法.

compiler(el) {
    let childNodes = el.childNodes;
    // 此时childNodes是一个类数组,我们将他转化成数组,然后进行遍历。
    Array.from(childNodes).forEach(node => {
        if(this.isTextNode(node)) {
            this.compileText(node);
        } else if(this.isElementNode(node)) {
            this.compileNodeElement(node)
        }
        // 判断当前节点是否有子节点,如果存在子节点则递归调用compiler方法进行处理
        if(node.childNodes && node.childNodes.length > 0) {
            this.compiler(node)
        }
    })
}

compileText方法实现

compileText方法的主要功能是处理处理差值表达式,通过正则判断,如果是差值表达试,则将真正对应的值赋值到当钱节点上。

compileText(node) {
    // 通过textContent属性
    let value = node.textContent;
    let reg = /\{\{(.+?)\}\}/;
    
    if(reg.test(value)) {
        let key = RegExp.$1.trim();
        node.textContent = value.replace(reg, this.vm[key]);
    }
}

compileNodeElement方法实现

compileNodeElement方法的主要功能是处理指令,通过获取指令,进行不同的指令操作。

compileNodeElement(node) {
    let attributes = node.attributes;
    Array.from(attributes).forEach((attr) => {
        let attrName = attr.name;
        
        if(this.isDirecive(attrName)) {
            // 处理指令的v-。例如v-text 得出text
            attrName = attrName.substr(2);
            // 通过attr.value获取attr的值,例如v-text='msg' 则获取msg,之后用来替换成真正的值
            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;
}
// 处理v-model指令
modelUpdater(node, value) {
    node.value = value;
}

这里我们只处理了v-model以及v-text,如果之后还有其他指令,则只需添加方法来处理相应的指令。