Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
101
Help us understand the problem. What is going on with this article?
@cocottejs

ブラウザとサーバで全く同じjavascriptを動かそう。[gulpとbrowserify超入門]

More than 5 years have passed since last update.

はじめに

この記事は、 gulpbrowserifyも知らない 初心者を対象にしています。
最近になってnode.jsでプログラミングしていますよって人が対象です
あまりにも0ステップからで、それくらいわかるよって事もあるかもしれません。

node.jsについて、0から勉強したい人はドットインストールとか便利かもね。

gulpって?browserifyって?

まず、gulpもbrowserifyもなんのことだか、さっぱりわからないって!
でも、まだ説明しません。
なぜなら、 なんで必要?を説明していないから。

目的は

ブラウザとサーバで全く同じjavascriptを動かそう

最近は、node.jsでサーバサイドjavascriptも珍しくなくなってきました。
でも、微妙に違うサーバとブラウザでのjavascript!
特に、違うなーと感じるのは、記述する場所ですね。

本当はもっといろいろあるけど、長くなるし面倒なので省略。

簡単なブラウザで動作するjavascriptは次のような感じですね

index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script>
     var x = 1;
     var y = 2;
     console.log(x + y);
  </script>
</head>
<body>
  contents
</body>
<html>

まったく実用的ではないフザケたコード。でもちゃんと動きます
結果はページには表示されませんが、デバッグツールのコンソールで確認できます

さすがに、htmlファイルにjavascriptを記述することが少なくなってきて、最近は

index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script type="text/javascript" src="script.js"></script>
</head>
<body>
  contents
</body>
<html>
scritp.js
var x = 1;
var y = 2;
console.log(x + y);

とファイルを分けることが一般的かつ推奨されています。(まあ常識なんでしょうけど一応)

一方、サーバ代表のnode.jsでも、上のscript.jsを実行するときちんとコンソールに表示することができます
ブラウザとサーバで全く同じjavascriptを動かそう」っっって問題ないじゃん!
てなるはずですよね。でも。。。
スクリプトファイルがひとつだけなら問題ないんだよ。

require?なにそれ

node.jsでは、他のファイルやモジュールを利用する際に、普通に requireを使いますよね。
複数のjsファイルでコードを書くなら常識!

script.js
var tasu = require('./tasu');
var total = tasu(1, 2);
console.log(total);
tasu.js
function tasu (x, y) {
  return x + y;
}
module.exports = tasu;

これって、超便利!
ファイルを分けてコード管理できるし、ほかの人が作ったモジュールを簡単に利用できるし。
サーバで動かす時も、tasu.jsの存在なんてしらなくってOK
node script.jsで実行できます

でもブラウザじゃ、そんな機能ないじゃんかー

だからブラウザで動かす時は、こうなっちゃう

index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script type="text/javascript" src="tasu.js"></script>
  <script type="text/javascript" src="script.js"></script>
</head>
<body>
  contents
</body>
<html>
script.js
var total = tasu(1, 2);
console.log(total);
tasu.js
function tasu (x, y) {
  return x + y;
}

script.jstasu.jsからrequiremodule.exportsの記述がなくなったかわりに
気をつけなくてはいけない事がある。しかも、htmlで!

  • 関係しているファイルをすべて取り込まなくてはいけません。
  • 依存している関数が記述されているtasu.jsは、それを使用するscript.jsより先に、取り込まなくてはいけません

はっきりいって、ブラウザでjavascriptの重要度が増している昨今、ファイルが爆発的に増えてくると管理なんて無理無理。
依存関係とか、知らんし。ってなります

出番です。browserify

そこで、browserifyを使うと、こう書けちゃうよって事です。

それにしてもbrowserifyって、どう発音するんだろね。”ぶらうぜりふぁい”?

index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <script type="text/javascript" src="script.js"></script>
</head>
<body>
  contents
