最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • webpack优化生成代码(2)

    正文概述 掘金(0²)   2021-01-10   565

    优化 babel-loader

    babel-loader始终是项目处理任务最多的 loader,尤其是 React 开发过程中,有大量的 JSX 需要去解析,编译。从babel-loader的配置项入手可以进行一些优化。babel-loader使用的插件集合主要是@babel/preset-env@babel/preset-react

    @babel/preset-env

    @babel/preset-env是一个负责将 JS 代码编译成兼容性更强的低版本 JS 代码的插件集合。

    配置项

    配置项类型默认值含义
    targetsString|Array|Object{}配置浏览器的版本或者 nodejs 的版本bugfixesBooleanfalse取决于targets设定,根据targets来编译目标浏览器支持的最新版本的语法;babel 8 版本后会自动启用specBooleanfalse启用更多规范,会导致编译速度减慢looseBooleanfalse宽松的编译规则,正常情况下,编译会尽可能遵循 ECMAScript 6 的语义,但是宽松模式会不严格,看起来像是手写的代码,见——Babel 6: loose modemodules"amd"|"umd"|"systemjs"|"commonjs"|"cjs"|"auto"|false"auto"将 ES Module 转换为其他类型模块语法的规则debugBooleanfalse将 preset 使用的 plugin 和 polyfill 输出到控制台,这个配置不会受到 webpack 的stats的影响includeArray[]自定义采用的插件名称excludeArray[]排除使用的插件useBuiltIns"usage"|"entry"|falsefalse定义如何处理 polyfillcorejs23{version: 2|3,proposals: boolean}2仅在useBuiltIns: usage或者useBuiltIns: entry的时候才生效,定义core-js的版本,version必须是数字 2 或者 3forceAllTransformsBooleanfalse禁用所有编译configPathStringNode.js 进程的当前工作目录配置指定了browserslist的目录ignoreBrowserslistConfigBooleanfalse是否忽略browserslist配置browserslistEnvObjectundefined配置不同开发环境下的browserslistshippedProposalsBooleanfalse是否启用浏览器已经支持的提案

    配置 modules

    modules配置项默认会根据 babel 内部的caller判断浏览器是否支持 ES Modules 的一些特性,例如是否支持静态import或者动态import()等语法;从而选择进一步将 ES Module 转换为 CommonJS 的那种require语法。

    由于 webpack 内置的 tree shaking 功能依赖于 ES Modules 的静态语法分析,但是目前modules的支持程度也不是太高,IE 11 是完全不支持的,所以看情况选择是否将modules禁用。

    配置 targets

    早期的 babel 还有一个 preset,叫babel-preset-latest,这个 preset 的特点是能自动根据 ES 规范的新特性添加对应的 plugin,这样就避免了开发者去单独配置,但是就这样直接添加进去会导致插件越来越多,并且随着时间推移,大部分 ES6 的语法已经被主流浏览器都支持了,没必要再去使用那些插件编译了,这些无用的插件留在 preset 中会导致 babel 的编译流程也越来越慢,于是推荐使用@babel/preset-env去替代这个 preset。

    @babel/preset-env帮助开发者从preset-latest进行过渡,如果不在@babel/preset-env中配置targets,那么它就会默认把 ES6+的 JS 代码全部编译成 ES5 的形式。通过开启@babel/preset-envdebug配置项可以清楚的在控制台看到@babel/preset-env使用的 plugin 有多少,如果不设定targets,那么就会像下图这样引入最高兼容到 IE10 的 plugin。

    webpack优化生成代码(2)

    在开发环境下,这些 plugin 基本都是不需要的,而@babel/preset-env并不会去查找browserslist的配置,即使是browserslist的默认配置,也必须在targets中配置,这个设定可能在 Babel 8 重新讨论。

    {
      "presets": [["@babel/preset-env", { "targets": "defaults" }]]
    }
    

    targets字段同样支持上述的browserslist配置形式,在开发环境始终使用最新的 Chrome 版本,这样开发环境引入的 plugin 就减少很多了。

    {
      targets: isDevelopment
    		? "last 1 chrome version"
    		: "> 1%, last 2 versions, Firefox ESR, ie >= 11, not dead",
    }
    

    webpack优化生成代码(2)

    targets还支持一些特殊字段的配置:

    • esmodules:默认是false,指定目标浏览器支持 ES Modules 语法,如果设置成true,那么browserslist的配置会失效
    • node"current" | true,指定针对当前 node 的版本进行编译
    • safari"tp",指定针对 safari 的技术预览版进行编译
    • browsers:指定一个browserslist规则数组,不建议使用,因为未来版本可能会删除

    配置 bugfixes

    默认情况下,@babel/preset-env或者其他的 Babel plugin 会对 ES 语法特性进行相关分组,例如function arguments包含默认参数,剩余参数等内容,如果开启bugfixes@babel/preset-env会根据targets设定的兼容范围,选择将不同的分组编译到目标浏览器支持的最接近的最新现代语法,这将导致已编译应用程序的大小显着减小,不仅优化 webpack 的构建速度,而且优化了生成的代码。

    配置 polyfill

    core-js 简介

    @babel/polyfill已经在 7.4 版本以后被弃用了,目前主流的 polyfill 方案是使用core-js

    core-js本身具有三个版本:

    • yarn add core-js@3.6.5:全局注入版本,使用的时候只需要在代码入口点全局引入core-js即可
    • yarn add core-js-pure@3.6.5:模块导入版本,需要在使用的时候单独引入对应的 polyfill 模块文件
    • core-js-bundle:使用 script 注入版本

    此外每个版本还具有包含不同 feature 的模块:

    • core-js(-pure)/eses表示稳定的 ES 规范内容
    • core-js(-pure)/stablestable同时包含稳定的 whatwg 的规范内容以及 ES 规范内容
    • core-js(-pure)/featuresfeatures只包含单独语法的特定模块内容,例如core-js(-pure)/features/set表示只包含Set数据集合的相关 polyfill

    如果使用全局注入版本,可以只在项目入口文件全局引入,全局引入会自动根据目标环境将 polyfill 引入,不会按需引入。也可以使用features只注入需要的 polyfill。

    //全局注入
    import 'core-js';
    
    Array.from(new Set([1, 2, 3, 2, 1]));
    [1, [2, 3], [4, [5]]].flat(2);
    Promise.resolve(32).then(x => console.log(x));
    
    //通过feature引入需要的模块
    import 'core-js/features/array/from';
    import 'core-js/features/array/flat';
    import 'core-js/features/set';
    import 'core-js/features/promise';
    
    Array.from(new Set([1, 2, 3, 2, 1]));
    [1, [2, 3], [4, [5]]].flat(2);
    Promise.resolve(32).then(x => console.log(x));
    

    如果使用pure版本,无法使用import 'core-js-pure';这种全局注入的方式,需要根据需要导入特定的模块;pure版本可以做到按需引入,但是使用很麻烦, 比如需要用到SetArray.from两个 feature,需要去找这两个 feature 怎么去引入。

    import from from 'core-js-pure/features/array/from';
    import flat from 'core-js-pure/features/array/flat';
    import Set from 'core-js-pure/features/set';
    import Promise from 'core-js-pure/features/promise';
    
    from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
    flat([1, [2, 3], [4, [5]]], 2); // => [1, 2, 3, 4, 5]
    Promise.resolve(32).then(x => console.log(x));
    

    useBuiltIns 和 corejs

    对于全局注入的版本,例如安装core-js@3版本以后,需要首先设置@babel/preset-envcorejs配置项,并且建议指定较小的core-js版本号,例如使用3.6而不是3,因为对于corejs:3,将不会添加在较小的core-js版本中添加的模块。默认情况下,只会采用稳定的 ES feature,对于尚在提案状态的规范内容不会引入 polyfill,不过可以通过corejs.proposals开启对提案内容的支持。

    然后必须指定useBuiltIns,如果不指定不会有任何 polyfill 被添加进来。

    webpack优化生成代码(2)

    core-js的全局注入版本为例,指定corejs: "3.6"useBuiltIns: "entry",配置如下:

    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env',
                {
                  debug: true, // 开启debug模式
                  targets: 'ie >= 8',
                  bugfixes: true,
                  modules: false,
                  corejs: {
                    version: 3,
                    proposals: true,
                  },
                  useBuiltIns: 'entry',
                },
              ],
            },
          },
        },
      ];
    }
    

    当指定useBuiltIns:"entry"时,会把core-js所有的针对目标targets的 polyfill 都打包进来,那简直是噩梦!当我只在入口使用了SetArray.from两个 feature 的时候,打包了 1 分钟 生成了一个150KB的 polyfill chunk

    import 'core-js';
    
    console.log(Array.from(new Set([1, 2, 3, 2, 1])));
    

    当指定useBuiltIns:"usage"时,只会根据代码引入需要的 polyfill,相应的打包时间和 chunk 体积就减小很多了,大概只有20KB

    webpack优化生成代码(2)

    因为直到 Babel 7.3 版本, useBuiltIns: usage还不够稳定,有时候一些需要的 polyfill 并不会自动添加进来,所以可能旧的项目使用会有一点问题。作为兼容性和按需引入更好的选择,可以使用下文core-js-pure@babel/plugin-transform-runtime结合的方案。

    @babel/preset-react

    @babel/preset-react preset 负责转换 JSX 等语法。

    配置项

    配置项类型默认值含义
    runtime"classic"或"automatic""classic"是否自动导入 JSX 转换后的函数,默认是不会developmentBooleanfalse是否开启开发环境,针对开发环境会启用辅助开发的插件,例如:@babel/plugin-transform-react-jsx-self@babel/plugin-transform-react-jsx-sourcethrowIfNamespaceBooleanfalse是否在使用 XML 命名空间的标记名是抛出错误;例如<f:image />形式,虽然 JSX 规范允许这样做,但是默认情况下是被禁止的,因为 React 的 JSX 目前并不支持这种方式importSourceStringreact设置函数导入来源的名称pragmaStringReact.createElement替换编译 JSX 表达式时使用的函数pragmaFragStringReact.Fragment设置 JSX fragments 语法转换后的函数useBuiltInsBooleanfalse是否使用原生内置的 polyfill,而不是通过其他插件进行 polyfilluseSpreadBooleanfalse当展开props的时候,使用 inline object 形式,而不是使用 Babel 拓展或者Object.assign复制对象

    inline object 形式:

    {
      (firstName = 'john'), (lastName = 'walter');
    }
    

    针对不同环境配置@babel/preset-react,以在开发环境使用辅助开发的插件

    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env',
                [
                  '@babel/preset-react',
                  {
                    development: isDevelopment, //开发环境
                    useBuiltIns: true,
                  },
                ],
              ],
            },
          },
        },
      ];
    }
    

    @babel/plugin-transform-runtime

    Babel 会使用一些非常小的辅助性的代码插入到需要编译的源代码中,有时候这些代码是重复的,会增加代码体积。通过@babel/plugin-transform-runtime这个 plugin 可以禁用 Babel 自动对每个文件的 runtime 注入;然后通过安装@babel/runtime将 Babel 的辅助代码作为一个独立的依赖模块来引入,这样就可以避免编译后的代码中重复出现辅助性的代码,减小代码体积。

    yarn add @babel/plugin-transform-runtime @babel/runtime -D
    
    module.exports = {
      module: {
        rules: [
          {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
              plugins: ['@babel/plugin-transform-runtime'],
            },
          },
        ],
      },
    };
    

    @babel/plugin-transform-runtime会进行以下三项任务:

    • 当项目中使用生成器或者异步async函数的时候,自动引入@babel/plugin-transform-runtime
    • 使用core-js polyfill,可以通过corejs配置
    • 默认移除 Babel 的辅助代码,使用@babel/runtime/helpers代替,也就是上文说的减少代码体积的优化,可以通过helpers选择是否开启

    配置项

    配置项类型默认值含义
    corejsfalse
    2,3
    {version:2或3,proposals:boolean}
    false指定core-js库的版本helpersBooleantrue是否将 Babel 插入的辅助代码切换为模块调用regeneratorBooleantrue是否将生成器函数转化为生不会污染全局范围的运行时生成器useESModulesBooleanfalse是否使用辅助代码转换不通过@babel/plugin-transform-modules-commonjs的代码absoluteRuntimeBooleanStringfalseversionStringfalse指定@babel/runtime-corejs的版本

    polyfill

    这个插件的另一个用途就是结合pure版本的core-js-pure库来做 polyfill,简化core-js-pure的导入语法,例如原本需要使用模块导入的语法,会自动进行转换。

    // 本来的core-js-pure用法
    import from from 'core-js-pure/stable/array/from';
    import flat from 'core-js-pure/stable/array/flat';
    import Set from 'core-js-pure/stable/set';
    import Promise from 'core-js-pure/stable/promise';
    
    from(new Set([1, 2, 3, 2, 1]));
    flat([1, [2, 3], [4, [5]]], 2);
    Promise.resolve(32).then(x => console.log(x));
    
    // 引入@babel/plugin-transform-runtime的简化写法
    Array.from(new Set([1, 2, 3, 2, 1]));
    [1, [2, 3], [4, [5]]].flat(2);
    Promise.resolve(32).then(x => console.log(x));
    

    使用@babel/plugin-transform-runtime结合core-js-pure需要安装对应的@babel/runtime

    corejs对应安装的库
    falseyarn add @babel/runtime2yarn add @babel/runtime-corejs23yarn add @babel/runtime-corejs3

    默认情况下,@babel/plugin-transform-runtime只会采用稳定的 whatwg 规范内容或者 ES 规范内容,但是可以通过corejs.proposals配置项指定其采用尚未稳定的提案。

    module.exports = {
      module: {
        rules: [
          {
            test: /\.m?js$/,
            exclude: /(node_modules)/,
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
              plugins: [
                [
                  '@babel/plugin-transform-runtime',
                  {
                    corejs: {
                      version: 3,
                      proposals: true,
                    },
                  },
                ],
              ],
            },
          },
        ],
      },
    };
    

    起源地下载网 » webpack优化生成代码(2)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元