uglify打包压缩问题引起的思考

时间:2021-2-20 作者:admin

前言

最近vue项目打包时突然打包失败,报错原因显示element ui中的部分文件有语法错误,打开这些文件仔细研究,发现都是ES6语法,还有提示uglifyjs中的一些错误。

原因

根据提示,大概是因为ES6的语法没有转换成ES5,第一反应是babel-loader,于是找到webpack配置文件(webpack.conf.prod.js),查找babel-loader的配置,添加include,但是也无济于事。

{
   test: /\.js$/,
   loader: 'babel-loader',
   include: [resolve('src'), resolve('test'), resolve('node_modules/resize-detector')]
}

经过反复试验,发现在其他项目中相同的配置是可以打包成功的,这就引起了另外一个思考,他们之间babel配置没有什么不同,只是babel版本不同。基本可以确定是babel版本问题,之前因为一些原因在现在的项目升级了babel7,于是开始查找关于babel的文档。

通过对官方文档的翻阅,终于找到了答案:Babel之前在处理node_modules、symlinks,  monorepos时遇到过问题。为此,Babel团队做了一些修改:Babel将停止查找包。对于monorepos,Babel团队添加了一个新的babels .config.js文件,它将我们的配置集中到所有包中。重点来啦!!!!babel7升级后配置加载逻辑有所变化,简单说来,如下:

  • babel.config.js 是对整个项目(父子package) 都生效的配置,但要注意babel的执行工作目录

  • .babelrc 是对待编译文件生效的配置,子package若想加载.babelrc是需要babel配置babelrcRoots才可以(父package自身的babelrc是默认可用的)

  • 任何package中的babelrc寻找策略是: 只会向上寻找到本包的 package.json 那一级。

也就是说,我们之前配置的babelrc没有生效了,我们想要之前配置的babelrc生效,需要配置babelrcRoots或者有个更方便的方法,就是将之前的babelrc文件名改为babel.config.js,这样就会生效。

扩展

这里再来说说babel7更新后的部分优化:

useBuiltIns

首先我们来看一行简单的代码

arr.includes(1);

includes作为数组的实例方法,在某些浏览器其实是不支持的,babel默认的转换对于这种场景并不会做处理,同样不会处理的包括WeakMap, WeakSet, Promise等es6新引入的类,所以我们需要babel-polyfill为我们这些实例方法等等打上补丁.在很多项目中我们会看到项目的main.js入口顶部require了babel-polyfill包, 或者指定webpack的entry为数组,第一项引入babel-polyfill包,这样的确没问题而且很保险,但是很多场景下我们可能只是使用了少量需要polyfill的api,这个时候全量引入这个包就显得很不划算,babel给我们提供了很好的解决方案,那就是useBuiltIns 这个配置,下面来看实例。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "targets": {
          "chrome": "58",
          "ie": "10"
        }
      },
    ]
  ],
}

输入: src/main.js

arr.includes(1);

输出: dist/main.js

require("core-js/modules/es6.promise");

require("core-js/modules/es7.array.includes");

require("core-js/modules/es6.string.includes");

arr.includes(1);

babel帮我们做好了代码分析,在需要用到polyfill的地方再帮你引入这个单独的补丁,这样就实现了按需引入

@babel/plugin-transform-runtime

这个插件是帮我们把一些babel的辅助方法由直接写入代码专为按需引入模块的方式引用,我们先来看不使用这个插件时候,我们对于es6 class的转换。

输入: src/main.js

class A {}

输出: dist/main.js

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var A = function A() {
  _classCallCheck(this, A);
};

看似没问题,但是如果在很多模块都用了class语法的情况下呢?辅助函数_classCallCheck就会被重复写入多次,占用无意义的空间。解决方法就是引入@babel/plugin-transform-runtime,配置如下:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "targets": {
          "chrome": "58",
          "ie": "10"
        }
      },
    ]
  ],
  "plugins": [
    "@babel/plugin-transform-runtime",
  ]
}

输出: dist/main.js

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var A = function A() {
  (0, _classCallCheck2.default)(this, A);
};

这样就解决了辅助函数重复写入的问题了。

总结一下:babel7的版本下,利用present-env做按需转换,利用useBuiltIn做babel-polyfill的按需引入,利用transform-runtime做babel辅助函数的按需引入。

大家如果有兴趣,可以去官方文档瞅瞅babel

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。