Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8902

Express Generator で作成されたファイルを触って Express を理解したい1:生成, yarn start, express.static, path.join

$
0
0

express-generatorによる生成、yarn startの意味、express.static()の意味、path.join()の意味についてまで。記述しています。

「余談」とある項目はやや踏みこんだ内容になっています。必要に応じて読み飛ばしてくだい。(むしろ余談のほうが多いね…)

生成

下記のコマンドで app_name以下に雛形を生成できる。

$ express app_name --view=pug

--view=pugを付けない場合、views以下のファイルの拡張子が .jadeになる。jade は pug に改名される予定(というか改名された)ので、今後は .pugに統一されていく(はず)。なので、これから expressのアプリを作るなら --view=pugを付けて生成したほうが良さそう。

生成のコマンド名は expressだけど、使用するには express-generatorをインストールする必要がある。

$ npm install express-generator -g

または

$ yarn add global express-generator

実行

$ yarn start

ポート番号を指定した場合は、PORT=8000 yarn startとする。ポートを 8000 とした場合、http://localhost:8000でアクセスできる。プログラムが正常に起動していれば、下記のページが表示される。

余談:yarn start と package.json の関係

コマンドラインから yarn startとすると、yarnpackage.jsonscriptsの中を見て、startに書かれているコマンドを実行する。ためしに package.jsonの中の starthogehogeにかきかて、yarn startしてみると…

package.json
..."scripts":{"hogehoge":"node ./bin/www"},...
$ yarn start
error Command "start" not found.

このように、エラーになる。そのかわり、yarn hogehogeで実行できる。package.jsonhogehogestartに戻すと、再び yarn startで実行できるようになる。

余談: yarn run

yarn runとすると、yarnscriptsの中を見て、どの項目を実行したいのかと聞いてくる。

$ yarn run
yarn run v1.22.4
info Commands available from binary scripts: acorn, babylon, cleancss, mime, uglifyjs
info Project commands
   - hogehoge
      node ./bin/www
question Which command would you like to run?: 

ここで startと入力すると、node bin/wwwが実行される。

express-generatorではデフォルトで実行コマンド名を startとしているので、yarn startとして実行しているにすぎずstartというコマンドが yarnにあるわけではない(らしい)。そのため、先のように自分でコマンド名を別のものに書き換えたり、追加することもできる。

たとえば、デバッグ時は 8000 番ポートにして、本番は 80 番にしたいみたいなときに、下記のように書いておく。

  "scripts": {
    "start": "node ./bin/www",
    "test": "PORT=8000 node ./bin/www"
  },

これで yarn testまたは yarn run testとすると、8000 番ポートで実行され、yarn startまたは yarn run startだと 80 番ポートで実行できるようになる。

addのような yarnに元からあるコマンドを "start"と同列に追加したい場合は、yarn addではダメで yarn run addとする必要がある(そんなことするのかは謎)。

余談: node bin/www と yarn start の違い

コマンドラインから node www/binとしてもアプリの実行はできる。この場合、package.jsonstartに書かれている内容は無視される。

前述のように、引数などを package.jsonstartに書いている場合は、node bin/wwwとしたときと yarn startで挙動が異なることになる。

生成ファイル

express generator 4.16.1--view=pugオプション付きで生成した場合、下記のファイル構造になる。

project_dir
├── app.js          # アプリのメインファイル
├── bin            
│   └── www            # yarn start 時に node bin/www として実行されるファイル
├── package.json       # ライブラリ等の依存関係やバージョン情報を格納したファイル
├── public          # static なファイルを置くフォルダ
│   ├── images         # http://localhost:8000/images 
│   ├── javascripts    # http://localhost:8000/javascripts 
│   └── stylesheets    # http://localhost:8000/stylesheets
│       └── style.css  # http://localhost:8000/stylesheets/style.css
├── routes          # router (ミドルウェア) 置き場
│   ├── index.js       # http://localhost:8000/ (トップページ)
│   └── users.js       # http://localhost:8000/users
└── views           # テンプレートファイル置き場
    ├── error.pug      # エラー時のテンプレート
    ├── index.pug      # index.js 用のテンプレート
    └── layout.pug     # index.pug や error.pug に読みこまれるテンプレート

static ファイル

下記の内容で index.htmlを作り、public直下に置く。

<!DOCTYPE html><head></head><body><h1>Hello World</h1></body></html>

この状態で PORT=8000 yarn startすると、http://localhost:8000で表示される内容は下記のようになる。

image.png

public以下にファイルを置くと、ブラクザから直接そのファイルにアクセスできるようになる。ただし、アクセスするパスは /publicではなく /になる。

たとえば、上記のように public/index.htmlを作成した場合、http://localhost:8000/public/index.htmlではなく、http://localhost:8000/index.htmlとしてアクセスできる。

http://localhost:8000のように、ファイル名を指定しない場合は、デフォルトで index.htmlが読みこまれる (ファイルが存在していれば)。

このような動作をするのは、app.jsに下記のコードがあるため。

app.use(express.static(path.join(__dirname,'public')));

この記述で、public以下のディレクトリにあるファイルは、http://localhost:8000/以下にあるものとして、そのままクライアントに返される。

単純な web サーバとして使うだけなら、public 以下にファイルを置くだけで十分に機能する。

余談:static ディレクトリの変更、追加

ディレクトリ名を public以外にすることもできるし、複数のディレクトリを指定することもできる。

