15
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ECMAScript2015(es6)の基本的な開発環境

Last updated at Posted at 2017-06-18

#es6の基本的な開発環境を用意する
Vue.jsReact.js等を利用したJSの開発が広まりつつあるが、フレームワークを使わないJSの開発もありえるので、備忘録としてes6の簡易的な開発環境の構築方法についてまとめる。

##node.jsのインスール
npmコマンドが必要になるので、node.jsをインストールする。
インストール方法については、Windows10環境ではこちら、Macの場合はこちらが参考になる。

なお、Windows10の場合、node.jsのコマンドプロンプトで、chcp 65001を実行することで、UTF-8テキスト表示ができ、文字化けを防止できる。

##browserifyを利用した環境構築
es6に準拠した書き方で開発すると動作幅広いブラウザで動作しないため、babelifyが必要となる。また、import/exportを利用し(babelifyでrequireに書き換わるため)、複数のJSファイルを一つにまとめるにはbrowserifyが必要となる。

###browserifyとbabelifyのインスール
es6で開発するルートディレクトリで、以下のようにnpmコマンドを実行する。
npm init実行時はすべて質問に対して"Enter"キーのみでスキップした。

モジュールのインストール
$ npm init
$ npm install -g browserify 
$ npm install --save-dev babelify babel-preset-es2015

browserifyはコマンドから頻繁に利用することを想定し、グローバルインストールした。
なお、JSファイルの改行や空白を除去し、minファイルを作成する場合、uglifyもインストールする。

UglifyJSを利用する場合
$ npm install -g uglify-js

###サンプル
JS側でCSVファイルを読み込んで、その内容を描画するサンプルを用意。
具体的には、csvファイルの1列目を読み込みtextareaに表示するサンプル。データがURL形式(http/https)かどうかチェックし、読み込む行数はtextareaのmaxlengthで指定された値に制限する。

####コード
以下のファイルはすべて開発環境のルートディレクトリに配置する。
JSのコードについては、可能な限りES6で追加されているシンタックス(こちらを参考にした)を利用した。

index.html(圧縮前のjsを利用)
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>はじめてのES6</title>
</head>
<body>
<div style="margin: 20px" class="js-csv-read">
    <div style="color: red" class="js-message"></div>
    現在<span class="js-csv-count"></span>件登録されています<br>
    <textarea style="height: 30px" class="js-csv-data" readonly maxlength="3"></textarea>
    <br>
    <input type="button" class="js-csv-clear" value="クリア">
    <input type="file" class="js-csv-file">
</div>
<div style="margin: 20px" class="js-csv-read">
    <div style="color: red" class="js-message"></div>
    現在<span class="js-csv-count"></span>件登録されています<br>
    <textarea style="height: 50px" class="js-csv-data" readonly maxlength="5"></textarea>
    <br>
    <input type="button" class="js-csv-clear" value="クリア">
    <input type="file" class="js-csv-file">
</div>
<footer>
<script type="text/javascript" src="bundle.js"></script>
</footer>
</body>
</html>
read-csv.js
"use strict";

export default class ReadCsv {
    constructor(targets) {
        this.targets = targets;
        this.init_();
    }

    init_() {
        [].forEach.call(this.targets, (target) => {
            const area   = target.querySelector(".js-csv-data");
            const count  = target.querySelector(".js-csv-count");
            const clear  = target.querySelector(".js-csv-clear");
            const csv    = target.querySelector(".js-csv-file");

            this.clean_(area, count)

            clear.addEventListener("click", (evt) => {
                this.clean_(area, count)
                this.dispalyMessage_(target, "クリアしました");
            });

            csv.addEventListener("change", (evt) => {
                evt.preventDefault();
                this.readCsv_(target, evt, area, count);
            });
        });
    }

    clean_(area, count) {
        area.innerHTML  = "";
        count.innerHTML = "0";
    }

    displayMessage(target, message) {
        const msg = target.querySelector(".js-message");
        msg.innerHTML = message
    }

    appendValue_(target, area, v) {
        const e = document.createTextNode(v + "\n");
        area.appendChild(e);
    }

    readCsv_(target, evt, area, count) {
        const file   = evt.target.files;
        const reader = new FileReader();

        this.clean_(area, count)
        this.displayMessage(target, "");

        reader.onload = (function(obj) {
            const o = obj;
            return function(evt) {
                o.processCsv(target, evt.target.result, area, count);
            };
        })(this);

        reader.onerror = (function(obj) {
            const o = obj;
            return function(evt) {
                o.displayMessage(target, "ファイルの読み込みに失敗しました");
            };
        })(this);

        reader.readAsText(file[0]);
    }

