babel-polyfill和@babel/plugin-transform-runtime

目前部分浏览器和 Node.Js 已经支持 ES6,但是对 ES6 所有的标准支持不全,这导致在开发中不敢全面地使用 ES6。

通常我们需要把采用 ES6 编写的代码转换成目前已经支持良好的 ES5 代码。

  1. 把新的 ES6 语法用 ES5 实现,例如 ES6 的 class 语法用 ES5 的 prototype 实现。
  2. 给新的 API 注入 polyfill ,例如项目使用 fetch API 时,只有注入对应的 polyfill,才能在低版本使用。

Babel

Babel 可以方便的完成上面的两件事。Babel 是一个 JavaScript 编译器,能将 ES6 代码转为 ES5 代码,让你使用最新的语言特性而不用担心兼容性问题,并且可以通过插件机制根据需求灵活的扩展。在 Babel 执行编译的过程中,会从项目根目录下的 .babelrc 文件读取配置。.babelrc 是一个 JSON 格式的文件。

Plugins

plugins 属性告诉 Babel 要使用哪些插件,插件可以控制如何转换代码。

例如: 配置文件里的 transform-runtime 对应的插件全名叫做 babel-plugin-transform-runtime。在文件里可以忽略前缀 babel-plugin-,安装需要全名。

Presets

presets 属性告诉 Babel 要转换的源码使用了哪些新的语法特性,一个 Presets 对一组新语法特性提供支持,多个 Presets 可以叠加。 Presets 其实是一组 Plugins 的集合,每一个 Plugin 完成一个新语法的转换工作。Presets 是按照 ECMAScript 草案来组织的,通常可以分为以下三大类:

  1. 已经被写入 ECMAScript 标准里的特性,由于之前每年都有新特性被加入到标准里,所以又可细分为:全拼 babel-preset-

    a. es2015 包含在 2015 里加入的新特性。
    b. es2016 包含在 2016 里加入的新特性。
    c. es2017 包含在 2017 里加入的新特性。
    d. env 包含当前所有 ECMAScript 标准里的最新特性。

{
    "plugins": [
        "check-es2015-constants",
        "es2015-arrow-functions",
        "es2015-block-scoped-functions"
    ]
}

为了方便,将同属ES2015的几十个Transform Plugins集合到babel-preset-es2015一个Preset中。

{
    "presets": [
        "es2015"
    ]
}

随着时间的推移,可能出现好多预设版本,然后就出现了 preset-env , 包含当前所有 ECMAScript 标准里的最新特性。

{
    "presets": [
        "env"
    ]
}

  1. 被社区提出来但还未被写入 ECMAScript 标准里的特性。

    a. stage0 只是一个想法,有 Babel 插件实现了对这些特性的支持,但是不确定是否会被定为标准;
    b. stage1 值得被纳入标准的特性;
    c. stage2 该特性规范已经被起草,将会被纳入标准里;
    d. stage3 该特性规范已经定稿,各大浏览器厂商和 Node.js 社区开始着手实现;
    d. stage4 在接下来的一年将会加入到标准里去。

  2. 为了支持一些特定应用场景下的语法,和 ECMAScript 标准没有关系,例如 babel-preset-react 是为了支持 React 开发中的 JSX 语法。

Presets的其他配置

{
    "presets": [
        [
            "env",
            {
                // 项目所支持的浏览器的配置
                "target": {
                    "browsers": [
                        // 支持每一个浏览器最后两个版本
                        "last 2 versions",
                        // 大于等于7版本的 safari
                        "safari >= 7",
                        // 支持市场份额超过 5% 的浏览器
                        "> 5%"
                    ],
                    // 也能够指定浏览器的指定版本
                    "chrome": 56,
                    // 如是经过 Babel 编译 Node.Js 代码的话,支持的是当前运行版本的nodejs
                    "node": "current"
                },
                // 启用 ES6 模块语法转换为另一种模块类型,false 不会进行转换
                // 默认为 'commonjs', 'amd' | 'umd' | 'systemjs' | 'commonjs' | false
                // 之前我们通过 babel 来将 ES6 模块语法转换为 AMD, CommonJS,UMD
                // webpack都帮我作了这件事了,因此咱们不须要babel来作
                // 不然可能会产生冲突
                "modules": false
            }
        ]
    ]
}