</body>
<html>
script.js
var tasu = require('./tasu');
var total = tasu(1, 2);
console.log(total);
tasu.js
function tasu (x, y) {
  return x + y;
}
module.exports = tasu;

依存関係なんて、htmlで管理する必要はありませんし、なによりjavascriptのコードがサーバと一緒ってのが気持ちがいい

もっと重要なのは、サーバ用とブラウザ用の2種類のコードを保守しなくていいってことだけどね

でもさ、 これってどうすりゃ出来んの

さて、まずはbrowserifyをインストール。-gを忘れないでね。

npm install -g browserify

すると、OSのコンソールでbrowserifyコマンドが使えるようになるはず。

たぶん。。。私の環境がMacオンリーなんでWindowsはわからんです。

さっそく、使ってみましょう

browserify script.js -o bundle.js

すると、script.jstasu.jsが結合されたbundle.jsってファイルができました
実はbrowserifyがブラウザの機能を拡張するわけではなく、サーバで動作するファイルをブラウザで読み込む事のできるファイルに変換することしかしません。
でもhtmlでは、このbundle.jsをひとつだけ読み込めばOKです。依存関係もコードを解析してくれるから知らなくて大丈夫。
依存しているファイルtasu.jsの指定を、コマンドではしていないでしょ。
これで、htmlからサーバで動くファイルscript.jstasu.jsとまったく同じjavascriptを動かしている事と同じなります。(結果的に)

htmlのsrc="script.js"はbundle.jsに変えてね。

bundle.jsの中身はこんな感じ。おまじないのコードがごちゃーとはいっていますが、script.jstasu.jsのコードがきっちり含まれていることがわかりますね。

bundle.js
(function e(t,n,r){       /** (おまじないは省略) **/      })({1:[function(require,module,exports){

var tasu = require('./tasu');
var total = tasu(1, 2);
console.log(total);

},{"./tasu":2}],2:[function(require,module,exports){

function tasu (x, y) {
  return x + y;
}
module.exports = tasu;

},{}]},{},[1])

これで、今までサーバで動かしていたコードをブラウザ仕様にする必要がなくなりました

よかったー。


って。。。

jsファイルを書き換えるたびにコマンド打つのは め・ん・ど・く・さ・く・ね
だいたいbrowserifyってなげーし、スペル覚えらんない。

出番です。gulp

そこで、gulpを使うと、 繰り返しのコマンド打つのが自動化できるかもよ!ってことです。

gulpもなんて発音?”がるぷ”でええのかなー。

gulpはタスクランナーって部類のモジュールです。毎回毎回毎回毎回、、おんなじコマンド打って実行している処理をまとめたり、自動化したりと何かと便利。
nodeで開発しているとやたらとお世話になります。

タスクランナーにはgruntってのもあるけど、gulpのほうが後発で初心者向けだと思います。
gruntはずっと使っていたわりに、すぐにやり方忘れちゃうんだよね。gulpにしよう!

なにはともあれインストールって前に、関係ファイルとディレクトリ構成を確認。
いまから、この構成にしていきます。
libディレクトリだけは手動で作成して、script.jstasu.jsを入れてください。
index.htmlsrc="public/script.js"とします。
その他のファイル、ディレクトリはその都度説明します。

file&directory
myapp/
  index.html
  gulpfile.js            あとで説明するよ
  lib/
    script.js
    tasu.js
  public/
    script.js            自動化で作成されるよ
  node_modules/
    browserify/          インストールするよ    
    gulp/                インストールするよ
    vinyl-source-stream/ インストールするよ

コードを書くファイルはlibって名前のディレクトリに、変換後のファイルをpublicって名前のディレクトリにを入れるのは、慣例みたいなもので多数の人がそのルールで書いてたりします。ここは深く考えずに従うのが吉ですね。

さて、仕切り直しでgulpをインストール。こちらも-gを忘れないこと。(gulpコマンドがコンソールから使用できるようになるからね)

npm install -g gulp

さらに、ローカルインストールもします。(両方しないと動かないんだよね)

