クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【JS】BabelでESをコンパイルする

JSコンパイル系の続きで今回はBabelです。前回はAltJSのTypeScriptをGulpでコンパイルしました。今回のBabelはESの新しい書式を従来の書式(ES5)に変換します。AltJSではなく正規なJSでちょっと書き方が新しいだけだし、Babel自体にコンパイル機能があるためGulpも使いません。それでは行きましょう!(ESメソッドを適用させるポリフィルについても追記しました!)

【目次】

前回記事
※参考:【JS】TypeScriptをGulpでコンパイルしてみる - クモのようにコツコツと

Web開発環境まとめ
qiita.com

Babelとは何か

こちらの解説がわかりやすい。

※参考:Babelの手ほどき | 前編 Babelとは | CodeGrid

Babel(バベル)*は、次世代のJavaScriptの標準機能を、ブラウザのサポートを待たずに使えるようにするNode.js製のツールです。次世代の標準機能を使って書かれたコードを、それらの機能をサポートしていないブラウザでも動くコードに変換(トランスパイル)します。

そしてBabelはなんと、厳密に言うとAltJSではなかった。

BabelとAltJS

Babelはコードを変換するという点では、CoffeeScriptやTypeScript*といった、JavaScriptのコードを生成するAltJSの仲間と言えるかもしれません。

しかしながら…

Babelを使う際に書くのは、AltJSではなく純粋なJavaScriptです*。新しい機能や構文を使って書いたJavaScriptを、現状のブラウザでも動くJavaScriptへと変換します。

CodePenの「JavaScript Preprocessor」にBabelもあったのでAltJSの一種かと思っていた。

ちなみにプリプロセッサとは…

プリプロセッサとは、ソフトウェアの役割による分類の一つで、ある中心的な処理を行うプログラムに対して、その前処理(preprocess)を行うプログラムのこと。プログラミング言語のコンパイラの前処理を行うものが非常に有名。

※参考:プリプロセッサ(プリコンパイラ)とは - IT用語辞典 e-Words

TyepeScriptはJSのメタ言語(代替言語)のため拡張子が.ts。BabelはJSの標準仕様のため拡張子は.jsのままだった。

ESとは何か

ESはEcmaScriptの略

JSの標準仕様なのになぜコンパイルが必要なのか。それはJSの新しい仕様にまだ対応していないブラウザがあるため。

以前の記事でも触れた様にJSの標準仕様は「ES」という。

※参考:周回遅れでもWebGLを事始める!canvas大地に立つ!! - クモのようにコツコツと

ESは「ECMAScript」の略称

ECMAScript(エクマスクリプト)は、JavaScriptの標準であり、Ecma Internationalのもとで標準化手続きなどが行われている。

※参考:ECMAScript - Wikipedia

ESを策定している「Ecma International」とは何か

Ecmaインターナショナル(エクマ・インターナショナル、英: Ecma International)は情報通信システムの分野における国際的な標準化団体。

※参考:Ecmaインターナショナル - Wikipedia

脚注によると「ECMA」は「European Computer Manufacturers Association」の略で「欧州電子計算機工業会」という意味。今は略語ではなくなったため、大文字では書かず「Ecma」が正式名称になったそうな。

ESのバージョン

このESのバージョンが今は毎年改定されていてブラウザの対応がなかなか追いつかない。

6th editionから、「ECMAScript 2015」仕様の名称に発行年が付加されることになった。以降、ECMAScriptは毎年改訂されることになり、以降特定の版を指す場合は、edition名ではなく年号つきの仕様書名で呼ばれることが推奨されている[2]。

※参考:ECMAScript - Wikipedia

「ES6(ES2015)」の前の「ES5」は2009年、「ES5.1」は2011年に改定されたため、今ではほとんどのブラウザが対応している。

ES6からは便利な機能がかなり追加された。

  • letとconst
  • カーリーブラケット{}によるブロックスコープ
  • アロー関数
  • Class構文
  • 関数のデフォルト引数
  • 分割代入
  • テンプレート文字列
  • スプレッド演算子...
  • for...of
  • Promise