webpack 配置

module.exports = {
    module: {
        rules: [
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: [
                        ['@babel/preset-env']
                    ]
                }
            }
        ]
    }
}

babel-loader 不是用来 ES6 转译的,它充其量只是一个打通 babelwebpack 的一个 桥梁 ,首先两者得建立一个连接。

@babel/preset-env 是用来将 ES6 的语法转为 ES5的。比如 letconst 转为 var()=>{} 转为 function () {} ES5函数。

但是 @babel/preset-env 不会转换 新的API,以及一些在全局对象上的方法 都不会进行转码。 比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局对象,以及 Object.assign , Array.from 全局对象的方法,都不会进行转码。

虽然也是进行了语法翻译,但是只是翻译了一部分 ES6 特性。在低版本依赖会报错。

polyfill (填充)

业务代码

我们可以借助 @babel/polyfill 来对这些 API 进行填充。

npm install --save @babel/polyfill

import "@babel/polyfill"; 引入到我们使用了 ES6 特性的文件。

这样子,我们就可以了看到,API 都被 polyfill 好打包输出到 bundle.js 文件。babel 会将所有的 polyfill 全部引入,这样会导致结果的包大小非常大,而我们可能仅仅需要一个方法而已.

按需加载
options: {
    ['@babel/preset-env', {
        useBuiltIns: 'usage',
        "debug": true
    }]
}

我们通过 useBuiltIns: 'usage' 配置,就可以做到 按需转译

targets: {chrome: "67"} 是说如果 chrome 67版本,对 ES6 兼容性好的话,我们就不需要进行 ES6 转 ES5.

  1. babelpolyfill 机制是,对于例如 Array.from 等静态方法,直接在 global.Array 上添加;对于例如 includes 等实例方法,直接在 global.Array.prototype 上添加。这样直接修改了全局变量的原型,有可能会带来意想不到的问题。污染全局.

  2. babel 经常会使用一些辅助函数来帮忙转。

  3. class 语法中,babel 自定义了 _classCallCheck 这个函数来辅助;typeof 则是直接重写了一遍,自定义了 _typeof 这个函数来辅助。这些函数叫做 helpers。如果一个项目中有100个文件,其中每个文件都写了一个 class,那么这个项目最终打包的产物里就会存在100个 _classCallCheck 函数,这样肯定是不好的。

组件库,类库

为了不污染全局对象和内置的对象原型,又想要在低版本浏览器运行,可以配合使用babel-runtime@babel/plugin-transform-runtime

比如当前运行环境不支持 promise,可以通过引入 babel-runtime/core-js/promise 来获取 promise。

在一开始的时候,只是存在 babel-runtime,用起来不方便,但代码中引入 helper 函数,意味着不能页面间共享,造成打包完很多重复的 helper 代码。

Babel又开发了 @babel/plugin-transform-runtime,这个模块会将我们的代码重写,然后引入 helper 函数。这样子就避免了重复打包代码和手动引入的繁琐。

npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime-corejs2
@babel/plugin-transform-runtime 的作用就是转译代码,转译后的代码中可能会引入 @babel/runtime-corejs2 里面的模块。@babel/runtime-corejs2 需要被打包到最终产物里在浏览器中运行

module.exports = {
    module: {
        rules: [
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
                loader: 'babel-loader',
                options: {
                    "plugins": ["@babel/plugin-transform-runtime", {
                        "corejs": 2
                    }]
                }
            }
        ]
    }
}

指定 runtime-corejs 的版本,目前有 2 3 两个版本,默认值是 false,不会 polyfill 提案。

使用了 @babel/plugin-transform-runtime 之后

  1. API 从直接修改原型改为从一个统一的模块中引入,避免了对全局变量及其原型的污染。
  2. helpers 从之前的直接定义改为了从一个统一的模块中引入,使得打包的结果中每个 helper 只会存在一个。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值