<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://organizeseries.com/"
	>

<channel>
	<title>古川陽介 &#8211; HTML5Experts.jp</title>
	<atom:link href="/yosuke_furukawa/feed/" rel="self" type="application/rss+xml" />
	<link>https://html5experts.jp</link>
	<description>日本に、もっとエキスパートを。</description>
	<lastBuildDate>Sat, 07 Jul 2018 03:14:05 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.7.19</generator>
	<item>
		<title>Electronプログラミング入門 — インストールからミニブラウザ構築まで</title>
		<link>/yosuke_furukawa/20841/</link>
		<pubDate>Thu, 15 Sep 2016 00:00:50 +0000</pubDate>
		<dc:creator><![CDATA[古川陽介]]></dc:creator>
				<category><![CDATA[最新動向]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[Chromium]]></category>
		<category><![CDATA[Electron]]></category>
		<category><![CDATA[Node.js]]></category>

		<guid isPermaLink="false">/?p=20841</guid>
		<description><![CDATA[連載： Web技術でアプリ開発2016 (6)Electronとは、デスクトップクライアントを作るためのフレームワークです。クロスプラットフォームで動作することをサポートしているため、Electronで作ったアプリケーシ...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/web-based-apps-2016/" class="series-391" title="Web技術でアプリ開発2016" data-wpel-link="internal">Web技術でアプリ開発2016</a> (6)</div><div id="attachment_20845" style="width: 650px" class="wp-caption aligncenter"><img src="/wp-content/uploads/2016/09/electron-eyecatch-640x361.png" alt="Electron" width="640" height="361" class="size-large wp-image-20845" srcset="/wp-content/uploads/2016/09/electron-eyecatch.png 640w, /wp-content/uploads/2016/09/electron-eyecatch-300x169.png 300w, /wp-content/uploads/2016/09/electron-eyecatch-207x117.png 207w" sizes="(max-width: 640px) 100vw, 640px" /><p class="wp-caption-text">Electron</p></div>

<p>Electronとは、デスクトップクライアントを作るためのフレームワークです。クロスプラットフォームで動作することをサポートしているため、Electronで作ったアプリケーションはMac、Windows、Linuxの環境でも動作します。Atomと呼ばれる GitHub社製のエディタがあります。 ElectronはAtomを作る際にフレームワークとして作られました。以前はAtomShellと呼ばれていましたが、Electronとして名前を変更し、2016年にはversion 1.0がリリースされるまでに成長しました。</p>

<p>ElectronはJavaScript / HTML / CSSを使ってクライアントアプリケーションを作成します。中のアーキテクチャはChromiumとNode.jsで作られており、Web開発の技術を使ってデスクトップアプリケーションを構築することが可能です。</p>

<p>ElectronはCheng Zhao氏 (以降zcbenz)が開発したフレームワークですが、zcbenz氏は実際Electronの開発前にNW.js(旧 NodeWebkit) と呼ばれるフレームワークのコントリビューターでした。ElectronはNW.jsと非常によく似たフレームワークですが、いくつか技術的に異なるポイントが有ります。決定的な違いは、Chromiumの組み込み方の違いです。ElectronはライブラリとしてChromiumを組み込んでいるのに対して、NW.jsはChromiumをforkしたプロジェクトを使っています。Chromiumは非常に進化が早いプロダクトなので、forkして持つよりもアップデートを考えると効率的です。</p>

<p>とはいえ、NW.jsとElectronの技術的な部分以外は違いはそこまでありません。Atom開発当初にNW.jsが多少不安定だったために新しいプロダクトとして立ち上げたという側面もあるそうです。詳しくは<a href="https://github.com/electron/electron/blob/master/docs/development/atom-shell-vs-node-webkit.md" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">AtomShellとNodeWebkitの違い</a>に詳しく記載されています。</p>

<h2>Electronの特徴</h2>

<p>いくつか特徴があるので紹介します。</p>

<h3>Electronのランタイム</h3>

<p>Electron そのものはただのランタイムライブラリです、Node.jsにおける<code>node</code>コマンドのようなもので、<code>electron</code>コマンドでエントリポイントとなるJavaScriptを実行します。</p>

<p><code>electron</code>コマンドは<a href="http://electron.atom.io/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">公式サイト</a>からダウンロードすることもできますが、Node.jsのパッケージモジュールである、<code>npm</code>コマンドを使ってインストールすることも可能です。</p>

<p></p><pre class="crayon-plain-tag">$ npm install electron -g</pre><p></p>

<p>※ 以前までは <code>npm install electron-prebuilt</code>からインストールする必要がありましたが、最近は<code>npm install electron</code>でinstallできるようになりました。</p>

<p>これで<code>electron</code>コマンドが有効になるので、そのコマンドを使ってデスクトップアプリケーションを起動させます。アプリケーションを実際に書くのは後述します。</p>

<h3>Electron / Browser間でモジュールを共有</h3>

<p>Electronアプリを構築すると、シームレスにブラウザからNode.jsのコードを呼ぶことが可能です。そのため、下記のようなコードを実行することもできます。　</p>

<p></p><pre class="crayon-plain-tag">// script tag から
&lt;script&gt;
// 自分のローカルファイル読みこんだり
const fs = require(‘fs’);
fs.readFile(‘foo/bar/baz’, (err, data) =&gt; {
  console.log(data);
});
&lt;/script&gt;

&lt;script&gt;
// 外部プロセスを呼んだり
const cp = require(‘child_process’);
cp.exec(‘ls -l’, (err, stdout) =&gt; {
  console.log(stdout);
});
&lt;/script&gt;</pre><p></p>

<p>ただし、この方法を使った場合、DOM-based XSSが発生すると、任意のコマンドだったり、ファイルが操作できてしまう結果になるため、プロダクションでElectronを活用する場合は注意が必要です。</p>

<p><a href="http://utf-8.jp/public/2016/0307/electron.pdf" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">参考資料: Electronの倒し方</a></p>

<h2>Hello World</h2>

<p>一旦Electronを起動してみましょう。Electronを動かすだけなら Node.jsはbuilt-inされているので不要ですが、npmがある方が便利なのでNode.jsをインストールしておきましょう。Node.jsは <a href="https://nodejs.org/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">公式サイト</a>からダウンロードできます。Macであれば、<code>brew</code>でもインストール可能です。</p>

<p>Node.jsがインストールされたらElectronをインストールしてみましょう。下記の通りです。</p>

<p></p><pre class="crayon-plain-tag">$ npm install electron -g</pre><p></p>

<p>適当なフォルダを作成し、<code>package.json</code>、<code>main.js</code>、<code>index.html</code>を作成します。
package.jsonは下記の通りに作成します。</p>

<p></p><pre class="crayon-plain-tag">{
  "name": "electron-intro",
  "version": "0.0.1",
  "main": "main.js"
}</pre><p></p>

<p><code>main.js</code>を記述します。</p>

<p></p><pre class="crayon-plain-tag">const {app, BrowserWindow} = require('electron');

// window objectがGCされないようにするために、globalに定義する
let win;

function createWindow () {
  win = new BrowserWindow({width: 800, height: 600});

  win.loadURL(`file://${__dirname}/index.html`);

  win.on('closed', () =&gt; {
    // windowがクローズされたら null にして削除
    win = null;
  });
}

app.on('ready', createWindow);

app.on('window-all-closed', () =&gt; {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () =&gt; {
  if (win === null) {
    createWindow();
  }
});</pre><p></p>

<p>最後に表示するための<code>index.html</code>を記述します。</p>

<p></p><pre class="crayon-plain-tag">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Hello Electron!&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello Electron!&lt;/h1&gt;
    Node version: &lt;script&gt;document.write(process.versions.node)&lt;/script&gt;,
    Chrome version: &lt;script&gt;document.write(process.versions.chrome)&lt;/script&gt;,
    Electron version: &lt;script&gt;document.write(process.versions.electron)&lt;/script&gt;.
  &lt;/body&gt;
&lt;/html&gt;</pre><p></p>

<p>これだけでHello Worldは一旦完成です。 下記のようなファイル構成になっていることを確認してください。</p>

<pre><code>.
├── index.html
├── main.js
└── package.json
</code></pre>

<p>Electronを起動させてみましょう。</p>

<p></p><pre class="crayon-plain-tag">$ electron .</pre><p></p>

<p>下記のようなウィンドウが出たら完成です。</p>

<div id="attachment_20847" style="width: 650px" class="wp-caption aligncenter"><img src="/wp-content/uploads/2016/09/hello_world-640x477.png" alt="Hello, World実行結果" width="640" height="477" class="size-large wp-image-20847" srcset="/wp-content/uploads/2016/09/hello_world.png 640w, /wp-content/uploads/2016/09/hello_world-300x224.png 300w, /wp-content/uploads/2016/09/hello_world-207x154.png 207w" sizes="(max-width: 640px) 100vw, 640px" /><p class="wp-caption-text">Hello, World実行結果</p></div>

<h2>PhotonKitを使ってミニマムブラウザを作る</h2>

<p>これだけだと味気ないので、PhotonKitを使ってミニマムブラウザを作ってみましょう。ブラウザを作ると言っても、 Chromiumを内包しているElectronであれば、Chromiumの機能を借りてくるだけなのでそこまで難しくはないです。</p>

<p>PhotonKitはCSSフレームワークの1つです。BootstrapやMaterial Design Liteのようなclass setを持っています。Mac のクライアントのようなアプリケーションを作るためのデザインテンプレートになっています。</p>

<div id="attachment_20849" style="width: 650px" class="wp-caption aligncenter"><img src="/wp-content/uploads/2016/09/photon-640x353.png" alt="Photon" width="640" height="353" class="size-large wp-image-20849" srcset="/wp-content/uploads/2016/09/photon.png 640w, /wp-content/uploads/2016/09/photon-300x165.png 300w, /wp-content/uploads/2016/09/photon-207x114.png 207w" sizes="(max-width: 640px) 100vw, 640px" /><p class="wp-caption-text">Photon</p></div>

<p>まずは<code>package.json</code>を作りましょう。</p>

<p></p><pre class="crayon-plain-tag">{
  "name": "electron-mini-browser",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "electron index.js"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "electron": "^1.3.5"
  }
}</pre><p></p>

<p><code>npm start</code>でElectronを起動できるようにしておくことと、<code>dependencies</code>に<code>electron</code>をインストールしておきましょう。下記の方法でpackage.jsonに記述しつつ、インストールさせることが可能です。　</p>

<p></p><pre class="crayon-plain-tag">$ npm install electron --save</pre><p></p>

<p>次に<code>index.js</code>を作成します。</p>

<p></p><pre class="crayon-plain-tag">'use strict';
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;

let mainWindow = null;

app.on('window-all-closed', function() {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('ready', function() {
  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
  });
  mainWindow.loadURL(`file://${__dirname}/index.html`);

  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});</pre><p></p>

<p>実際のページ(index.html)を作成します。</p>

<p></p><pre class="crayon-plain-tag">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;link rel="stylesheet" href="./css/photon.css"&gt;
    &lt;script src="./js/main.js"&gt;&lt;/script&gt;
    &lt;title&gt;Hello Electron!&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div class="window"&gt;
    &lt;header class="toolbar toolbar-header"&gt;
    &lt;h1 class="title"&gt;Hello Electron!&lt;/h1&gt;
    &lt;div class="toolbar-actions"&gt;
     &lt;!-- リロードボタン --&gt;
     &lt;button id="reload" class="btn btn-default"&gt;
       &lt;span class="icon icon-arrows-ccw icon-text"&gt;&lt;/span&gt;
       Reload
     &lt;/button&gt; 
     &lt;!-- 戻るボタン --&gt;
     &lt;button id="back" class="btn btn-default"&gt;
       &lt;span class="icon icon-left icon-text"&gt;&lt;/span&gt;
       Back
     &lt;/button&gt; 
     &lt;!-- 進むボタン --&gt;
     &lt;button id="forward" class="btn btn-default"&gt;
       &lt;span class="icon icon-right icon-text"&gt;&lt;/span&gt;
       Forward
     &lt;/button&gt; 
     &lt;!-- URL バー --&gt;
     &lt;input type="text" id="urlbar" class="form-control" placeholder="URL" value="https://github.com/"&gt;
     &lt;!-- お気に入りボタン --&gt;
     &lt;button id="favorite" class="btn btn-default"&gt;
       &lt;span class="icon icon-star icon-text"&gt;&lt;/span&gt;
       Favorite
     &lt;/button&gt; 
    &lt;/div&gt;
    &lt;/header&gt;
      &lt;div class="window-content"&gt;
        &lt;div class="pane-group"&gt;
          &lt;div class="pane-sm sidebar"&gt;
            &lt;!-- お気に入りリスト --&gt;
            &lt;ul id="fav-list" class="list-group"&gt;
            &lt;/ul&gt;
          &lt;/div&gt;
            &lt;!-- Webページ表示領域 --&gt;
          &lt;webview class="pane" id="webview" src="https://www.github.com/" autosize="on" style="height:100%;"&gt;&lt;/webview&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;footer class="toolbar toolbar-footer"&gt;
    &lt;h1 class="title"&gt;Footer&lt;/h1&gt;
    &lt;/footer&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre><p></p>

<p>ページの中のボタンに動きを与えるため、<code>js/main.js</code>を作成します。</p>

<p></p><pre class="crayon-plain-tag">document.addEventListener('DOMContentLoaded', () =&gt; {
  const webview = document.getElementById('webview');
  const reloadButton = document.getElementById('reload');
  const backButton = document.getElementById('back');
  const forwardButton = document.getElementById('forward');
  const favoriteButton = document.getElementById('favorite');
  const urlbar = document.getElementById('urlbar');
  const favList = document.getElementById('fav-list');

  // webview表示の時にurlbarの値を変える
  webview.addEventListener('load-commit', ({ url, isMainFrame }) =&gt; {
    if (isMainFrame) {
      urlbar.value = url;
    }
  });

  // urlbarでEnterキーを押したら遷移する
  urlbar.addEventListener('keypress', (e) =&gt; {
    if (e.key === 'Enter') {
      webview.setAttribute('src', urlbar.value);
    }
  });

  // 更新ボタンをクリックしたらwebviewをリロードする
  reloadButton.addEventListener('click', () =&gt; {
    webview.reload();
  });

  // 戻るボタンをクリックしたらwebviewを戻る
  backButton.addEventListener('click', () =&gt; {
    webview.goBack();
  });
  
  // 進むボタンをクリックしたらwebviewを進ませる
  forwardButton.addEventListener('click', () =&gt; {
    webview.goForward();
  });
  
  // お気に入りボタンをタップしたらリストにURLを追加する
  favoriteButton.addEventListener('click', () =&gt; {
    const listItem = document.createElement('li');
    const listContent = document.createElement('p');
    listItem.setAttribute('class', "list-group-item");
    listItem.setAttribute('data-url', urlbar.value);
    listContent.textContent = urlbar.value;
    listItem.appendChild(listContent);
    favList.appendChild(listItem);
    listItem.addEventListener('click', () =&gt; {
      const url = listItem.getAttribute('data-url');
      webview.setAttribute('src', url);
    });
  });
});</pre><p></p>

<p>最後にPhotonKitのCSSとFontセットを <a href="http://photonkit.com/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ダウンロード</a>しておきます。
下記のようなディレクトリ構成になります。</p>

<p></p><pre class="crayon-plain-tag">.
├── css
│   ├── photon.css
│   └── photon.min.css
├── fonts
│   ├── photon-entypo.eot
│   ├── photon-entypo.svg
│   ├── photon-entypo.ttf
│   └── photon-entypo.woff
├── index.html
├── index.js
├── js
│   └── main.js
└── package.json</pre><p></p>

<p>全てを終えたら、<code>npm start</code>コマンドで起動します。下記のようなブラウザが出現できたら完成です。</p>

<div id="attachment_20846" style="width: 650px" class="wp-caption aligncenter"><img src="https://github.com/yosuke-furukawa/electron-intro/raw/master/img/electron-mini-browser.gif" alt="Electron+Photonで作ったミニブラウザ" width="640" height="374" class="size-large wp-image-20846" /><p class="wp-caption-text">Electron+Photonで作ったミニブラウザ</p></div>

<h2>プラットフォーム/ライブラリの特徴</h2>

<table>
<thead>
<tr>
  <th>項目</th>
  <th>説明</th>
</tr>
</thead>
<tbody>
<tr>
  <td>対応プラットフォーム</td>
  <td>Windows, Linux, OS X</td>
</tr>
<tr>
  <td>コードベースは（ほぼ）完全に統一できるか？</td>
  <td>ほぼ統一できる</td>
</tr>
<tr>
  <td>UIを記述する言語</td>
  <td>JavaScript, HTML, CSS</td>
</tr>
<tr>
  <td>UIはネイティブかウェブか</td>
  <td>基本はウェブだが、OSネイティブの機能(タスクバー等)は一部利用できる</td>
</tr>
<tr>
  <td>パフォーマンス</td>
  <td>基本ウェブなのでウェブページと同等、ただし自分でオフラインキャッシュの仕組みを持てるので、改善可能</td>
</tr>
<tr>
  <td>ネイティブな機能を呼び出せるか？</td>
  <td>Node.jsからOSの機能を呼び出せる。</td>
</tr>
</tbody>
</table>

<p>以下に、上の表を補足します。</p>

<h3>対応プラットフォーム</h3>

<p>Electronの公式サポートはWindows, Linux, OS Xの３つのみです。今のところはデスクトップアプリケーションのためのフレームワークなので、モバイル対応は全く考えられていません。</p>

<h3>コードベースは (ほぼ) 完全に統一できるか？</h3>

<p>ほぼ統一できます。ただし、OS X用の機能であったり、Windows専用の機能は用意されていて、プラットフォームによって呼び出し可能なAPIや受信可能なEventが若干異なります。クロスプラットフォームでOSの専用の機能を利用する上で、気になる方は一度<a href="http://electron.atom.io/docs/api/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">API</a>を確認すると良いでしょう。</p>

<h3>UIを記述する言語</h3>

<p>Hello Worldやミニブラウザを作って分かる通り、HTML/JS/CSSで書きます。ネイティブ部分の呼び出しもNode.jsなので、 JavaScriptになります。</p>

<p>JavaScriptのレイヤはNode.js部分とChromium部分でv8(JavaScript Engine)が動くので、ほぼ同一の動きをします、ただし、Node.jsとChromiumでv8のバージョンは若干違う可能性があります。基本的な部分は変わりませんが、ES2016の対応状況を見てもらえれば分かる通り、新しいJavaScriptの機能面で違いはあります。</p>

<div id="attachment_20844" style="width: 650px" class="wp-caption aligncenter"><img src="/wp-content/uploads/2016/09/compat-640x155.png" alt="ES2016の対応状況" width="640" height="155" class="size-large wp-image-20844" srcset="/wp-content/uploads/2016/09/compat.png 640w, /wp-content/uploads/2016/09/compat-300x73.png 300w, /wp-content/uploads/2016/09/compat-207x50.png 207w" sizes="(max-width: 640px) 100vw, 640px" /><p class="wp-caption-text">ES2016の対応状況</p></div>

<h3>UIはネイティブかウェブか</h3>

<p>基本はウェブです。デスクトップアプリケーションとして、タスクトレイに常駐させたい場合やOSネイティブのダイアログボックスを使いたい場合は専用のAPIがあるので、それを利用して機能を作ることも可能です。</p>

<h3>パフォーマンス</h3>

<p>Electronは基本的にウェブなので、ネイティブのデスクトップアプリと比較するとそこまで高速ではありません。</p>

<p>Electronのアプリを起動させると、Chromium用のプロセスが3つ起動し、Node.js用のメインプロセスが1つ起動します。合計4プロセスが常駐することになります。言ってしまえばブラウザそのものを起動しつつ、バックグラウンドにNode.jsを起動しているのと同様です。富豪的な方法で実現していると言えるでしょう。</p>

<p>そのため、描画パフォーマンス等はブラウザの機能がそのまま利用できますが、実行効率が良いモデルとはいえません。パフォーマンスにシビアなアプリケーションを作る場合は Electron ではなく、ネイティブの機能を使って作る方が良いでしょう。</p>

<h3>ネイティブな機能を呼び出せるか？</h3>

<p>ElectronからNode.jsを呼び出せばファイル操作や外部プロセスコールといった基本的な機能は呼び出せます。また、C言語等で書かれたネイティブライブラリもNode.jsアドオンがあれば呼び出すことが可能です。ただし、ネイティブライブラリに関しては、通常のnpmでインストールするだけでは利用できないことがあります。Nodeのバージョンがelectronのbuilt-inで保持しているバージョンと異なる場合にうまく利用できないことが多いです。ネイティブライブラリを利用したい場合は下記のガイドを確認することを推奨します。</p>

<p><a href="https://github.com/electron/electron/blob/master/docs/tutorial/using-native-node-modules.md" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ネイティブライブラリ利用ガイド</a></p>

<h2>まとめ</h2>

<p>Electronの概要とアプリ構築の方法、ウェブ用プラットフォームを構築する上での共通質問項目を記述しました。Electron はこれまでのAngularJSやReact等で構築されたSingle Page Applicationを活用して、デスクトップアプリケーションを構築するのに非常に向いています。</p>

<p>まだv1.0がリリースされて日が浅いこともあり、そこまでノウハウが溜まっているわけではありません。特にセキュリティやパフォーマンスなどの改善は今進んでいますが、ユーザーからのノウハウも溜める必要があると感じています。</p>

<p>もしまだ触っていない方がいらっしゃるのであれば、<a href="http://yosuke-furukawa.hatenablog.com/entry/2015/12/31/223045" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">electronica</a>などのチュートリアルもあるので是非やってみてください。</p>

<p>また今回の<a href="http://nodefest.jp/2016/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Node学園祭</a>では、Electron作者のzcbenzもゲストスピーカーとして登壇する予定です。Electronの今後の話が聞けると思います。</p>

<p>一緒にElectronを盛り上げていきましょう。</p>
]]></content:encoded>
		
		<series:name><![CDATA[Web技術でアプリ開発2016]]></series:name>
	</item>
		<item>
		<title>今からでも間に合う！Node.js v4＆v5は何が変わったか？</title>
		<link>/yosuke_furukawa/17791/</link>
		<pubDate>Tue, 01 Dec 2015 00:00:18 +0000</pubDate>
		<dc:creator><![CDATA[古川陽介]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[ES2015]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Node.js]]></category>

		<guid isPermaLink="false">/?p=17791</guid>
		<description><![CDATA[Node.js v4リリースに向けて とうとうメジャーバージョンアップにされたNode.jsである、Node.js v4がリリースされました。今回はこのNode.js v4がこれまでのNode.js v0.12やv0.1...]]></description>
				<content:encoded><![CDATA[<h1>Node.js v4リリースに向けて</h1>

<p>とうとうメジャーバージョンアップにされたNode.jsである、Node.js v4がリリースされました。今回はこのNode.js v4がこれまでのNode.js v0.12やv0.10と比較してどう違うのかを解説します。また、最新ではv5もリリースされていますので、合わせてお伝えしていきます。</p>

<h1>なんでいきなりv4なのか</h1>

<p>おそらく一番最初に抱く感想は、v0.10とかv0.12みたいな数字からv1.0を飛ばして、なんでいきなりv4.0がリリースされたのかという疑問だと思います。これにはio.jsというプロダクトが関係しています。</p>

<p>2014年の年末、io.jsというプロダクトが発表され、2015年の初めにv1.0がリリースされました。io.jsというのは Node.js のforkで別リポジトリによって実装されたプロダクトです。io.jsの詳細は<a href="http://yosuke-furukawa.hatenablog.com/entry/2014/12/25/104300" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">筆者のブログ</a>を確認してください。io.jsがリリースされた後、週次リリースという早いサイクルでリリースするモデルを確立し、8カ月間もの間リリースを続けてきました。結果として、io.jsはv3までリリースされています。</p>

<p>紆余曲折を経た後に、Node.jsはこのio.jsと再び統合されることになります。統合までの経緯について、興味がある読者の方は<a href="https://speakerdeck.com/yosuke_furukawa/dousitekounatuta-node-dot-jstoio-dot-jsfalsefen-lie-totong-he-falsexing-fang-korekaradoujin-hua-siteikufalseka" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">筆者のYAPCでの発表資料</a>を参考にしてください。</p>

<p>統合される際にio.jsの既存バージョンと競合しないようにした結果、Node.jsはv1.0からv3.0を飛ばしていきなりv4.0がリリースされることになりました。</p>

<h1>Node.js v4とNode.js v0.12の違い</h1>

<p>Node.js v4はio.js v3が元になっています。つまり、io.js v1からv3までにはいった機能が、そのままNode.js v4では利用可能です。筆者がまとめた内容を元に、Node.js v4で入った新機能や違いを解説します。<em>また、v5で入った機能は本資料の最後にまとめて紹介します。</em></p>

<h2>JavaScript Syntax/Built-in Object の違い</h2>

<p>Node.js v4は内部的にJavaScript実行エンジンである、V8のv4.5を採用しています。これにより下記の部分でJavaScript全体の機能が進化しました。</p>

<ul>
<li>ES2015のサポート範囲拡大</li>
<li>StrongScript等のJavaScriptの新機能対応</li>
<li>Intlオブジェクトのサポートによる国際化対応</li>
</ul>

<p>一つ一つ説明していきます。</p>

<h3>ES2015のサポート範囲拡大</h3>

<p>ES2015のサポート範囲がNode.js v0.12と比較して拡大しました。具体的には下記の機能がデフォルトで有効になっています。</p>

<ul>
<li>let/const等のblock scope (&#8220;use strict&#8221;が必須)</li>
<li>class構文(&#8220;use strict&#8221;が必須)</li>
<li>Map/Set/WeakMap/WeakSetといった新しいコレクションオブジェクト</li>
<li>Generator構文</li>
<li>Binary/Octalリテラル</li>
<li>Symbolオブジェクト</li>
<li>Template Stringリテラル</li>
<li>String.prototype.repeat</li>
<li>Symbol.toStringTag</li>
<li>拡張Objectリテラル</li>
<li>Unicodeリテラル</li>
<li>アロー関数</li>
</ul>

<p>ES2015の構文のサポート範囲が変わったことで今までNode.jsで記述していたJavaScriptは変わっていくだろうと予測されます。具体的にどのように変わるのか、<code>EventEmitter</code>を拡張して<code>MyEventEmitter</code>を作るというケースを参考に考えてみましょう。</p>

<p>v0.12までは<code>EventEmitter</code>を拡張する場合、以下のように記述していました。</p>

<p></p><pre class="crayon-plain-tag">'use strict';
var events = require('events');
var util = require('util');
var EventEmitter = events.EventEmitter;
var MyEventEmitter = function(data) {
  this.data = data;
};

// EventEmitterを継承する
util.inherits(MyEventEmitter, EventEmitter);

MyEventEmitter.prototype.intervalCheck = function() {
  var originalData = this.data;
  var _this = this;
  setInterval(function (){
    if (originalData !== _this.data) {
      _this.emit('change', new Date() + ': ' + _this.data);
      originalData = _this.data;
    }
  }, 1000);
};

module.exports = MyEventEmitter;</pre><p></p>

<p>この<code>MyEventEmitter</code>は、<code>intervalCheck</code>という内部的に<code>data</code>が変わっていないかどうかを1秒ずつ調べて変更があったら、<code>change</code>イベントを発火させるという動きを行っています。v4からは下記のように<code>ES2015</code>の構文を使って下記のように記述します。</p>

<p></p><pre class="crayon-plain-tag">'use strict';
// 変更されないようにするためにconstで変数宣言する
const events = require('events');
const EventEmitter = events.EventEmitter;

// util.inheritsではなく、class構文とextendsを使う
class MyEventEmitter extends EventEmitter {
  constructor(data) {
    this.data = data;
  }
  intervalCheck() {
    // 基本的にletで変数宣言する
    let originalData = this.data;
    // アロー関数を利用する。
    setInterval(() =&gt; {
      if (originalData !== this.data) {
        // Template String Literalで変数の情報を文字列に埋め込む
        this.emit('change', `${new Date()}: ${this.data}`);
        originalData = this.data;
      }
    }, 1000);
  }
}

module.exports = MyEventEmitter;</pre><p></p>

<p>ひとつ前のコードと比較すると大分コードが短く、シンプルに記述できるようになっていることがわかります。
<a href="http://yosuke-furukawa.hatenablog.com/entry/2015/11/05/192521" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Node.js v5.0では、<code>Spread call</code>や<code>new.target</code>などの新しい機能</a>も使えるようになっています。ただし、現時点ではまだ下記のような機能は有効になっていません。</p>

<ul>
<li>default params</li>
<li>rest params</li>
<li>tail call optimization</li>
<li>Proxy/Reflect</li>
<li>modules</li>
</ul>

<h3>StrongScript等のJavaScriptの新機能対応</h3>

<p>StrongScriptというのはES2015のコードを強制させ、JavaScriptの実行エンジンにとって親和性の高いコードを生成するための新しい試みです。 もともとJavaScriptには<code>use strict</code>をつけることで厳格モードというモードになりますが、この厳格モードをより強くしたモードです。</p>

<p>StrongScriptは<code>readability</code>(読みやすさ)の向上と<code>predictability</code>(予測しやすさ) の向上という2つの事を狙った試みであり、特に<code>predictability</code>が上がることでJavaScriptの実行エンジンフレンドリーになり、全体的に性能が上がる事が期待されます。</p>

<p></p><pre class="crayon-plain-tag">"use strong";

function foo() {
  //下記のコードは、StrongModeでは全てエラーになります。
  // varを利用する代わりにlet、もしくはconstを使いましょう。
  var obj1 = 'aaa'; // Restrict var (use let or const instead)

  // argumentsの代わりに、restパラメータを使いましょう
  let args = arguments; // Restrict arguments (use Rest Params …args)

  // ==の代わりに、strict equal ===を使いましょう
  if (obj1 == 'aaa') {}  // Restrict == (use strict equal ===)

  // if とか for のあとにうっかり ; を置くけど、空の式を置くのは間違いの元なのでやめましょう。
  if (obj1 === 'aaa'); // Restrict empty if and for

  // for-inの代わりに、for-ofを使いましょう。
  for (let n in [1,2,3]) // Restrict for-in (use for-of instead)

  // delete構文を使うなら、Map/Setを代わりに使いましょう。
  delete obj1.key // Restrict delete operator (use Map/Set instead)

  // undefined っていう名前の変数に何かを入れるのはやめましょう。
  let undefined = 'aaa'; // Restrict undefined binding

  // 未定義のプロパティに、勝手にアサインするのはやめましょう。
  let obj3 = { foo : 'aaa'};
  obj3.bar = '123'; // Restrict undefined property access 
}</pre><p></p>

<p>本コードを実行するためには、<code>"use strong"</code>ディレクティブを書くだけじゃなく、実行時のオプションとして<code>--strong_mode</code>オプションが必要です。</p>

<p></p><pre class="crayon-plain-tag">$ node --strong_mode weak.js</pre><p></p>

<p>StrongScriptの詳細については、以前まとめた資料があるのでご一読ください。<a href="https://speakerdeck.com/yosuke_furukawa/strongmode-and-soundscript" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">StrongScriptについて</a></p>

<h3>Intlオブジェクトのサポートによる国際化対応</h3>

<p>ECMAScript国際対応用のAPIが利用できるようになっています。ECMA-402と呼ばれる仕様で通称ESIntlと呼ばれています。これを利用することで例えば時刻表期の国際化対応や通貨表記の国際化対応ができるようになります。</p>

<p>ただしデフォルトでは利用できないので、ビルドするときにオプションを指定する必要があります。下記のようにオプションを指定して実行すると有効になります。</p>

<p></p><pre class="crayon-plain-tag">$ ./configure --with-intl=full-icu --download=all
$ make
$ make install</pre><p></p>

<p>これを使うと新たに<code>Intl</code>というbuilt-inオブジェクトがglobal空間に生成されて使えるようになります。文字列比較処理から見ていきましょう。文字列比較をする際に日本語にはカタカナやひらがなといった表記の違いとまた句読点といった区切りが存在します。</p>

<p>読みやすくするためには表記の違いを使い分けたり、句読点をしかるべき所に入れる必要がありますが、プログラミングで文字列をソートしたり、検索するときなどはこれらの区別が不要なときも多いです。こういう場合に利用するのが<code>Intl.Collator</code>というクラスです。利用方法は以下のとおり。</p>

<p></p><pre class="crayon-plain-tag">// 文字列比較系

console.log(new Intl.Collator('ja', {
  sensitivity: 'base'  // 文字の派生系を同値と見なします、カタカナとひらがなの区別もつけません。Ex.か==が , ぴ == ヒ, あ != い
}).compare('ハハ', 'パパ'));  // 0 つまり同値

console.log(new Intl.Collator('ja', {
  sensitivity: 'accent'  // 文字で発音が同じものを同値と見なします。baseの中で派生系は不等という指定です。Ex. は == ハ , ぴ != ひ, あ != い
}).compare('ハハ', 'はは'));  // 0 つまり同値

console.log(new Intl.Collator('ja', { 
  sensitivity: 'accent', 
  ignorePunctuation: true // 句読点を無視するかどうか、デフォルトはfalse 
}).compare('わたしは、ふるかわです', 'わたしはふるかわです')); // 0 つまり同値</pre><p></p>

<p>また、この他にも時刻表記(<code>Intl.DateTimeFormat</code>)や通貨表記(<code>Intl.Numberformat</code>)といった数字の表記を、国際化対応することが可能です。</p>

<p></p><pre class="crayon-plain-tag">// 日付フォーマット系
var date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
console.log(new Intl.DateTimeFormat('en').format(date)); // 12/20/2012
console.log(new Intl.DateTimeFormat('ja').format(date)); // 2012/12/20
console.log(new Intl.DateTimeFormat('ja-JP-u-ca-japanese').format(date)); // 平成24/12/20

// 数値フォーマット系
console.log(new Intl.NumberFormat('en').format(10000000)); // 10,000,000
console.log(new Intl.NumberFormat('ja', {style: 'currency', currency: 'JPY'}).format(10000000)); //￥10,000,000
console.log(new Intl.NumberFormat('en', {style: 'percent', }).format(0.230232)); // 23.023%</pre><p></p>

<h2>API 部分の変更</h2>

<p>実は今回のNode.js v4では、JavaScriptの文法部分での進化と内部でのリファクタリングは進んでいるものの、APIとして新しい機能はそこまで増えていません。追加された機能はいくつかありますが、主だったものとしては下記の通りです。</p>

<ul>
<li>Buffer#indexOfの追加</li>
<li>Simple Stream Constructionの追加</li>
<li>os.homeDir()が追加</li>
</ul>

<p>一つ一つ説明していきます。</p>

<h3>Buffer API の変更</h3>

<p>Node.jsには、<code>Buffer</code>と呼ばれるbinaryデータを扱うためのAPIが存在します。普通にNode.jsのライブラリを利用するときにはあまり意識していないかもしれませんが、<code>fs</code>から<code>readFile</code>した時の戻り値や<code>http</code>のPOSTメッセージを受け取った時は基本的に<code>Buffer</code>型の値を受け取っています。JavaScriptにはArrayBufferという似たオブジェクトが存在しますが、v4.0.0からはこの<code>ArrayBuffer</code>を<code>Buffer</code>が継承する形になりました。これによって<code>ArrayBuffer</code>で利用可能なAPIは<code>Buffer</code>でも利用可能になります。</p>

<p>またそれとは別に<code>Buffer</code>に<code>indexOf</code>メソッドが増えました。これによりわざわざ文字列に変換しなくても<code>Buffer</code>としてそのまま文字列を含んでいるかどうかを検索することができるようになります。</p>

<p></p><pre class="crayon-plain-tag">var fs = require('fs');

// ./foo.txt =&gt; yosuke furukawa
fs.readFile(__dirname + '/foo.txt', function(err, buf){
  console.log(buf.indexOf('furukawa')); // 7
});</pre><p></p>

<p>これまでは<code>indexOf</code>などの文字列検索をする場合、一旦<code>toString</code>を使って文字列を変換する必要がありました。これはNode.js内部では、<code>Buffer</code>型から<code>string</code>型への変換をしている上にさらにHeapメモリを消費してしまうため、メモリ効率的にも速度効率的にも悪いというデメリットが有りました。今回のv4.0からは<code>indexOf</code>メソッドが<code>Buffer</code>で利用可能になっているため、このデメリットを解消しています。また内部的にもこのメソッドを利用することで速度の向上が見られます。</p>

<p>また、<code>ArrayBuffer</code>で再実装された影響で以下のような<code>ArrayBuffer</code>をそのままコンストラクタに入れられるようになりました。</p>

<p></p><pre class="crayon-plain-tag">const Buffer = require('buffer').Buffer;
const ab = new ArrayBuffer(16);
var buf = new Buffer(ab); // Buffer constructor accepts ArrayBuffer.

console.log(buf instanceof Uint8Array); // true
console.log(buf instanceof Buffer); // true

buf.writeUInt32BE(0x61626364, 0);

console.log(buf.toString()); //abcd</pre><p></p>

<h3>Simple Stream Construction の追加</h3>

<p>Streamの作成が簡単になりました。今までStreamを作るためには、目的のStreamを継承して、<code>TransformStream</code>であれば <code>_transform</code>のようなメソッドを拡張して実現する必要がありました。 これをより簡単にしたものが<a href="https://github.com/rvagg/through2" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">through2</a>に代表されるヘルパライブラリでしたが、簡単にいえばこのthrough2がなくても Node.js v4.0ではStreamを作るのが簡単にできるようになりました。</p>

<p></p><pre class="crayon-plain-tag">// これまで
var Transform = require('stream').Transform;
var util = require('util');

util.inherits(MyTransform, Transform);

function MyTransform(opts){
    Transform.call(this, opts);
}

MyTransform.prototype._transform = function(chunk, encoding, callback){
  // ここで変換して
  ...
  // pushする
  this.push(chunk);
};

MyTransform.prototype._flush = function(done){
  // 最後に何かしたければここで flush する
};</pre><p></p>

<p></p><pre class="crayon-plain-tag">// これから
var stream = require('stream');
var transform = new stream.Transform({
  transform: function(chunk, encoding, next) {
    // ここで変換して
    ...
    // pushする
    this.push(chunk);
  },
  flush: function(done) {
    // 最後に何かしたければここでflushする
  }
});</pre><p></p>

<p>詳しくはこちらの<a href="https://nodejs.org/api/stream.html#stream_simplified_constructor_api" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">API資料</a>を参考にしてください。</p>

<h3>os.homedir機能の追加</h3>

<p>HOMEディレクトリを取得するための関数<code>os.homedir</code>が追加されました。</p>

<p></p><pre class="crayon-plain-tag">const os = require('os');
console.log(os.homedir()); // /User/yosuke</pre><p></p>

<p>今までは<code>HOME</code>の環境変数から取得したり色々な方法で解決していましたが、これからは<code>os.homedir</code>関数で取得することができます。</p>

<h1>性能向上</h1>

<p>v8のバージョンアップ、http_parserモジュールのバージョンアップにより、これまでよりも性能が8%ほど向上しています。まだ、前述した<code>Buffer</code>の改善により、メモリ効率的にも向上が見られます。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2015/11/perf1.png" data-wpel-link="internal"><img src="/wp-content/uploads/2015/11/perf1-640x344.png" alt="perf1" width="640" height="344" class="aligncenter size-large wp-image-17805" srcset="/wp-content/uploads/2015/11/perf1.png 640w, /wp-content/uploads/2015/11/perf1-300x161.png 300w, /wp-content/uploads/2015/11/perf1-207x111.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a></p>

<p><a href="https://raygun.io/blog/2015/09/nodejs-and-io-js-are-now-one/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">この結果</a>を見ていただくと分かる通り、Requests/secondベースでhttpの速度が8%程改善されています。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2015/11/perf2.png" data-wpel-link="internal"><img src="/wp-content/uploads/2015/11/perf2-640x300.png" alt="perf2" width="640" height="300" class="aligncenter size-large wp-image-17806" srcset="/wp-content/uploads/2015/11/perf2.png 640w, /wp-content/uploads/2015/11/perf2-300x141.png 300w, /wp-content/uploads/2015/11/perf2-207x97.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a></p>

<p>また<a href="http://apmblog.dynatrace.com/2015/09/05/all-you-need-to-know-about-node-js-4-0/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">こちらの結果</a>では、性能改善はもとより、メモリ効率的にも効率化されていることがわかります。</p>

<p><code>http</code>だけではなく、<code>fs</code>に<code>writev</code>書き込みをするモードが追加されたり、<code>require</code>関数の無駄な処理を省いて高速化するといったことが行われています。</p>

<h1>Deprecated になったAPI</h1>

<p>残念ながら、<code>deprecated</code>になったAPIも存在します。下記のAPIは今後は使わないようにしてください。</p>

<ul>
<li>domains(エラーはキャッチできるものの、キャッチしたエラーでできることが少ない)</li>
<li>fs.exist/existSync(存在確認後に別プロセスから消されることもあるのでRace Conditionに弱い)</li>
<li>util.isObject/isNumberなどのisXXX系(一部既存のライブラリの関数と異なる振る舞いがあり、使いにくいため)</li>
</ul>

<h1>Node.js v4のNode.js v0.12は互換性はあるのか</h1>

<p>JavaScriptのレイヤは互換性があります。ほとんどのモジュールはそのまま動くでしょう。ただし、CやC++のnative module をバインディングして作っているライブラリはV8がバージョンアップされたことにより、内部のABIの互換が崩れているため動かなくなります。それらのモジュールが、まだ<code>v4.0</code>に対応するまではアップグレードをしても動きません。</p>

<p>C/C++のnativeモジュールの問題で動いていないモジュールを確認するためにはこの<code>issue</code>を確認していただくとよいと思います。</p>

<p><a href="https://github.com/nodejs/node/issues/2798" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://github.com/nodejs/node/issues/2798</a></p>

<h1>Node.js v4とNode.js v5の違い</h1>

<p>Node.js v4はLTSというリリースしてから2年半サポートするサポートポリシーがついていますが、Node.js v5にはついていません。つまり、 Node.js v5は次のバージョンが出たらサポートされなくなります。
今のところ、 LTSの対象になるのは偶数のバージョンとされています。ただし、偶数のバージョンが必ず<code>LTS</code>というわけではなく、今のところ偶然そういうバージョンになっているというのが正しい状態なので、きちんとLTSかどうかを把握するためには、バージョン番号の他にLTS識別名(<code>Argon</code>や<code>Boron</code>等の元素名)が付いていることを確認したほうがよいです。</p>

<p>確認するには、公式サイトのトップページに書いてある情報から識別するのが簡単です。</p>

<div id="attachment_17808" style="width: 650px" class="wp-caption aligncenter"><a href="https://html5experts.jp/wp-content/uploads/2015/11/official.png" data-wpel-link="internal"><img src="/wp-content/uploads/2015/11/official-640x434.png" alt="公式サイト" width="640" height="434" class="size-large wp-image-17808" srcset="/wp-content/uploads/2015/11/official.png 640w, /wp-content/uploads/2015/11/official-300x203.png 300w, /wp-content/uploads/2015/11/official-207x140.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a><p class="wp-caption-text">公式サイト</p></div>

<p>v0.10からv0.12までのLTSプランについては下記の図を参考にしてください。</p>

<div id="attachment_17809" style="width: 650px" class="wp-caption aligncenter"><a href="https://html5experts.jp/wp-content/uploads/2015/11/schedule.png" data-wpel-link="internal"><img src="/wp-content/uploads/2015/11/schedule-640x358.png" alt="スケジュール" width="640" height="358" class="size-large wp-image-17809" srcset="/wp-content/uploads/2015/11/schedule.png 640w, /wp-content/uploads/2015/11/schedule-300x168.png 300w, /wp-content/uploads/2015/11/schedule-207x116.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a><p class="wp-caption-text">スケジュール</p></div>

<p>Node.js v5には、<code>npmのバージョン3</code>や<code>ALPN</code>サポートがついています。ただし、v5.0は前述したとおりLTSバージョンではないので次のバージョンからはサポートされません。最新の機能を使ってみたい方向けのバージョンになります。実際に最新の機能を使ってみたい場合は、Node.js v5を利用するのがいいかと思います。</p>

<h1>まとめ</h1>

<p>Node.js v4.0がリリースされました。実際にすでにプロダクションで使っているところもある安定したバージョンになります。また、Node.js v4.0には新しいJavaScriptであるES2015の機能が使えるようになっています。APIにもいくつか変更があるのと、性能が改善されています。</p>

<p>また、LTSということで、今から2年半、2018年4月まではメンテナンスされる予定です。v5もすでにリリースされていますが、これは今のところ最新の機能を使ってみたいedgeなエンジニア向けのバージョンです。</p>

<p>バージョンの違いを意識した上で利用してください。</p>

<p>また、Node.jsのv0.10やv0.12を使っている人たちはまだ多いと思いますが、来年の末には両方のバージョンともにサポートが切れるので、今のうちから新しいバージョンにバージョンアップを検討しておくことをおすすめします。</p>
]]></content:encoded>
			</item>
	</channel>
</rss>
