改进JS重复数据删除函数的性能

Improving performance of JS dedupe function

本文关键字:函数 性能 删除 数据 JS 改进      更新时间:2023-09-26

功能问题来自postcss-discard-duplicate插件。下面是index.js中的代码:

'use strict';
var postcss = require('postcss');
function dedupe (node) {
    if (node.nodes) { node.each(dedupe); }
    if (node.type === 'comment') { return; }
    var nodes = node.parent.nodes.filter(function (n) {
        return String(n) === String(node);
    });
    nodes.forEach(function (n, i) {
        if (i !== nodes.length - 1) {
            n.removeSelf();
        }
    });
}
module.exports = postcss.plugin('postcss-discard-duplicates', function () {
    return function (css) {
        css.each(dedupe);
    };
});

Plugin使用PostCSS API返回CSS节点树。这里有完整的API文档。

目前我有一个很大的CSS文件建立在Twitter Bootstrap上,有很多复杂的选择器。CSS需要~37秒来编译,如果使用这个函数来查找和删除任何重复的规则。没有它,它是~3秒。

我正在寻找帮助优化这个函数以获得更好的性能。

UPDATE:我张贴了复制函数作者所做改进的答案。

我不确定在这个函数中直接找到优化是否容易,似乎这是通过构建二叉树等帮助结构进行专业优化的任务。这不是一个简单的问题,算法上的问题,而不是实现上的问题。如果你只想优化代码——试着合并filterforEach。至少,它将有助于避免重复迭代。

var last = null;
var nodes = node.parent.nodes.forEach(function(n){
    if (String(n) === String(node)) {
        if (last) {
            last.removeSelf();
        }
        last = n;
    }
});

我向回购所有者报告了这个问题,他做出了一些显著的性能改进。我的编译时间现在是~13.5秒(通过添加微优化提到的问题评论,我也设法减少了额外的秒)

改进后的代码:

'use strict';
var postcss = require('postcss');
function dedupe (node, index) {
    if (node.type === 'comment') { return; }
    if (node.nodes) { node.each(dedupe); }
    var toString = node.toString();
    var nodes = node.parent.nodes;
    var result = [node];
    var i = index + 1;
    var max = nodes.length;
    for (; i < max; i++) {
        if (nodes[i].toString() === toString) {
            result.push(nodes[i]);
        }
    }
    for (var x = 0; x < result.length; x++) {
        if (x !== result.length - 1) {
            result[x].remove();
        }
    }
}
module.exports = postcss.plugin('postcss-discard-duplicates', function () {
    return function (css) {
        css.each(dedupe);
    };
});