※参考:ES2015(ES6) 入門 - Qiita

なので、みんな使いたい。そのため開発時はES6〜の仕様でJSを書いて、公開時にはBabelでES5にコンパイルをするわけだ。

ES6(ES2015)のブラウザ対応状況

なお、ES6のブラウザ対応状況はこちら。

※参考:ECMAScript 6 compatibility table

以前はChromeとFireFox以外はあまり対応してくれていなかった様だが、さすがに5年も経っているのでSafariやEdgeなども対応してくれている様だ。ただ、IEはもうホントどーしようもない。早く市場から消え去って欲しい。

IEが標準ブラウザなOS「Windows7」のサポート終了は2020年1月14日。いよいよカウントダウンが始まっている!時が来た!!!!

※参考:Windows 7 のサポート終了情報 - Microsoft

Babelのコンパイル環境を準備する

ということで早速バベりたい。

CDNリンク

Babelのコンパイルについて調べるとGulpやWebPackと連携する方法が多いが、まずはBabel単体に標準で付いているコンパイル機能がどんなものか体験する。こちらの記事がわかりやすく、参考にさせていただきました!

※参考:Babelの使い方入門編|ES6をコンパイル(JavaScript)

ちなみに、先に書いた様にBabelは.jsファイルの中身を変換するのみのため、なんとCDNのリンクでブラウザ(クライアントサイド)のみでも変換が可能らしい!headタグの中に下記をリンクする。

<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

※参考:GitHub - babel/babel-standalone: Now located in the Babel repo! Standalone build of Babel for use in non-Node.js environments, including browsers.

Babelインストール前の準備

node.jsとnpmは入っている前提(下記のコマンドでバージョン番号が出る)

node -v
npm -v

Babelをインストールするフォルダを作る。今回はbabel_testという名前にした。

ターミナルを開いてcdコマンドでフォルダに移動する。

cd  /(フォルダ)/babel_test

package.jsonを作成する。

npm init -y

ここまではTypeScriptの時と同じですな。

※参考:【JS】TypeScriptをGulpでコンパイルしてみる - クモのようにコツコツと

babel-cliをインストール

この次、今回はGulpは入れない。

babel-cliというツールをローカルでインストールする。

npm install -D babel-cli

