【问题标题】:Webpack's removes classnames when minifying/uglifying ES6 code with inheritanceWebpack 在使用继承缩小/丑化 ES6 代码时删除类名
【发布时间】:2018-04-01 12:54:14
【问题描述】:

当使用继承缩小/丑化 ES6 代码时,Webpack 会删除 类名

MVCE 代码,我们尝试缩小/丑化:

班级孩子

const ParentClass = require('parent');

class Child extends ParentClass{
    constructor(){
        super();
    }
}

module.exports = Child;

index.js 调用Child 类:

const Child = require('./classes_so/child');

let child = new Child();

console.log(child.constructor.name);

Module Parentnode_modules:

class Parent {
    constructor() {
        if (this.constructor.name === 'Parent'){
            throw new TypeError("Parent class is abstract - cant be instance");
        }
    }

}

module.exports = Parent;

整个输出我将发布到问题的末尾,在这里我只想发布我认为会导致错误行为的相关行(原始输出中的第 33-37 行):

n.exports = class extends r {
        constructor() {
            super();
        }
    };

为什么这里缺少类名class extends r?我希望该值会被破坏但会存在,我可以将其视为错误吗?我尝试使用keep_classnames 标志,但它保留了不可接受的原始类名。

我们正在使用

  • Webpack:3.11.0(尝试使用 4,行为相同)
  • uglifyjs-webpack-plugin: 1.2.4(尝试使用不同的插件)
  • NodeJS:v6.9.1 和 v8.9.1(相同的输出)
  • 演示问题的完整项目:webpack-uglify-inheritence

更新 1

我们的webpack.config.js

const webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const path = require('path');
const fs = require('fs');

const nodeModules = {};
const localDependencies = ['.bin'];
fs.readdirSync('node_modules')
    .filter(function (x) {
        return localDependencies.indexOf(x) === -1;
    })
    .forEach(function (mod) {
        nodeModules[mod] = 'commonjs ' + mod;
    });

try {


    module.exports = {
        target: 'node',
        node: {
            console: false,
            global: false,
            process: false,
            Buffer: false,
            __filename: true,
            __dirname: true
        },

        entry: './index_so.js',

        output: {
            path: path.join(__dirname, 'build'),
            filename: 'index.js'
        },

        externals: nodeModules,
        plugins: [
            new webpack.IgnorePlugin(/\.(css|less)$/),
            new webpack.BannerPlugin({
                banner: 'require("source-map-support").install();',
                raw: true,
                entryOnly: false
            })
        ],
        devtool: 'sourcemap',

        module: {
            loaders: [
                {test: /\.json$/, loader: "json-loader"}
            ]
        },

        plugins: [
            new UglifyJsPlugin({
                uglifyOptions: {
                    compress: {
                        warnings: false
                    },
                    keep_classnames: false,
                    mangle: true,
                    output: {
                        beautify: true
                    }
                }
            })
        ]
    };
}
catch (e) {
    console.error(e);
}

上例中的整个缩小/丑化代码:

!function(n) {
    var t = {};
    function e(r) {
        if (t[r]) return t[r].exports;
        var o = t[r] = {
            i: r,
            l: !1,
            exports: {}
        };
        return n[r].call(o.exports, o, o.exports, e), o.l = !0, o.exports;
    }
    e.m = n, e.c = t, e.d = function(n, t, r) {
        e.o(n, t) || Object.defineProperty(n, t, {
            configurable: !1,
            enumerable: !0,
            get: r
        });
    }, e.n = function(n) {
        var t = n && n.__esModule ? function() {
            return n.default;
        } : function() {
            return n;
        };
        return e.d(t, "a", t), t;
    }, e.o = function(n, t) {
        return Object.prototype.hasOwnProperty.call(n, t);
    }, e.p = "", e(e.s = 0);
}([ function(n, t, e) {
    let r = new (e(1))();
    console.log(r.constructor.name);
}, function(n, t, e) {
    const r = e(2);
    n.exports = class extends r {
        constructor() {
            super();
        }
    };
}, function(n, t) {
    n.exports = require("parent");
} ]);

【问题讨论】:

  • n.exports = class extends r { 是正确的语法。运行代码时是否因为缺少 class 名称而出现任何错误?
  • 我的意思是为什么类名被弄错了,但如果它丢失了就不行了?如果你不关心名字,你为什么关心它的存在?
  • 是的,我收到一个错误。我们依赖代码this.constructor.name。如果代码没有被缩小,它会正常工作,否则它会返回一个空字符串(当代码在 NodeJS v6.9.1 中运行时)或在我们的例子中继承它的类的名称,它是 Parent 类,这绝对是错误的。最后一个发生在我们在 NodeJS v8.9.1 上运行压缩代码时
  • 我创建了一个演示问题的简单项目,您可以在这里下载:github.com/anatoly314/webpack-uglify-inheritence
  • 但是,在名称可能被破坏的情况下,您为什么要依赖this.constructor.name === 'Parent'?我没有看到缩小有任何错误,我认为错误在你的代码中,你应该写一些像if (this.constructor == Parent){

标签: javascript node.js webpack uglifyjs uglifyjs2


【解决方案1】:

给定设置中的问题不在 webpack 或 uglify 的代码中,而是在这部分代码中:

class Parent {
  constructor() {
    if (this.constructor.name === 'Parent') {
      throw new TypeError("Parent class is abstract - cant be instance");
    }
  }

}

module.exports = Parent;

this.constructor.name === 'Parent' 中继 class/function 名称,以测试 Parent 是否被直接实例化。

与其传递可能导致各种问题的名称,不如测试构造函数是否等于类。

class Parent {
  constructor() {
    if (this.constructor === Parent) {
      throw new TypeError("Parent class is abstract - cant be instance");
    }
  }

}

module.exports = Parent;

【讨论】:

  • 它在损坏的版本中不起作用。当对象从ParentClient 实例化时,this.constructor 在这两种情况下都返回Parent
  • 响应取决于 NodeJS 的版本,在 v6.9.1 中,它在扩展构造函数上返回一个空的 classname,如果对象从 abstract 对象实例化,则返回 Parent。在 v8.9.1 中,当从继承类或抽象类中实例化对象时,它会返回 Parent
  • @Anatoly 我用6.9.1 和更新的8.9.1 对其进行了测试,如果new Childnew ParentClassthis.constructor === Parent 返回falsetrue。您确定您正确更新并重新更新了您的webpack-parent-class
【解决方案2】:

试试我的库typescript-class-helpers

import { CLASS } from 'typescript-class-helpers/browser';

@CLASS.NAME('Parent')
class Parent {
    constructor() {
        if (CLASS.getNameFromObject(child) === 'Parent'){
            throw new TypeError("Parent class is abstract - cant be instance");
        }
    }

}

@CLASS.NAME('Child')
class Child extends ParentClass{
    constructor(){
        super();
    }
}

let child = new Child();

console.log(CLASS.getNameFromObject(child)); // Child

有了这个,你可以缩小你的类名,一切都会好起来的。

【讨论】:

    猜你喜欢
    • 2017-02-09
    • 1970-01-01
    • 2018-04-13
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多