    processCsv(target, csv, area, count) {
        const lines  = csv.split(/\r\n|\n/);
        const maxLen = parseInt(area.getAttribute("maxlength"), 10);

        let c = parseInt(count.innerHTML, 10);

        try {
            lines.forEach((elm, idx) => {
                const data = elm.split(",");
                if (data.length > 0 && /^(https?)(:\/\/[-_.!~*'()a-zA-Z0-9;\/?:@&=+$,%#]+)$/.test(data[0])) {
                    if (c >= maxLen) {
                        throw new Error('上限に達しました');
                    }
                    this.appendValue_(target, area, data[0]);
                    c++;
                }
            });
        } catch (e) {
            console.log(e);
        }
        count.innerHTML = c;
    }
}
app.js
"use strict";

import ReadCsv from "./read-csv";

class MyApp {
    static exec() {
        const obj = new MyApp();
        obj.prepareReadCsv();
    }

    prepareReadCsv() {
        const targets = document.querySelectorAll(".js-csv-read");
        if (targets) {
            const ts = new ReadCsv(targets);
        }
    }
}

MyApp.exec();

####ビルド
下記のコマンドを実行する。

ビルド
$ browserify app.js -o bundle.js -t [ babelify --presets es2015 ]
圧縮する場合
$ uglifyjs bundle.js -c -o bundle.min.js

####デモ
#####1. index.htmlにアクセス
スクリーンショット 2017-06-18 12.23.43.png

#####2. 2つの「ファイルを選択」ボタンをそれぞれクリックし、csvファイルを渡す
スクリーンショット 2017-06-18 12.28.45.png

csvファイルは以下を利用した。

test.csv
http://hogehoge/01
ftp://hogehoge/02
http://hogehoge/03
http:/hogehoge/04
http://hogehoge/05
http://ほげほげ/06
http://hogehoge/07
http://hogehoge/08
http://hogehoge/09

##webpackを利用した環境構築
webpackは、フロントエンドにおけるビルドツールの1つ。browserifyのように「複数ファイルを1つのjsファイルにまとめる」ことができ、パッケージ管理もできるので非常に便利。

###インストール
下記コマンドを開発環境のルートディレクトリで実行する。
npm initを実行するといくつか質問に答える必要があるが、今回作成時はすべてリターンキーを押下し、デフォルトとした。

$ npm init
$ npm install -g webpack

###ビルド
サンブルコードは、「browserifyを利用した環境構築」と同じコードを利用。
下記を実行することでビルドができる。

実行したコマンド
webpack app.js bundle.js
出力結果
Hash: 2bae931e656c8d42ecde
Version: webpack 2.5.1
Time: 72ms
            Asset     Size  Chunks             Chunk Names
bundle.webpack.js  6.05 kB       0  [emitted]  main
   [0] ./read-csv.js 2.69 kB {0} [built]
   [1] ./app.js 364 bytes {0} [built]

ただし、これでは複数ファイルをまとめただけなので、複数ブラウザで対応できていないので、package.jsonwebpack.config.jsonを編集し、babelを利用する。

###package.jsonとwebpack.config.json
####package.json
npm initコマンド実行後に以下のようなpackage.jsonが生成されている。ここでは作業ディレクトリ名がbaseのためnameキーの値がbaseとなっている。

package.json
{
  "name": "base",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

####babel-loader、プリセットのインストール
下記コマンドでwebpackをローカルインストールし、babelをインストールする。

$ npm install --save-dev webpack babel-loader babel-core babel-preset-es2015

インストールするとpackage.jsonも下記のように更新される。

package.json
{
  "name": "base",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "webpack": "^3.5.5"
  }
}

####webpack.config.js
webpackコマンド実行時にファイル名をwebpack.config.jsとし、ワーキングディレクトリ直下に配置すると、webpackコマンド実行時に--configオプションでconfigファイルを指定する必要がなくなる。
なお、便宜上、出力ファイル名をapp1とした。

webpack.config.js
var webpack = require('webpack');

module.exports = {
  entry: {
    'app1': './app.js',
  },
  output: {
    path: __dirname,
    filename: '[name].bundle.js'
  },
  module: {
    loaders: [
      { 
        test: /\.js$/, 
        exclude: /node_modules/, 
        loader: "babel-loader", 
        query:{
          presets: ['es2015']
        }
      }
    ]
  }
};

余談だが、'app1'を'output/app1'とすることで、出力ファイルを指定したディレクトに変更することができるので、entryが複数あり、それぞれ別のディレクトリに出力したいときに便利。

###もう一度ビルド
準備ができたのでもう一度ビルドしてみる。

実行コマンド
$ webpack -p
実行結果
Hash: 4417e05b925329407a30
Version: webpack 2.5.1
Time: 1813ms
         Asset     Size  Chunks             Chunk Names
app1.bundle.js  8.37 kB       0  [emitted]  app1
   [0] ./read-csv.js 4.15 kB {0} [built]
   [1] ./app.js 1.49 kB {0} [built]

これで複数ブラウザでもJSが動作できる。

####npm runコマンド
package.jsonを以下のように変更することで、npmコマンドでもビルドできる。

package.json
{
  "name": "base",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack -p"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "webpack": "^3.5.5"
  }
}

変更後、下記コマンドを実行しても同じ結果となる。

$ npm run build

###pluginを導入
せっかくなのでpluginも使ってみた。今回は、UglifyJsBrowserSyncを利用した。
Browsersyncはファイル変更を監視し、自動でブラウザリロードを行う。

####BrowserSyncのインストール
以下を実行する

$ npm install --save-dev browser-sync browser-sync-webpack-plugin

package.jsonも更新される。

####UglifyJsのインストール
この場合、グローバルではなくローカルインストール。

$ npm install --save-dev uglify-js

####webpack.config.jsとpackage.jsonの更新
webpack.config.jspackage.jsonをそれぞれ下記のように更新する。

webpack.config.js
var webpack = require('webpack');
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');

module.exports = {
  entry: {
    'app1': './app.js',
  },
  output: {
    path: __dirname,
    filename: '[name].bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        query:{
          presets: ['es2015']
        }
      }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compress: { warnings: false }
    }),
    new BrowserSyncPlugin({
      host: 'localhost',
      port: 3000,
      server: { baseDir: ['./'] } })
  ]
};
package.json
{
  "name": "base",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack -p",
    "watch": "webpack  --progress --colors --watch"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "browser-sync": "^2.18.13",
    "browser-sync-webpack-plugin": "^1.2.0",
    "webpack": "^3.5.5"
  }
}