成功するとpackage.jsondevDependenciesにbabel-cli`が記載される。

 "devDependencies": {
    "babel-cli": "^6.26.0"
  }

babel-preset-envをインストール

Babelの設定ファイルである.babelrcを作成する。

なお、ドット付きのファイルは「非表示ファイル」のため、ファインダーで表示されない場合は「command + shift + . 」で表示する。

※参考:Macのショートカットキー - 不可視ファイルの表示/非表示を切り替える - PC設定のカルマ

次にbabel-preset-envというプリセットをローカルでインストールする。

npm install babel-preset-env -D

参考記事ではbabel-preset-es2015babel-preset-envのいずれかを入れる、とあった。

babel-preset-es2015は「ES2016」を「ES2015(ES6)」にコンパイルしてくれるプリセットだが、公式サイトを見るとそれに変わる機能としてbabel-preset-envを推奨している様だ。

※参考:babel-preset-es2015 -> babel-preset-env · Babel

毎年改定されるES20xxの内容をこのプリセットが判断してくれるっぽい。

※参考:babel-preset-envを簡単にさわってみた。 - Qiita

成功するとまたpackage.jsonに追記される。

 "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-env": "^1.7.0"
  }

.babelrcに設定を書き込む。これだけでも大丈夫っぽいが…

{
  "presets": ["env"]
}

念のため、参考記事にある様にブラウザの詳細設定も追記する。

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      }
    }]
  ]
}
  • presetsの第二引数として連想配列targetsキーを入れる
  • targetsキーの中にさらに連想配列browsersキーを入れる。
  • browsersの値は配列で一つ目の値に相対的なバージョン、二つ目の値にSafariのバージョン

データはJSON形式のようだ。

コンパイルするjsファイルを準備

以前のAltJS記事のおさらい

コンパイルしたいjsファイルを作る。以前、AltJS記事で作ったBabelのCodePenを使う。

See the Pen Babel practice 1 by イイダリョウ (@i_ryo) on CodePen.

※参考:【CoffeeScript, LiveScript, TypeScript, Babel】AltJS 事始め - クモのようにコツコツと

HTMLファイル、css、jsフォルダ作成

プロジェクトフォルダ直下にindex.htmlを作る。

HTMLコード

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Babel事始め</title>
<link rel="stylesheet" href="css/style.css">
</head>

<body>
<section id="coffee">
  <h1>Babel事始め</h1>
  <p>CodePen Settingの旅、JS編第4回。JS編の最後を飾るBabelはブラウザがまだ対応していない最新のESに準拠した記述ができるAltJSとのこと。
  </p>
  <section id="hello">
    <h2>特徴</h2>
    <ul>
      <li>最新のESに準拠</li>
      <li>変数にletやconstなどが使える</li>
     <li>アロー関数式「=>」が使える</li>
      <li>クラス定義ができる</li>
    </ul>
    <p>Babelも素のJSがそのまま行けるようなのでJSからの書き換えもしやすそう。</p>
    <p><small>※letは再宣言が出来ない。<br>
※constは再宣言も再代入もできない。<br>
 →いずれもvarより厳密だが、全てvarに置き換えても問題はない。
</small></p>
  </section>
  <div><button id="btn">ここ押せワンワン</button>
</section>
<script src="js/script.js"></script>
</body>
</html>

bodyの閉じタグの直前でscript.jsというファイルをリンクしている。これはBabelでコンパイル後のファイルの前提。

<script src="js/script.js"></script>

また、cssフォルダ、jsフォルダも作る。また、cssフォルダの中にcommon.cssファイルを作っている(コードはCodePen参照)

ブラウザで開いてみると「ここ押せワンワン」というボタンがある。 f:id:idr_zz:20200111205702p:plain
しかし、まだscript.jsが存在していないため、ボタンを押しても何も起こらない。

Babelコンパイル前のファイル

jsフォルダの中にBabelコンパイル前のファイルを作る。babel_test.jsという名前にする。

こんなファイル構成。

f:id:idr_zz:20200111205420p:plain

コンパイル前(babel_test.js

JSコード

/*関数
function hellow(){
window.alert('hellow TypeScript');
window.alert('こんにちは、型スクリプト');
window.alert('おしまい');
}*/

/*アロー関数*/
var hellow = () => {
window.alert('hellow Babel');
window.alert('こんにちは、バベル');
window.alert(title + 'は第' + num + '回です。');
window.alert('おしまい');
}



/*変数
var btn = document.getElementById('btn');*/

/*最新の変数*/
const btn = document.getElementById('btn');

let title = 'JS編';

var num = 4;

/*イベント*/
btn.addEventListener('click', hellow, false);

Babelコンパイル実行!

それでは、いよいよコンパイルを実行する!

./node_modules/.bin/babel js/babel_test.js -o js/common.js
  • ./node_modules/.bin/babelを実行
  • js/babel_test.jsjs/common.jsにコンパイルする

別ファイルにコンパイルしたい場合は-oで別名を指定する。付けない場合は上書きされる。

やた!common.jsが作られた!

f:id:idr_zz:20200111205835p:plain

コンパイル後(common.js

JSファイル

'use strict';

/*関数
function hellow(){
window.alert('hellow TypeScript');
window.alert('こんにちは、型スクリプト');
window.alert('おしまい');
}*/

/*アロー関数*/
var hellow = function hellow() {
  window.alert('hellow Babel');
  window.alert('こんにちは、バベル');
  window.alert(title + 'は第' + num + '回です。');
  window.alert('おしまい');
};

/*変数
var btn = document.getElementById('btn');*/

/*最新の変数*/
var btn = document.getElementById('btn');

var title = 'JS編';

var num = 4;

/*イベント*/
btn.addEventListener('click', hellow, false);
  • アロー関数(=>)が従来のfunctionの関数になっている。
  • 変数のconstletも全てvarになっている。

ブラウザをふたたび開く。

common.jsがリンクになっているので… f:id:idr_zz:20200111210401p:plain
ボタンを押すとアラートが表示された!

IE未対応メソッドにはポリフィル必要!(追記)

(※2021/05/01追記)

Babelを使えばIE気にせずモダンなJS書ける〜♪と思ってたのだが…どうやらBabelがコンパイルしてくれるのはJSの文法的な部分(アロー関数、const変数など)だけのようだ。。

JSリファレンスのポリフィル

例えば文字列(String)が要素に含まれるかを探すincludes()メソッド。ES2015から追加されたメソッドでIEは未対応。

f:id:idr_zz:20210501092201j:plain

※参考:String.prototype.includes() - JavaScript | MDN

このメソッドはBabelコンパイル後もそのままのため、IE上ではコンソールエラーになる。。


これを防ぐにはポリフィルを追記する必要がある。JSリファレスを調べるとポリフィルが載っていたりする。

if (!String.prototype.includes) {
  String.prototype.includes = function(search, start) {
    'use strict';

    if (search instanceof RegExp) {
      throw TypeError('first argument must not be a RegExp');
    }
    if (start === undefined) { start = 0; }
    return this.indexOf(search, start) !== -1;
  };
}

これは「もしString.prototypeincludesがなければ変わりに別の処理を行う」という内容で、includes対応ブラウザ(=IE以外のブラウザ)では動かない。

※参考:String.prototype.includes() - JavaScript | MDN


しかし!リファレンスにポリフィルが必ず書かれてるとは限らないようだ。例えば配列(Array)用のincludes()メソッドにはポリフィルが掲載されていなかった。

※参考:Array.prototype.includes() - JavaScript | MDN

調べると個人の方がポリフィルを書いてくれていたりする!

※参考:【JavaScript】Arrayにincludesをポリフィルしよう | レコチョクのエンジニアブログ

とてもありがたいことだが、リファレンスで全部のポリフィルを用意してくれればいいのにと思ふ。

また、そもそもincludes()メソッドを使うたびにポリフィルをコードに追加するのもなかなか手間な作業ではある。。

polyfill-io

ポリフィル対策は他にもいくつかある。

「polyfill-io」上で該当するメソッドのポフィリスのURLを取得してリンクする方法。

※参考:【Polyfill.io】を使用してJavascriptのブラウザ互換を解決する|日々、アップデート
※参考:Polyfill.io

先ほどの配列用includes()のリンクを取得して

f:id:idr_zz:20210501091714j:plain

ページ上にリンクを追加する

<script src="https://polyfill.io/v3/polyfill.min.js?features=Array.prototype.includes"></script>

npmライブラリのポリフィル

JSコードの中にポロフィルを含みたい場合はnpmで探すとライブラリが見つかるのでインストールするといい。

先ほどの配列用includes()を調べると下記が出てくる。

※参考:polyfill-array-includes - npm

ライブラリをインストールして

$ npm install polyfill-array-includes

モジュールでインポートするとIEでもincludes()メソッドが動く。

$ import 'polyfill-array-includes';

コンパイルするとこのコードも含まれて1ファイルになる。モジュール開発している場合は「polyfill-io」のリンクよりこの方法の方が望ましい。

まあ個人的にはIEは対象外にするのが一番望ましくはあるw(2021/05/01現在、PCブラウザでのIEのシェアは6.91%)

※参考:Desktop Browser Market Share Japan | StatCounter Global Stats

最後に

ということで、Babelを使ってES6のコードをES5にコンパイルしました!Babelは自動コンパイル設定をしたり、GulpやWebPackと連携してコンパイルすることもできる様です。おいおいトライしていこうと思います。

なお、以前にも触れた通りGulp、Babel、Webpackの3つがフロント開発3銃士と呼ばれている様です。

※参考:gulpとbabelとwebpackというフロント開発3銃士 - Qiita

そのうち2つに触れたことになりますが、最後のWebPackがまだ未体験のため、次回トライしてみたく思います。それではまた!


Web開発環境まとめ
qiita.com