10月10, 2016

js记录之简单的DOM Parse

记录一下简单的dom parse。

需求是这样的:

   <div>
         <p>111  {{user.name}} 222 {{user.age}} 333</p>
   </div>
var data = {
    user: {
         name: "henryzp",
         age: "18"
    }
}

抛开要做数据变更的双向绑定,抛开replace的思路,我们怎么来做替换呢?

无非是自己做一个简单的compile,下面贴一段从github上拷贝过来的代码:

/**
 * 整体思路: 利用递归的思想
 */

exports._compile = function () {
    this._compileNode(this.$el);
};

/**
 * 渲染节点
 * @param node {Element}
 * @private
 */
exports._compileElement = function (node) {
    if (node.hasChildNodes()) {
        Array.from(node.childNodes).forEach(this._compileNode, this);
    }
};

/**
 * 渲染文本节点
 * @param node {Element}
 * @private
 */
exports._compileTextNode = function (node) {
    let tokens = textParser.parse(node.nodeValue);
    if (!tokens) return;

    tokens.forEach((token) => {
        if (token.tag) {
            // 指令节点
            let value = token.value;
            let el = document.createTextNode("");
            _.before(el, node); //插入一个空文本
            //this._bindDirective("text", value, el);
        } else {
            // 普通文本节点
            let el = document.createTextNode(token.value);
            _.before(el, node);
        }
    });

    _.remove(node); //删除之前node
};

exports._compileNode = function (node) {
    switch (node.nodeType) {
        // text
        case 1:
            this._compileElement(node);
            break;
        // node
        case 3 :
            //如果是空白节点则不做处理
            if(/^\s+$/.test(node.nodeValue)){
               return;
            }
            this._compileTextNode(node);
            break;
        default:
            return;
    }
};

上面代码的核心是_compileTextNode。其中textParser.parse的代码如下:

exports.parse = function (text) {
    let tokens = [];
    let tagRE = /\{?\{\{(.+?)\}\}\}?/g;
    let match, index, value, lastIndex = 0;
    while (match = tagRE.exec(text)) {
        index = match.index;
        if (index > lastIndex) {
            tokens.push({
                value: text.slice(lastIndex, index)
            });
        }
        value = match[1];
        tokens.push({
            tag: true,
            value: value.trim()
        });
        lastIndex = index + match[0].length;
    }

    if (lastIndex < text.length - 1) {
        tokens.push({
            value: text.slice(lastIndex)
        });
    }

    return tokens;
};

在传入字符串“111 {{user.name}} 222 {{user.age}} 333”后,通过parse得到:

[{"value":"111 "},{"tag":true,"value":"user.name"},{"value":" 222 "},{"tag":true,"value":"user.age"},{"value":" 333"}]

如果tag为true的,表示是指令。

然后创建一个空的文本Node,在绑定中,只要将这个对象的nodeValue给修改即可。

小坑

var data = {
     a: {
           b: 1
     }
}

我们要得到b属性,通常是这样写的:

data.a.b

那么当"a.b"时,可能会下意识地以为,可以这样写:

data["a.b"] //undefined

当然了,你可以通过eval的方式,最终写成这样的:

var result = "data" + "." + "a.b"; //a.b通常为一个传入的字符串
eval(result);

但其实这种方式还是挺lower的,我们可以写成这样的:

let properties = "a.b".split(".");
properties.forEach((property) => {
    data = data[property];
});

用递归的方式来搞定。这也是我在翻别人的源码时,学到的一招。

本文链接:www.my-fe.pub/post/simple-dom-parse.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。