威尼斯手机平台-电子正规官网登录首页

热门关键词: 威尼斯手机平台,威尼斯登录首页,威尼斯正规官网
威尼斯电子平台下面是 ES6 箭头函数语法的代码,主要更新内容
分类:威尼斯电子平台

Babel 7.5.0 已发布,Babel 是用于编写下一代 JavaScript 的编译器。

在 webpack 中编写 JavaScript 代码,可以使用最新的 ES 语法,而最终打包的时候,webpack 会借助 Babel 将 ES6+语法转换成在目标浏览器可执行 ES5 语法。所以 Babel 是一个重要的知识点需要掌握。

时间: 2019-11-02阅读: 92标签: babel

主要更新内容:

什么是 Babel

对Babel的配置项的作用不那么了解,是否会影响日常开发呢?老实说,大多情况下没有什么影响。

Spec Compliance

  • babel-parser
    • #10099 在词法绑定中禁止使用“let”作为名称

Babel 是 JavaScript 的编译器,通过 Babel 可以将我们写的最新 ES 语法的代码轻松转换成任意版本的 JavaScript 语法。随着浏览器逐步支持 ES 标准,我们不需要改变代码,只需要修改 Babel 配置即可以适配新的浏览器。举例说明,下面是 ES6 箭头函数语法的代码:

不过呢,还是想更进一步了解下,于是最近认真阅读了Babel的文档,外加不断编译验证,输出了本篇文章,为了更好的阅读体验,修修改改,最终算是以我个人比较喜欢的方式推进了每个知识点(每一个配置的引入都是有原因的),希望能够帮助你对Babel的各种配置有一个更清晰的认识 (已经很懂的小伙伴,无视本文) 。

New Feature

  • babel-parser
    • #10091 Flow 的 BigInt 类型 
    • #9450 在解析器中实现 f# 管道
    • #9864 [@babel/parser] 添加 "allowUndeclaredExports" 选项
  • babel-plugin-proposal-dynamic-importbabel-preset-env-standalonebabel-preset-env
    • #10109 将 @babel/plugin-proposal-dynamic-import 添加到 @babel/preset-env 
  • babel-plugin-proposal-dynamic-importbabel-plugin-transform-modules-amdbabel-plugin-transform-modules-commonjsbabel-plugin-transform-modules-systemjs
    • #9552 创建 @babel/plugin-proposal-dynamic-import
  • babel-plugin-proposal-pipeline-operatorbabel-plugin-syntax-pipeline-operator
    • #9984 转换为 F# 管道
  • babel-plugin-transform-typescriptbabel-types
    • #9785 实现对 TypeScript 命名空间的支持

新版本还包括不少 bug 修复,详情可查看更新说明。

(文/开源中国)    

[1,2,3].map;

Babel 是一个 JS 编译器

经过 Babel 处理后,可以转换为普通的 ES5 语法:

Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

