javascript编译器

介绍 ( Introduction )

Transpilers, or source-to-source compilers, are tools that read source code written in one programming language, and produce the equivalent code in another language. Languages you write that transpile to JavaScript are often called compile-to-JS languages, and are said to target JavaScript.

Transpiler或源到源编译器是读取以一种编程语言编写的源代码,并以另一种语言生成等效代码的工具。 您编写的可转换为JavaScript的语言通常称为“ 编译为JS”语言,并且据说是针对 JavaScript的。

Oh, and, even though people tend to use "compile/r" and "transpile/r" interchangeably, I'll prefer the latter term in this article.

哦,即使人们倾向于交替使用“ compile / r”和“ transpile / r”,在本文中我还是更喜欢后者。

You've probably heard about CoffeeScript and TypeScript. CoffeeScript provides syntactic sugar for a number of features not yet native to JavaScript, while discouraging some of JavaScript's "bad parts". TypeScript is more drastic, adding classical object-oriented semantics to a fundamentally different language.

您可能已经听说过CoffeeScript和TypeScript 。 CoffeeScript为JavaScript尚未提供的许多功能提供了语法糖,同时不鼓励使用JavaScript的“不良部分” 。 TypeScript更加激进,将经典的面向对象的语义添加到根本不同的语言中 。

Anything you can write in JavaScript, you can write in CoffeeScript or TypeScript.

您可以使用JavaScript编写的任何内容,都可以使用CoffeeScript或TypeScript编写。

"use strict";

// Good 'ol JS
function printSecret ( secret ) {
    console.log(`${secret}. But don't tell anyone.`);
}

printSecret("I don't like CoffeeScript"); 
"use strict";

// Good 'ol JS
function printSecret ( secret ) {
    console.log(`${secret}. But don't tell anyone.`);
}

printSecret("I don't like CoffeeScript");
"use strict"

# CoffeeScript
printSecret (secret) =>
    console.log '#{secret}. But don't tell anyone.'

printSecret "I don't like JavaScript."
"use strict";

// TypeScript -- JavaScript, with types and stuff
function printSecret ( secret : string ) {
    console.log("${secret}. But don't tell anyone.");
}

printSecret("I don't like CoffeeScript.");

Trouble is, JavaScript environments only understand . . . Well, JavaScript. Trying those last two examples in your console will throw errors. As a matter of fact, if you try that pure JavaScript example in an older browser, you'll still get an error. Template literals still don't have reliable browser support.

麻烦的是,JavaScript环境只能理解。 。 。 好吧,JavaScript。 在控制台中尝试最后两个示例将引发错误。 实际上,如果您在较旧的浏览器中尝试该纯JavaScript示例,仍然会收到错误消息。 模板文字仍然没有可靠的浏览器支持。

That's where transpilers come in: They read CoffeeScript, TypeScript, and ES2015, and spit out JavaScript guaranteed to work anywhere.

那就是编译器出现的地方:他们阅读CoffeeScript,TypeScript和ES2015,并吐出保证可以在任何地方使用JavaScript。

捍卫转运者 ( In Defense of Transpilers )

If your workflow doesn't already include a transpiler, you might wonder why you'd even bother. Why learn new syntax and pick up new tools if all we get at the end of the day is the JavaScript we could have written in the first place?

如果您的工作流尚未包含转译器,您可能会想知道为什么还要打扰。 如果我们最终获得的只是我们本来可以编写JavaScript,为什么还要学习新的语法并使用新的工具?

In the case of languages that target JavaScript, it's largely a matter of preference or background. Writing in a language that "thinks" the way you do makes you more productive. People with backgrounds in OOP often like TypeScript because it's familiar territory. Pythonistas like CoffeeScript. Clojurists write ClojureScript. You get the idea.

对于以JavaScript为目标的语言,很大程度上取决于偏好或背景。 用一种“思考”您的工作方式的语言进行写作可以使您的工作效率更高。 具有OOP背景的人们经常喜欢TypeScript,因为它是熟悉的领域。 Pythonista喜欢CoffeeScript。 Clojurist编写ClojureScript。 你明白了。