####ビルド

$ npm run watch

上記コマンドを実行すると、自動的にブラウザ起動し、http://localhost:3000にアクセスされて、index.htmlが表示される。
http://localhost:3001にアクセスすると以下の画面が表示されるが、browser-sync-weboack-pluginについて理解度が不足しているのでさらに研究したいと思う。

スクリーンショット 2017-05-18 3.21.40.png

###package.jsonを利用したモジュールのインストール
es6の開発環境が既に用意されている場合、package.jsonがあるディレクトリ配下で下記コマンドを実行するだけで、必要なpackageがローカルインストールされるので、個別にそれぞれ必要なモジュールをインストールする必要がない。プラグインなどもインストールされる。

$ npm install

##webpack-dev-serverについて
前述したBrowserSyncがあるが、webpack-dev-serverがあり、こちらを利用することが多い。
対象となるファイル(contentBaseで指定したディレクトリ配下)に修正・変更等があると自動的にビルドされる。

###インストール

$ npm install --save-dev webpack-dev-server

###package.jsonwebpack.config.jsの更新
webpack.config.jspackage.jsonをそれぞれ下記のように更新する。今回は、pathを使っている。
webpack-dev-serverのデフォルトパスは

package.json
{
  "name": "base",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack -p",
    "watch": "webpack  --progress --colors --watch",
    "dev": "webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "browser-sync": "^2.18.13",
    "browser-sync-webpack-plugin": "^1.2.0",
    "webpack": "^3.5.5",
    "webpack-dev-server": "^2.7.1"
  }
}
webpack.config.js
var webpack = require('webpack');
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');
var path = require('path');

module.exports = {
  entry: {
    'app1': './app.js',
  },
  output: {
    path: __dirname,
    filename: '[name].bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        query:{
          presets: ['es2015']
        }
      }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compress: { warnings: false }
    }),
    new BrowserSyncPlugin({
      host: 'localhost',
      port: 3000,
      server: { baseDir: [path.resolve(__dirname)] } })
  ],
  devServer: {
    contentBase: path.resolve(__dirname),
    compress: true,
    port: 9000
  }
};

####ビルド

$ npm run dev

上記コマンドを実行すると、自動的にブラウザ起動し、http://localhost:9000にアクセスされて、index.htmlが表示される。

####備考
BrowserSyncwebpack-dev-serverを利用する場合、コンテンツがPHPなどCGIで動作する場合、nginxでリバースプロキシの設定などを施す必要がある。その場合、jsが実行されているサーバのURLもCGI側でscriptタグで指定するといったイメージ。

##yarnについて
npm互換のパッケージマネージャにyarnがあり、rails5.1でサポートされていたり、Reactを利用する際のパッケージマネージャとしてもこちらを利用することが推奨されている。

###yarnのインストール
Macでは下記コマンドでインストール可能。

$ brew install yarn --ignore-dependencies

###npmとyarnでの簡単なコマンド比較

npm yarn
npm init yarn init
npm install yarn install
npm install --save-dev yarn add --dev
npm install -g yarn global add
npm uninstall --save-dev yarn remove
npm uninstall --save yarn remove
npm run yarn run
npm test yarn test
15
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?