[1,2,3].map { return Math.pow;

我们先看看Babel能够做什么:

Babel 初体验

语法转换通过Polyfill方式在目标环境中添加缺失的特性(@babel/polyfill模块)源码转换(codemods)

下面来介绍下 Babel 的安装和功能及其配置文件。

本篇文章的目的是搞明白Babel的使用和配置,搞清楚@babel/runtime,@babel/polyfill,@babel/plugin-transform-runtime这些作用是什么,插件和预设都是用来干什么的,我们为什么需要配置它们,而不是讲如何进行AST转换,如果你对AST转换非常感兴趣,欢迎阅读我们的RN转小程序引擎 Alita的源码,其中应用了大量的AST转换。

1. 使用 babel-cli 命令行工具

更多文章可戳(如Star,谢谢你):

Babel 本身自己带有 CLI(Command-Line Interface,命令行界面)工具,可以单独安装使用。下面我们在项目中安装 @babel/cli 和 @babel/core。

为了更清晰的了解每一步,首先创建一个新项目,例如babelTemp(你爱取啥名取啥名),使用npm init -y进行初始化,创建src/index.js,文件内容如下(你也可以随便写点什么):

npm i -D @babel/core @babel/cli
const fn = () = { console.log('a');};

然后创建一个 babel.js 文件:

OK,创建好的项目先放在一边,先了解下理论知识:

// babel.js[1,2,3].map;

核心库 @babel/core

然后执行npx babel babel.js,则会看到输出的内容,此时可能会看到输出的内容跟源文件内容没有区别,这是因为没有加转换规则,下面安装@babel/preset-env。然后执行 CLI 的时候添加 --presets flag:

Babel 的核心功能包含在@babel/core模块中。看到core这个词了吧,意味着核心,没有它,在babel的世界里注定寸步难行。不安装@babel/core,无法使用babel进行编译。

// 安装 preset-envnpm i -D @babel/preset-env// 执行 CLI 添加 --presetsnpx babel babel.js --presets=@babel/preset-env

CLI命令行工具 @babel/cli

最终输出的代码就是转换为 ES5 的代码了:

babel提供的命令行工具,主要是提供babel这个命令,适合安装在项目里。

‘use strict'[1,2,3].map { return Math.pow;

@babel/node提供了babel-node命令,但是@babel/node更适合全局安装,不适合安装在项目里。

如果要输出结果到固定文件,可以使用 --out-file 或 -o 参数:

npminstall --save-dev @babel/core @babel/cli
npx babel babel.js -o output.js。

现在你就可以在项目中使用babel进行编译啦(如果不安装@babel/core,会报错噢)

Tips: Babel 7 使用了 @babel 命名空间来区分官方包,因此以前的官方包 babel-xxx 改成了 @babel/xxx。

将命令配置在package.json文件的scripts字段中:

2.配置文件

//..."scripts": { "compiler": "babel src --out-dir lib --watch"}

除了使用命令行配置 flag 之外,Babel 还支持配置文件,配置文件支持两种:

使用npm run compiler来执行编译,现在我们没有配置任何插件,编译前后的代码是完全一样的。

使用 package.json 的 babel 属性;

因为Babel虽然开箱即用,但是什么动作也不做,如果想要Babel做一些实际的工作,就需要为其添加插件(plugin)。

在项目根目录单独创建 .babelrc或者 .babelrc.js文件。

插件

// package.json{ ‘name': ‘my-package', ‘version': ‘1.0.0', ‘babel': { ‘presets': [‘@babel/preset-env'] }} // .babelrc{ ‘presets': [‘@babel/preset-env']}

Babel构建在插件之上,使用现有的或者自己编写的插件可以组成一个转换通道,Babel的插件分为两种: 语法插件和转换插件。

Babel会在正在被转义的文件当前目录中查找一个 .babelrc 文件。 如果不存在,它会向外层目录遍历目录树,直到找到一个 .babelrc 文件,或一个 package.json 文件中有 "babel": {}。

语法插件

3.env 选项

这些插件只允许Babel解析(parse)特定类型的语法(不是转换),可以在AST转换时使用,以支持解析新语法,例如:

如果我们希望在不同的环境中使用不同的 Babel 配置,那么可以在配置文件中添加 env 选项:

import * as babel from "@babel/core";const code = babel.transformFromAstSync(ast, { //支持可选链 plugins: ["@babel/plugin-proposal-optional-chaining"], babelrc: false}).code;
{ ‘env': { ‘production': { ‘presets': [‘@babel/preset-env'] } }}

转换插件

env 选项的值将从 process.env.BABEL_ENV 获取,如果没有的话,则获取 process.env.NODE_ENV 的值,它也无法获取时会设置为 "development"。

转换插件会启用相应的语法插件(因此不需要同时指定这两种插件),这点很容易理解,如果不启用相应的语法插件,意味着无法解析,连解析都不能解析,又何谈转换呢?

Babel 的插件和 Preset

插件的使用

Babel 的语法转换是通过强大的插件系统来支持的。Babel 的插件分为两类:转换插件和语法解析插件。

如果插件发布在npm上,可以直接填写插件的名称,Babel会自动检查它是否已经被安装在node_modules目录下,在项目目录下新建.babelrc文件 (下文会具体介绍配置文件),配置如下:

不同的语法对应着不同的转换插件,比如我们要将箭头函数转换为 ES5 函数写法,那么可以单独安装 @babel/plugin-transform-arrow-functions 插件,转换插件主要职责是进行语法转换的,而解析插件则是扩展语法的,比如我们要解析 jsx 这类 React 设计的特殊语法,则需要对应的 jsx 插件。

//.babelrc{ "plugins": ["@babel/plugin-transform-arrow-functions"]}

如果不想一个个的添加插件,那么可以使用插件组合 preset,最常见的 preset 是 @babel/preset-env。之前的 preset 是按照 TC39 提案阶段来分的,比如看到 babel-preset-stage-1 代表,这个插件组合里面是支持 TC39< Stage-1 阶段的转换插件集合。

也可以指定插件的相对/绝对路径

@babel/preset-env 是 Babel 官方推出的插件预设,它可以根据开发者的配置按需加载对应的插件,通过 @babel/preset-env 我们可以根据代码执行平台环境和具体浏览器的版本来产出对应的 JavaScript 代码,例如可以设置代码执行在 Node.js 8.9 或者 iOS 12 版本。

{ "plugins": ["./node_modules/@babel/plugin-transform-arrow-functions"]}

Babel polyfill

执行npm run compiler,可以看到箭头函数已经被编译OK,lib/index.js内容如下:

Babel 只负责进行语法转换,即将 ES6 语法转换成 ES5 语法,但是如果在 ES5 中,有些对象、方法实际在浏览器中可能是不支持的,例如:Promise、 Array.prototype.includes,这时候就需要用 @babel/polyfill 来做模拟处理。@babel/polyfill 使用方法是先安装依赖,然后在对应的文件内显性的引入:

const fn = function () { console.log('a');};
// 安装,注意因为我们代码中引入了 polyfill,所以不再是开发依赖npm i @babel/polyfill

现在,我们仅支持转换箭头函数,如果想将其它的新的JS特性转换成低版本,需要使用其它对应的plugin。如果我们一个个配置的话,会非常繁琐,因为你可能需要配置几十个插件,这显然非常不便,那么有没有什么办法可以简化这个配置呢?

在文件内直接 import 或者 require 进来:

有!预设!(感谢强大的Babel)

// polyfillimport ‘@babel/polyfill'console.log;

预设

Bable runtime

通过使用或创建一个preset即可轻松使用一组插件。

@babel/polyfill 虽然可以解决模拟浏览器不存在对象方法的事情,但是有以下两个问题:

官方 Preset@babel/preset-env@babel/preset-flow@babel/preset-react@babel/preset-typescript

直接修改内置的原型,造成全局污染;

注:从 Babel v7 开始,所以针对标准提案阶段的功能所编写的预设(stage preset)都已被弃用,官方已经移除了@babel/preset-stage-x。

无法按需引入,Webpack 打包时,会把所有的 Polyfill 都加载进来,导致产出文件过大。

@babel/preset-env

为了解决这个问题,Babel 社区又提出了 @babel/runtime 的方案,@babel/runtime 不再修改原型,而是采用替换的方式,比如我们用 Promise,使用 @babel/polyfill 会产生一个 window.Promise 对象,而 @babel/runtime 则会生成一个 _Promise 来替换掉我们代码中用到的 Promise。另外 @babel/runtime 还支持按需引入。下面以转换 Object.assign 为例,来看下 @babel/runtime 怎么使用。

@babel/preset-env主要作用是对我们所使用的并且目标浏览器中缺失的功能进行代码转换和加载polyfill,在不进行任何配置的情况下,@babel/preset-env所包含的插件将支持所有最新的JS特性(ES2015,ES2016等,不包含 stage 阶段),将其转换成ES5代码。例如,如果你的代码中使用了可选链(目前,仍在 stage 阶段),那么只配置@babel/preset-env,转换时会抛出错误,需要另外安装相应的插件。

安装依赖 @babel/runtime:npm i @babel/runtime; 安装 npm i -D @babel/plugin-transform-runtime 作为 Babel 插件; 安装需要转换 Object.assign 的插件:

//.babelrc{ "presets": ["@babel/preset-env"]}
npm i -D @babel/plugin-transform-object-assign

需要说明的是,@babel/preset-env会根据你配置的目标环境,生成插件列表来编译。对于基于浏览器或Electron的项目,官方推荐使用.browserslistrc文件来指定目标环境。默认情况下,如果你没有在Babel配置文件中(如 .babelrc)设置targets或ignoreBrowserslistConfig,@babel/preset-env会使用browserslist配置源。

编写一个 runtime.js 文件,内容如下:

如果你不是要兼容所有的浏览器和环境,推荐你指定目标环境,这样你的编译代码能够保持最小。

Object.assign;

例如,仅包括浏览器市场份额超过0.25%的用户所需的polyfill和代码转换(忽略没有安全更新的浏览器,如 IE10 和 BlackBerry):

执行 npx babel runtime.js --plugins @babel/plugin-transform-runtime,@babel/plugin-transform-object-assign,最终的输出结果是:

//.browserslistrc 0.25%not dead
import _extends from ‘@babel/runtime/helpers/extends';_extends;

查看browserslist的更多配置

代码中自动引入了 @babel/runtime/helpers/extends 这个模块(所以要添加 @babel/runtime 依赖啊)。@babel/runtime也不是完美的解决方案,由于 @babel/runtime 不修改原型,所以类似[].includes() 这类使用直接使用原型方法的语法是不能被转换的。

例如,你将.browserslistrc的内容配置为:

Tips:'@babel/polyfill'实际是 core-js和regenerator-runtime的合集,所以如果要按需引入'@babel/polyfill'的某个模块,可以直接引入对应的core-js 模块,但是手动引入的方式还是太费劲。

last2Chrome versions

@babel/preset-env

然后再执行npm run compiler,你会发现箭头函数不会被编译成ES5,因为chrome的最新2个版本都能够支持箭头函数。现在,我们将.browserslistrc仍然换成之前的配置。

铺垫了这么多,我们继续来讲 @babel/preset-env,前面介绍了@babel/preset-env 可以零配置的转化 ES6 代码,我们如果要精细化的使用 @babel/preset-env ,就需要配置对应的选项了,在 @babel/preset-env 的选项中,useBuiltIns 和 target 是最重要的两个, useBuiltIns 用来设置浏览器 polyfill,target 是为了目标浏览器或者对应的环境。

就咱们目前的代码来说,当前的配置似乎已经是OK的了。

preset-env 的 useBuiltIns

我们修改下src/index.js。

前面介绍了 @babel/polyfill 和 @babel/runtime 两种方式来实现浏览器 polyfill,两种方式都比较繁琐,而且不够智能,我们可以使用 @babel/preset-env 的 useBuildIn 选项做 polyfill,这种方式简单而且智能。useBuiltIns 默认为 false,可以使用的值有 usage 和 entry:

const isHas = [1,2,3].includes(2);const p = new Promise((resolve, reject) = { resolve(100);});
{ ‘presets': [ ‘@babel/preset-env', { ‘useBuiltnls': ‘usage|entry|false' } ]}