But the rest of us, who are perfectly fine with writing plain JavaScript, still use transpilers, because they're the only reliable way to use features from ES2015 and beyond.

但是我们其余的人,即使可以编写纯JavaScript,也非常满意,但他们仍然使用编译器,因为它们是使用ES2015及更高版本中的功能的唯一可靠方法。

(Tomorrow's JavaScript, Today)

Anyone who's had to deal with browser compatibility issues before knows its not as simple as writing JavaScript that runs everywhere. That's because every browser uses a different JavaScript engine: Chrome runs V8, Firefox runs SpiderMonkey, and Interet Explorer, Chakra. Each has different performance characteristics, each implements a different subset of ES2015 features, and each is approaching full compliance with the spec at different rates.

曾经不得不处理浏览器兼容性问题的任何人都知道它并不像编写可在任何地方运行JavaScript那样简单。 这是因为每种浏览器都使用不同JavaScript引擎: Chrome运行V8 , Firefox运行SpiderMonkey ,以及Interet Explorer Chakra 。 每个都有不同的性能特征,每个实现不同的ES2015功能子集,并且每个都以不同的速率完全符合规范。

That means that, while our template literal example works just fine for those of you running the most recent Chrome, Firefox, or Safari, it won't work for people running older versions. Or for anyone using Internet Explorer . . . Obviously.

这意味着,虽然我们的模板文字示例对运行最新版本的Chrome,Firefox或Safari的用户来说效果很好,但不适用于运行较旧版本的用户。 或适用于使用Internet Explorer的任何人。 。 。 明显。

The ES6 compatibility table shows that, while we're clearly making progress, it's not quite time to write ES2015 directly. Instead, we write our source in ES2015, and let a transpiler translate it to vanilla ES5 that works in every browser. If you need to support browsers from the last millennium, you can even compile down to ES3.

ES6兼容性表显示,尽管我们正在取得明显进展,但现在还不是直接编写ES2015的时间。 取而代之的是,我们在ES2015中编写源代码,然后让编译器将其翻译为适用于所有浏览器的普通ES5。 如果您需要支持上个千年的浏览器,甚至可以编译到ES3。

This way, you can use any feature supported by your transpiler of choice, right now, and know that it'll work for everyone who hits your site -- from the public employee with no choice but to use IE8 to the hacker types running a FireFox nightly.

这样,您就可以使用您所选择的transpiler支持, 现在的任何功能,并知道它会为大家谁击中了你的网站的工作-从政府雇员没有选择,只能使用IE8来运行黑客类型FireFox每晚。

(Tomorrow's JavaScript, Along the Way)

Transpilers also play an important role in guiding the decisions of the TC39 committee, which is the group in charge of designing the ECMAScript standard.

编译器还在指导TC39委员会的决策中发挥重要作用,TC39委员会是负责设计ECMAScript标准的小组。

For one, transpilers can contribute directly to the inclusion of a feature in the standard. For a potential feature to move from Stage 1 (Proposal) to Stage 2 (Draft):

首先,翻译者可以直接为标准中的功能添加做出贡献。 要使潜在功能从第1阶段(建议)移至第2阶段(草案),请执行以下操作:

Two experimental implementations of the feature are needed, but one of them can be in a transpiler such as Babel.

需要对该功能进行两个实验性的实现,但是其中一个可以在诸如Babel的编译器中进行。

The rest of Dr Rauschmayer's overview of the inclusion process is on his 2ality blog..

Rauschmayer博士对纳入过程的概述的其余部分在他的2ality博客上。 。

Probably more important than this contribution is that feedback from users and implementers of transpilers like Babel or Traceur help TC39 iterate on syntax and proposals. This comes up often during TC39 meetings: If you read every line of the TC39 Meeting Notes -- or, you know, just rgrep -i them -- you'll find that Babel was mentioned 36 times, and spot that the conclusion of the March 25, 2015 meeting on This-binding syntax was: "Get more feedback from users of Babel"!

比这种贡献更重要的是,来自翻译者(例如Babel或Traceur)的用户和实施者的反馈帮助TC39迭代了语法和建议。 这通常在TC39会议期间出现:如果您阅读了TC39会议记录的每一行-或者,您知道只是rgrep -i他们-您会发现Babel被提及了36次,并且发现2015年3月25日,关于这种绑定语法的会议是:“从Babel用户那里获得更多反馈”!

To recap, transpilers:

回顾一下,转译者:

  1. Allow us to write compile-to-JavaScript languages, like CoffeeScript, TypeScript, or ClojureScript;
  2. Let us use new and potential JavaScript features, reliably; and
  3. Contribute to the development of the ECMAScript specification.

Now that we know everything we need to know about what transpilers are, let's dig into how to use them.

既然我们知道了什么是编译器,我们就需要了解所有内容,接下来让我们深入研究如何使用它们。

使用编译器 ( Using Transpilers )

There are a number of ES2015-to-ES5 transpilers out there, but I'll be focusing on Babel.

有很多ES2015到ES5的编译器,但我将重点介绍Babel 。

In this section, we'll cover:

在本节中,我们将介绍:

  1. Comparing ES2015 source to transpiled output;
  2. Setting up the Babel command-line interface (CLI); and
  3. A look at how build toolks like Webpack, JSPM, and Babelify streamline the process.

(My Source Is Your Source)

To get started, head over to Babel's live transpiler. Copy the code below into the JavaScript editor (left box):

首先,请转到Babel的实时翻译机 。 将以下代码复制到JavaScript编辑器中(左框):

"use strict";

class Planet {

  constructor (mass, moons) {
    this.mass  = mass;
    this.moons = moons || 0;
  }

  reportMoons () {
    console.log(`I have ${this.moons} moons.`)
  }

}

// Yeah, Jupiter really does have (at least) 67 moons.
const jupiter = new Planet('Pretty Big', 67);
jupiter.reportMoons();
"use strict";

class Planet {

  constructor (mass, moons) {
    this.mass  = mass;
    this.moons = moons || 0;
  }

  reportMoons () {
    console.log(`I have ${this.moons} moons.`)
  }

}

// Yeah, Jupiter really does have (at least) 67 moons.
const jupiter = new Planet('Pretty Big', 67);
jupiter.reportMoons();

Take a look at the output (reproduced below, for convenience). You'll notice that there are no ES2015 features. const variables get converted to carefully scoped var declarations; class is converted to an old-school function constructor; and the template string desugars to simple string concatenation.

看一下输出(为方便起见,以下复制)。 您会注意到没有 ES2015功能。 const变量将转换为仔细定义的var声明; class转换为老式函数构造器; 并将模板字符串还原为简单的字符串连接。

// Everything below is Babel's output.
'use strict';

var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

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

var Planet = function () {
  function Planet(mass, moons) {
    _classCallCheck(this, Planet);

    this.mass = mass;
    this.moons = moons || 0;
  }

  _createClass(Planet, [{
    key: 'reportMoons',
    value: function reportMoons() {
      console.log('I have ' + this.moons + ' moons.');
    }
  }]);

  return Planet;
}();
// Everything below is Babel's output.
'use strict';

var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

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

var Planet = function () {
  function Planet(mass, moons) {
    _classCallCheck(this, Planet);

    this.mass = mass;
    this.moons = moons || 0;
  }

  _createClass(Planet, [{
    key: 'reportMoons',
    value: function reportMoons() {
      console.log('I have ' + this.moons + ' moons.');
    }
  }]);

  return Planet;
}();

These are features the language has had from day one. Using them to emulate features from ES2015 is precisely how they let us use non-standardized features now, and to do so safely, to boot.

这些是语言从一开始就具有的功能。 使用它们来模拟ES2015中的功能正是它们如何让我们现在使用非标准化功能并安全地进行启动。

As a fun little note, a catch clause creates a scope in JavaScript. This behavior was standardized all the way back in ES3. Traceur took advantage of this for some time to emulate block-scoping, producing something like this:

有趣的一点是, catch子句在JavaScript中创建作用域。 早在ES3中就一直对此行为进行了标准化。 Traceur在一段时间内利用了这一点来模拟块作用域,从而产生如下内容:

"use strict";

// ES2015
{
    let name = "Never woulda thunk.";
}

// ES3/5 output
try { throw void 0; } catch(name) {
    name = "Never woulda thunk.";
    console.log(name);
}
"use strict";

// ES2015
{
    let name = "Never woulda thunk.";
}

// ES3/5 output
try { throw void 0; } catch(name) {
    name = "Never woulda thunk.";
    console.log(name);
}

How's that for a polyfill?

填充料怎么样?

(Setting Up the Babel CLI)

The live REPL is slick, but writing your entire codebase that way would suck. Let's save ourselves some work and set up the Babel CLI.

实时REPL很漂亮,但是用这种方式编写整个代码库会很麻烦。 让我们节省一些工作并设置Babel CLI。

To get started:

开始:

  1. Create a directory somewhere;
  2. Initialize it as an NPM project;
  3. Install the Babel tool, along with the presets and plugins we'll be using; and
  4. Configure Babel to use those presets and plugins.
# This moves to your home directory to create the folder. 
# If you don't want it there, cd somewhere else.
cd && mkdir babel_example && cd $_
npm init
# Hit ENTER a bunch of times . . . 
npm install --save-dev babel-cli babel-preset-es2015 babel-plugin-transform-async-to-generator

This will install the Babel CLI (babel-cli); a collection of plugins enabling all the ES2015 features that Babel supports (babel-preset-es2015); and a plugin allowing you to use the ES7 proposal, Async functions (babel-plugin-transform-async-to-generator).

这将安装Babel CLI( babel-cli ); 一系列插件,支持Babel支持的所有ES2015功能( babel-preset-es2015 ); 还有一个插件,可让您使用ES7提案, 异步功能 ( babel-plugin-transform-async-to-generator )。

To run Babel, you simply type babel <FILENAME>. But before we do that, we need to tell it what plugins to use. Babel looks for this information in a file called .babelrc, in the top level of your NPM project.

要运行Babel,只需键入babel <FILENAME> 。 但是在我们这样做之前,我们需要告诉它要使用哪些插件。 Babel在NPM项目的顶层的名为.babelrc的文件中查找此信息。

{
    "presets": ["es2015"],
    "plugins": ["transform-async-to-generator"]
}
{
    "presets": ["es2015"],
    "plugins": ["transform-async-to-generator"]
}

Now, copy the snippet from above with the Planet class into an index.js, and run babel index.js --out-file index.transpiled.js --source-maps. This will create a transpiled version of index.js, in index.transpiled.js, and a separate sourcemap file, index.transpiled.js.map. A source map is a file that tells the browser which lines of your transpiled code correspond to which lines of your original source, so you can debug index.js directly.

现在,从上方将Planet类的代码片段复制到index.js ,然后运行babel index.js --out-file index.transpiled.js --source-maps 。 这将创建一个transpiled版本index.js ,在index.transpiled.js ,和一个单独的文件sourcemap, index.transpiled.js.map 。 源映射是一个文件,该文件告诉浏览器转码的哪些行与原始源的哪些行相对应,因此您可以直接调试index.js 。

If you want to transpile your file every time you save changes, you can run:

如果要在每次保存更改时转换文件,则可以运行:

babel index.js --out-file index.transpiled.js --source-maps --watch

babel index.js --out-file index.transpiled.js --source-maps --watch

制作工具 ( Build Tools )

There are several other options you can use with the Babel CLI. But, as you can probably imagine, figuring out how to configure them, on top of whatever other tools you're using, can become unwieldly.

Babel CLI可以使用其他几个选项 。 但是,您可能会想到,在您正在使用的任何其他工具之上弄清楚如何配置它们可能会变得毫无用处。

For us, running babel --watch is enough. But for any nontrivial project, you'll have a number of build steps: Transpiling, minifiying, organizing your outputs for distribution, determining what needs to be updated, optimizing images . . . It's as much of a nightmare as it sounds like.

对我们来说,运行babel --watch就足够了。 但是对于任何不平凡的项目,您将有许多构建步骤:编译,最小化,组织输出以进行分发,确定需要更新的内容,优化图像。 。 。 听起来就像一场噩梦。

If you use the CLI directly, you'd have to run through each step manually, or script the process yourself. That second option is definitely viable, but it can be:

如果直接使用CLI,则必须手动执行每个步骤,或者自己编写脚本。 第二种选择绝对是可行的 ,但可以是:

  1. Brittle;
  2. Difficult for teams; and
  3. Unportable across platforms.

This is where build tools come in. Grunt and Gulp are popular systems that streamline orchestration of the build process. Bundlers and module loaders like Webpack, JSPM, and Browserify -- which you can read about in another article of mine -- take care of transpilation alongside all of the other magic they work.

这就是构建工具的用武之地。Grunt和Gulp是流行的系统,可简化构建流程的编排。 捆绑程序和模块加载程序(例如Webpack , JSPM和Browserify) (您可以在我的另一篇文章中阅读)会与它们工作的所有其他魔术一起处理转堆。

I've used Browserify to transpile tests written in CoffeeScript to plain JS; delete the output after the tests run; and bundle my code with Babel if they all pass; and put everything in the right place, with source maps, along the way. That's all possible using the command-line tools directly, but it's brittle.

我已经使用Browserify将以CoffeeScript编写的测试转换为纯JS。 测试运行后删除输出; 并把我的代码和Babel捆绑在一起; 并一路将所有内容和原始地图放在正确的位置。 可以直接使用命令行工具来完成所有操作,但这很脆弱。

You can trust me on that -- I used to use bash script for this. That Linux diehard coming back to bite me. And if you ever find yourself needing to load multiple module formats or wanting to experiment with hot module replacement, you don't have much choice but to turn to a build system.

您可以相信我-我曾经为此使用bash脚本。 那个Linux顽固派回来咬我。 而且,如果您发现自己需要加载多种模块格式或想尝试热模块更换 ,则别无选择,只能求助于构建系统。

Unless you like to roll your own glue code, of course.

当然,除非您喜欢自己编写胶水代码。

结论 ( Conclusion )

It's hard to get away from Transpilers: You need them whether you plan to write a language that compiles to JavaScript, or simply use ES015 reliably. We saw that feedback from people in that latter group is an important source of guidance for the TC39 committee in its decisions about the future direction of JavaScript, as well.

很难摆脱Transpilers:无论您打算编写要编译为JavaScript的语言,还是可靠地使用ES015,您都需要它们。 我们看到,来自后者的人们的反馈意见对于TC39委员会在决定JavaScript的未来方向方面也具有重要的指导意义。

We took a look at using the Babel CLI to transpile ES2015 with source maps, and learned about tools that streamline transpilation in more complicated projects.

我们了解了如何使用Babel CLI将ES2015与源映射一起进行转换,并了解了可简化更复杂项目中的转换的工具。

Jame Kyle's Babel Handbook is a go-to for everything Babel, and if you're interested in Webpack, read the How-To before wading through its . . . Erm, "suboptimal" documentation.

Jame Kyle的《 Babel手册》是Babel的入门指南,如果您对Webpack感兴趣,请在仔细阅读How-To之前先阅读一下。 。 。 Erm,“次优”文档。

Feel free to ask question in the comments, or shoot them to me on Twitter (@PelekeS) -- I'll be sure to get back to everyone individually.

随时在评论中提问,或在Twitter( @PelekeS ) 上将它们枪杀给我-我一定会单独回信给每个人。

翻译自: https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them

javascript编译器