app.use(express.static(path.join(__dirname,'public')));app.use(express.static(path.join(__dirname,'video')));app.use(express.static(path.join(__dirname,'img/hoge')));

この場合、いずれのディレクトリにあるフィルも、URL としては /にあるものとしてアクセスされる。dat/img/hoge.txthttp://localhost:8000/hoge.txtでアクセスできる。

static に指定されている複数のディレクトリに同名のファイルがある場合は、app.js内で、より先にある行の記述が優先される。たとえば、上のコードの状態で実行したとして、

public
├── images
└── index.html
    └── style.css

video
├── index.html
└── hoge.txt

img
└── hoge
    └── hoge.txt

このように、public/index.htmlvideo/index.htmlが存在している場合に、http://localhost:8000にアクセスした場合、public/index.htmlがブラウザに返される。同様にして、http://localhost:8000/hoge.txtへアクセスした場合は video/hoge.txtが返される。

余談:同じパスを示す app.use() は先にあるものが優先される

先に書いた行が優先というルールは、他の app.use()の記述にも適用される。

varindexRouter=require('./routes/index');app.use(express.static(path.join(__dirname,'public')));app.use('/',indexRouter);

app.use('/', indexRouter)という記述は、http://localhost:8000にアクセスがあった時に、./routes/index.jsの中を実行することを意味している。public/index.htmlが存在しなければ、app.use('/', indexRouter)が実行されて、下記のページが表示される。

しかし、public/index.htmlが存在していると、先にある staticの記述のほうが優先されて、public/index.htmlの内容が表示される。

app.use()の順序を逆にすると、優先順位も逆になる。

varindexRouter=require('./routes/index');app.use('/',indexRouter);app.use(express.static(path.join(__dirname,'public')));

この順序の場合、'public/index.html' が存在していても、app.use('/', indexRouter)の記述が優先される。

public/index.htmlを置くと、このような混乱が起こるので、分かっていてやるのでなければ避けたほうが良いと思う。もし分かってやってるなら、むしろ app.use('/', ... )の記述を消したほうが良い、また publicディレクトリ以下に、他の app.use()で使っているパス名と同じパスがあると、同様の混乱がおこりやすいため、それも避けたほうが良い(と思う)。

余談:path.join(__dirname, '') の意味

下記の index.htmlpublic/index.htmlに置いておき、

<!DOCTYPE html>
<head></head>
<body>
    <h1>Hello World</h1>
</body>
</html>

app.js内の、

app.use(express.static(path.join(__dirname,'public')));app.use('/',indexRouter);

この記述を、

app.use(express.static('public'));app.use('/',indexRouter);

このように変更し、アプリケーションのフォルダ内で yarn startしてみる。ブラウザで http://localhost:8000にアクセスすると、下記のページが表示される、はず。

しかし、bin ディレクトリに入ってから、下記のようにしてアプリケーションを実行し http://localhost:8000をブラウザで開くと…

$ cd bin
$ node www

このようになる、これは public/index.htmlが無視されて app.use('/', indexRouter);が実行されていることを意味する。

なぜこうなるかというと、node コマンドは実行したフォルダをカレントディレクトリとして認識するからのようだ。つまり、binディレクトリ内で node を実行すると、publicディレクトリを探す起点(ルート)が binディレクトリになってしまい、bin/publicを探しにいってしまうから、ということ。

binディレクトリ内に publicディレクトリが存在しない場合、app.use(express.static('public'));の記述は無視されて(マッチせずに) app.use('/', indexRouter);のほうが実行される。これは binが実行時のカレントディレクトリになり、express.static('public')という記述は、bin/publicディレクトリを指すようになるためである。

試しに、次のように書きかえると、binディレクトリ内で node wwwを実行しても、http://localhost:8000へアクセスしたときのブラウザには "Hello World" が表示されるようになる。

app.use(express.static('../public'));

ここで、下記のように path.join()を使った記述に戻すと、binディレクトリ内で node wwwとした場合でも "Hello World" と表示されるようになる。

app.use(express.static(path.join(__dirname,'public')));

これは、app.js (path.join()が記述されているファイル)があるディレクトリが __dirnameに自動的に入れられるため。つまり、path.join(__dirname, name)とすることで、__dirnamenameが連結され、結果的に app.jsのあるディレクトリを起点(ルート)として publicディレクトリを探してくれる。

アプリのフォルダ以外からアプリを起動することは、そうそう無いと思うのだけど、たまーにそういうことをしてハマることがある。特段の事情がない限り、実在するファイルやフォルダのパスを指定するときは path.join(__dirname, name)としたほうが良い

一方で、__dirname を app.useの第一引数や、外部参照するURLのパスの指定に使うとだいたいバグになる(分かっていたらやらないはず…)。

後述するように、routesディレクトリ内にあるファイルで path.join(__dirname, name)を使用したときは、routesディレクトリがカレントディレクトリ (__dirname) になる。app.jsのあるディレクトリではない。これはよく間違う。

また、app.use(path, ... )の一つ目の引数で指定する pathは、実在するファイルのパスを指定する引数ではない。この引数で path.join()を使うのは(分かってやっているのではない限り)誤りである。

余談:path でファイル名操作

pathjoin()以外にも、ファイル名を操作する便利な機能がいろいろある。詳しくは以下を参照のこと。


Viewing all articles
Browse latest Browse all 8902

Trending Articles