编译出来的结果为:

usage 表示明确使用到的 Polyfill 引用。在一些 ES2015+ 语法不支持的环境下,每个需要用到 Polyfill 的引用时,会自动加上,例如:

"use strict";var isHas = [1, 2, 3].includes(2);var p = new Promise(function (resolve, reject) { resolve(100);});
const p = new Promise;‘foobar'.includes;

这个编译出来的代码在低版本浏览器中使用的话,显然是有问题的,因为低版本浏览器中数组实例上没有includes方法,也没有Promise构造函数。

使用 useBuiltIns='usage' 编译之后,上面代码变成,真正的做到了按需加载,而且类似 [].includes() 这类直接使用原型方法的语法是能被转换的:

这是为什么呢?因为语法转换只是将高版本的语法转换成低版本的,但是新的内置函数、实例方法无法转换。这时,就需要使用polyfill上传了,顾名思义,polyfill的中文意思是垫片,所谓垫片就是垫平不同浏览器或者不同环境下的差异,让新的内置函数、实例方法等在低版本浏览器中也可以使用。

‘use strict'require(‘core-js/modules/es.array.includes');require(‘core-js/modules/es.object.to-string');require(‘core-js/modules/es.promise');require(‘core-js/modules/es.string.includes');var p = new Promise;‘foobar'.includes;

Polyfill

entry 表示替换 import "@babel/polyfill";(新版本的 Babel,会提示直接引入 core-js或者regenerator-runtime/runtime来代替 @babel/polyfill)的全局声明,然后根据 targets 中浏览器版本的支持,将 polyfill 拆分引入,仅引入有浏览器不支持的 polyfill,所以 entry 相对 usage 使用起来相对麻烦一些,首先需要手动显性的引入 @babel/polyfill ,而且根据配置 targets 来确定输出,这样会导致代码实际用不到的 polyfill 也会被打包到输出文件,导致文件比较大。

@babel/polyfill模块包括core-js和一个自定义的regenerator runtime模块,可以模拟完整的 ES2015+ 环境(不包含第4阶段前的提议)。

一般情况下,个人建议直接使用 usage 就满足日常开发了。

这意味着可以使用诸如Promise和WeakMap之类的新的内置组件、Array.from或Object.assign之类的静态方法、Array.prototype.includes之类的实例方法以及生成器函数(前提是使用了@babel/plugin-transform-regenerator插件)。为了添加这些功能,polyfill将添加到全局范围和类似String这样的内置原型中(会对全局环境造成污染,后面我们会将不污染全局环境的方法)。

需要提一下的是,polyfill 用到的 core-js 是可以指定版本的,比如使用 core-js@3,则首先安装依赖 npm i -S core-js@3,然后在 Babel 配置文件 .babelrc 中写上版本。

首先,安装@babel/polyfill依赖:

// .babelrc{ ‘presets': [ [ ‘@babel/preset-env', { ‘useBuiltlns': ‘useage', ‘corejs': 3 } ] ]}
npminstall --save @babel/polyfill

preset-env 的 target

注意:不使用--save-dev,因为这是一个需要在源码之前运行的垫片。

假设希望代码中使用 ES6 的模板字面量`语法,但是实际执行代码的宿主浏览器是 IE 10 却不支持,那么我们可以使用target指定目标浏览器了。

我们需要将完整的polyfill在代码之前加载,修改我们的src/index.js:

{ ‘presets': [ [ ‘@babel/preset-env', { ‘targets': { ‘browsers': ‘IE 10' } } ] ]}
import '@babel/polyfill';const isHas = [1,2,3].includes(2);const p = new Promise((resolve, reject) = { resolve(100);});

如果我们代码是在 Node.js 环境执行的,则可以指定 Node.js 的版本号:

@babel/polyfill需要在其它代码之前引入,我们也可以在webpack中进行配置。

{ ‘presets': [ [ ‘env', { ‘@babel/preset-env': { ‘node: ‘8.9.3' } } ] ] }

例如:

targets.browsers 需要使用 browserslist 的配置方法,但是其设置会被 targets.[chrome, opera, edge, firefox, safari, ie, ios, android, node, electron] 覆盖;targets.node 设置为 true 或 "current" 可以根据当前 Node.js 版本进行动态转换。也可以设置为具体的数字表示需要支持的最低 Node.js 版本;targets.esmodules 设置使用 ES Modules 语法,最新浏览器支持,这个在后面 Webpack 插件章节会详细介绍如何实现 Modern Mode。

entry: [ require.resolve('./polyfills'), path.resolve('./index')]

在 Webpack 中使用 Babel

polyfills.js文件内容如下:

通过上面的内容,我们已经掌握了 Babel 的基本用法,下面在 webpack 中使用 Babel 就变得很简单了,首先安装 npm 依赖,然后修改 webpack.config.js。

//当然,还可能有一些其它的 polyfill,例如 stage 4之前的一些 polyfillimport '@babel/polyfill';
// 安装开发依赖npm i webpack babel-loader webpack-cli @babel/core @babel/preset-env @babel/plugin-transform-runtime -D// 将 runtime 作为依赖npm i @babel/runtime -S

现在,我们的代码不管在低版本还是高版本浏览器(或node环境)中都能正常运行了。不过,很多时候,我们未必需要完整的@babel/polyfill,这会导致我们最终构建出的包的体积增大,@babel/polyfill的包大小为89K (当前@babel/polyfill版本为 7.7.0)。

第二步创建 webpack.config.js 文件,内容如下:

我们更期望的是,如果我使用了某个新特性,再引入对应的polyfill,避免引入无用的代码。

// webpack.config.jsmodule.exports = { entry: ‘./babel.js', mode: ‘development', devtool: false, module: { rules: [ { test: /.js$/, use: [ { loader: ‘babel-loader', options: { presets: [ [ ‘@babel/preset-env', { useBuiltlns: ‘usage' } ] ] } } ] } ] }}

值得庆幸的是,Babel已经考虑到了这一点。

上面的 webpack.config.js 文件直接将 Babel 的配置写到了 options 中,还可以在项目根目录下创建 .babelrc 或者使用 package.json 的 babel 字段。

@babel/preset-env提供了一个useBuiltIns参数,设置值为usage时,就只会包含代码需要的polyfill。有一点需要注意:配置此参数的值为usage,必须要同时设置corejs(如果不设置,会给出警告,默认使用的是"corejs": 2) ,注意: 这里仍然需要安装@babel/polyfill(当前@babel/polyfill版本默认会安装 "corejs": 2):

小结

首先说一下使用core-js@3的原因,core-js@2分支中已经不会再添加新特性,新特性都会添加到core-js@3。例如你使用了Array.prototype.flat(),如果你使用的是core-js@2,那么其不包含此新特性。为了可以使用更多的新特性,建议大家使用core-js@3。

在本篇中,我们学习了 Webpack 怎么配置 Babel,希望对大家有所帮助,如果喜欢萝卜的文章,请大家持续关注,下一篇我将给大家介绍 webpack-dev-server 这个超好用的工具。

安装依赖依赖:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

npminstall--save core-js@3

core-js (点击了解更多): JavaScript 的模块化标准库,包含Promise、Symbol、Iterator和许多其他的特性,它可以让你仅加载必需的功能。

现在,修改Babel的配置文件如下:

//.babelrcconst presets = [ [ "@babel/env", { "useBuiltIns": "usage", "corejs": 3 } ]]

Babel会检查所有代码,以便查找在目标环境中缺失的功能,然后仅仅把需要的polyfill包含进来。

例如,src/index.js代码不变:

const isHas = [1,2,3].includes(2);const p = new Promise((resolve, reject) = { resolve(100);});

我们看看编译出来的文件(lib/index):

"use strict";require("core-js/modules/es.array.includes");require("core-js/modules/es.object.to-string");require("core-js/modules/es.promise");var isHas = [1, 2, 3].includes(2);var p = new Promise(function (resolve, reject) { resolve(100);});

同样的代码,我们用webpack构建一下(production模式),能看到最终的代码大小仅为: 20KB。而如果我们引入整个@babel/polyfill的话,构建出的包大小为:89KB

前面曾提到,在useBuiltIns参数值为usage时,仍然需要安装@babel/polyfill,虽然我们上面的代码转换中看起来并没有使用到,但是,如果我们源码中使用到了async/await,那么编译出来的代码需要require("regenerator-runtime/runtime"),在@babel/polyfill的依赖中,当然啦,你也可以只安装regenerator-runtime/runtime取代安装@babel/polyfill。

到了这一步,已经很棒棒了,是不是想跳起来转个圈圈?

下面我要说的内容,也许你已经知道,也许你还不知道,这都不重要,但是此刻起,你要知道了:Babel会使用很小的辅助函数来实现类似_createClass等公共方法。默认情况下,它将被添加(inject)到需要它的每个文件中。

假如,我们的src/index.js是这样的:

class Point { constructor(x, y) { this.x = x; this.y = y; }; getX() { return this.x; }}let cp = new ColorPoint(25, 8);

编译出来的lib/index.js,如下所示:

"use strict";require("core-js/modules/es.object.define-property");function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }function _defineProperties(target, props) { for (var i = 0; i  props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }var Point = /*#__PURE__*/ function () { function Point(x, y) { _classCallCheck(this, Point); this.x = x; this.y = y; } _createClass(Point, [{ key: "getX", value: function getX() { return this.x; } }]); return Point; }();var cp = new ColorPoint(25, 8);

看起来,似乎并没有什么问题,但是你想一下,如果你有10个文件中都使用了这个class,是不是意味着_classCallCheck、_defineProperties、_createClass这些方法被inject了10次。这显然会导致包体积增大,最关键的是,我们并不需要它inject多次。

这个时候,就是@babel/plugin-transform-runtime插件大显身手的时候了,使用@babel/plugin-transform-runtime插件,所有帮助程序都将引用模块@babel/runtime,这样就可以避免编译后的代码中出现重复的帮助程序,有效减少包体积。

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime是一个可以重复使用Babel注入的帮助程序,以节省代码大小的插件。

注意:诸如Array.prototype.flat()等实例方法将不起作用,因为这需要修改现有的内置函数(可以使用@babel/polyfill来解决这个问题) —— 对此需要说明的是如果你配置的是corejs3,core-js@3现在已经支持原型方法,同时不污染原型

另外,@babel/plugin-transform-runtime需要和@babel/runtime配合使用。

首先安装依赖,@babel/plugin-transform-runtime通常仅在开发时使用,但是运行时最终代码需要依赖@babel/runtime,所以@babel/runtime必须要作为生产依赖被安装,如下 :

npm install --save-dev @babel/plugin-transform-runtimenpm install --save @babel/runtime

除了前文所说的,@babel/plugin-transform-runtime可以减少编译后代码的体积外,我们使用它还有一个好处,它可以为代码创建一个沙盒环境,如果使用@babel/polyfill及其提供的内置程序(例如Promise,Set和Map),则它们将污染全局范围。虽然这对于应用程序或命令行工具可能是可以的,但是如果你的代码是要发布供他人使用的库,或者无法完全控制代码运行的环境,则将成为一个问题。

@babel/plugin-transform-runtime会将这些内置别名作为core-js的别名,因此您可以无缝使用它们,而无需polyfill。

修改.babelrc的配置,如下:

//.babelrc{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3 } ] ], "plugins": [ [ "@babel/plugin-transform-runtime" ] ]}

重新编译npm run compiler, 现在,编译出来的内容为(lib/index.js):

"use strict";var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));var Point = /*#__PURE__*/ function () { function Point(x, y) { (0, _classCallCheck2.default)(this, Point); this.x = x; this.y = y; } (0, _createClass2.default)(Point, [{ key: "getX", value: function getX() { return this.x; } }]); return Point; }();var cp = new ColorPoint(25, 8);

可以看出,帮助函数现在不是直接被inject到代码中,而是从@babel/runtime中引入。前文说了使用@babel/plugin-transform-runtime可以避免全局污染,我们来看看是如何避免污染的。

修改src/index.js如下:

let isHas = [1,2,3].includes(2);new Promise((resolve, reject) = { resolve(100);});

编译出来的代码如下(lib/index.js):

"use strict";require("core-js/modules/es.array.includes");require("core-js/modules/es.object.to-string");require("core-js/modules/es.promise");var isHas = [1, 2, 3].includes(2);new Promise(function (resolve, reject) { resolve(100);});

Array.prototype上新增了includes方法,并且新增了全局的Promise方法,污染了全局环境,这跟不使用@babel/plugin-transform-runtime没有区别嘛。

如果我们希望@babel/plugin-transform-runtime不仅仅处理帮助函数,同时也能加载polyfill的话,我们需要给@babel/plugin-transform-runtime增加配置信息。

首先新增依赖@babel/runtime-corejs3:

npminstall@babel/runtime-corejs3--save

修改配置文件如下(移除了@babel/preset-env的useBuiltIns的配置,不然不就重复了嘛嘛嘛,不信的话,你用async/await编译下试试咯):

{ "presets": [ [ "@babel/preset-env" ] ], "plugins": [ [ "@babel/plugin-transform-runtime",{ "corejs": 3 } ] ]}

然后重新编译,看一下,编译出来的结果(lib/index.js):

"use strict";var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));var _context;var isHas = (0, _includes.default)(_context = [1, 2, 3]).call(_context, 2);new _promise.default(function (resolve, reject) { resolve(100);});

可以看出,没有直接去修改Array.prototype,或者是新增Promise方法,避免了全局污染。如果上面@babel/plugin-transform-runtime配置的core-js是 "2",其中不包含实例的polyfill需要单独引入。

划重点:如果我们配置的 `corejs` 是 `3` 版本,那么不管是实例方法还是全局方法,都不会再污染全局环境。

看到这里,不知道大家有没有这样一个疑问?给@babel/plugin-transform-runtime配置corejs是如此的完美,既可以将帮助函数变成引用的形式,又可以动态引入polyfill,并且不会污染全局环境。何必要给@babel/preset-env提供useBuiltIns功能呢,看起来似乎不需要呀。

带着这样的疑问,我新建了几个文件(内容简单且基本一致,使用了些新特性),然后使用webpack构建,以下是我对比的数据:

.babelrc 配置

webpack mode production

不使用@babel/plugin-transform-runtime36KB使用@babel/plugin-transform-runtime,并配置参数corejs: 3。不会污染全局环境37KB使用@babel/plugin-transform-runtime,不配置corejs22KB

我猜测是@babel/runtime-corejs3/XXX的包本身比core-js/modules/XXX要大一些~

插件/预设补充知识插件的排列顺序很重要!!!

如果两个转换插件都将处理“程序(Program)”的某个代码片段,则将根据转换插件或preset的排列顺序依次执行。

插件在 Presets 前运行。插件顺序从前往后排列。Preset 顺序是颠倒的(从后往前)。

例如:

{ "plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-syntax-dynamic-import"]}

先执行@babel/plugin-proposal-class-properties,后执行@babel/plugin-syntax-dynamic-import

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

preset的执行顺序是颠倒的,先执行@babel/preset-react, 后执行@babel/preset-env。

插件参数

插件和preset都可以接受参数,参数由插件名和参数对象组成一个数组。preset设置参数也是这种格式。

如:

{ "plugins": [ [ "@babel/plugin-proposal-class-properties", { "loose": true } ] ]}

插件的短名称

如果插件名称为@babel/plugin-XXX,可以使用短名称@babel/XXX:

{ "plugins": [ "@babel/transform-arrow-functions" //同 "@babel/plugin-transform-arrow-functions" ]}

如果插件名称为babel-plugin-XXX,可以使用端名称XXX,该规则同样适用于带有scope的插件:

{ "plugins": [ "newPlugin", //同 "babel-plugin-newPlugin" "@scp/myPlugin" //同 "@scp/babel-plugin-myPlugin" ]}

创建 Preset可以简单的返回一个插件数组

module.exports = function() { return { plugins: [ "A", "B", "C" ] }}

preset中也可以包含其他的preset,以及带有参数的插件。

module.exports = function() { return { presets: [ require("@babel/preset-env") ], plugins: [ [require("@babel/plugin-proposal-class-properties"), { loose: true }], require("@babel/plugin-proposal-object-rest-spread") ] }}

配置文件

Babel 支持多种格式的配置文件。这部分内容补充了解下即可,谁管你用哪种配置文件,只要你的配置是OK的就可以了(敷衍)~

所有的BabelAPI 参数都可以被配置,但是如果该参数需要使用的 JS 代码,那么可能需要使用 JS 代码版的配置文件。

根据使用场景可以选择不同的配置文件:

如果希望以编程的方式创建配置文件或者希望编译node_modules目录下的模块:那么babel.config.js可以满足你的需求。

如果只是需要一个简单的并且中用于单个软件包的配置:那么.babelrc即可满足你的需求。

babel.config.js

在项目根目录下创建一个名为babel.config.js的文件。

module.exports = function(api) { api.cache(true); const presets = [...]; const plugins = [...]; return { presets, plugins };} 

具体的配置可以查看:babel.config.js 文档

.babelrc

在项目根目录下创建一个名为.babelrc的文件:

{ "presets": [], "plugins": []}

具体的配置可以参考.babelrc 文档

package.json

可以将.babelrc中的配置信息作为babel键(key) 添加到package.json文件中:

{ "name": "my-package", "babel": { "presets": [], "plugins": [] }}

.babelrc.js

与.babelrc配置相同,但是可以使用JS编写。

//可以在其中调用 Node.js 的APIconst presets = [];const plugins = [];module.exports = { presets, plugins };

本文由威尼斯手机平台发布于威尼斯电子平台,转载请注明出处:威尼斯电子平台下面是 ES6 箭头函数语法的代码,主要更新内容

上一篇:没有了 下一篇:没有了
猜你喜欢
热门排行
精彩图文