npm install gulp

そして、さらにさらに、gulpでbrowserifyを実行させるためにbrowserifyをインストールします。-gはつけないでね。

npm install browserify

あとひとつ、ファイルの取り扱いはstreamが欠かせないってことで、vinyl-source-streamをインストールします

npm install vinyl-source-stream

自動化は、ファイルを更新したときにbrowserify lib/script.js -o public/script.jsが実行されるということで、よろしかったでしょうかー。
その自動化の処理を記述するファイルgulpfile.jsを作成します
まずはコピペで作ってください。解説はのちほど。

gulpfile.js
var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream');

gulp.task('browserify', function () {
  browserify('lib/script.js')
    .bundle()
    .pipe(source('script.js'))
    .pipe(gulp.dest('public'));
});

gulp.task('watch', function() {
  gulp.watch('lib/**/*.js', ['browserify']);
});

gulp.task('default', ['browserify', 'watch']);

このファイルを実行する方法は、コンソールでgulpと打つだけです。
gulpコマンドはgulpfile.jsを探して実行してくれます
すると即座に、publicディレクトリとその下にscript.jsが作成されます
さらに、lib下のjsファイルを更新するたびに、public/script.jsが更新されます

ひとつひとつ説明します
実際に、変換の処理を行っているのは、6-9行目のところです。

  • browserify('lib/script.js') : 対象のjsファイルを指定して読み込みます
  • .bundle() : ブラウザ用のコードを作成
  • .pipe(source('script.js')) : ファイルを出力するためのStream(データの塊みたいなもん)を出力
  • .pipe(gulp.dest('public')); : 受け取り側のディレクトリを指定します

でも、このファイルの先頭の行から変換処理が始まるわけではありません。

gulpを実行した際に、このファイルは奇妙なことですが、まず最後の行から変換処理を始めます
先ほどファイルの実行は gulpと打ってくださいといいましたが、正確には gulp defaultが実行されます
gulpの後にタスク名を指定する必要があるんですが、指定しないとdefaultを指定したことになるんですね。
ということで、gulp.taskはタスクの登録だけで 処理はしてくれないんです
このファイルではbrowserifywatchdefaultの3つのタスクが登録されています。
実行は、 コンソールコマンドから指定すると理解してください。

そしてタスクで実行される処理内容は関数を記述します。
でも、defaultのようにタスク名を配列で指定した場合は、同名のタスクを実行します。
なので、最初にbrowserifyを実行して、次にwatchを実行します。
すぐにpublic/script.jsが作成されるのは、そういうことですね。

ではlibディレクトリ内のファイルを更新するたびにpublic/script.jsも更新されるのはなぜでしょう。
gulp.watchがファイルの更新を監視してくれるからですね。
ちょっと独特な記述方法のlib/**/*.jsは、libディレクトリ以下で子ディレクトリも含むすべてのjsファイルの更新を監視します。
もし変更があったら、タスクbrowserifyを実行します`。

これで、もうbrowserifyというなかなか覚えられないスペルのコマンドを打つ必要がなくなりました。

ばんざーーい

おわりに

いろいろ端折っていますが、最低限の説明でまったくbrowserifyとgulpを理解できるようにしたつもり。
案外、初心者向けな情報がなさそうだったのでまとめてみました。半分以上は忘れっぽい自分用のメモです。
(他の方の記事を参考にさせていただいたところあって感謝・感謝)
本当は、すごいメリットがあることを、説明したかったけどどんどん趣旨がずれるんで、そこらへんは端折りました。

グローバル汚染が防げるとか、使用できるオブジェクトの制限とか、ファイル圧縮できるとか、coffeescriptとの併用とか、livereloadとかとか

間違いがあったらコメント下さいね。ではでは

101
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
cocottejs
Java,C#,PHPと業務系に携わってきました。最近はnodejsでフレームワークを作成中。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
101
Help us understand the problem. What is going on with this article?