Node.jsこと始め(備忘録)

なんとなく使っていたNode.jsって??「PHPとなにがちがうのだろう?」と漠然とした疑問

疑問を解消すべく、調べました

ついでに、これからはバージョン管理をします

目次
  1. インストールについて(バージョン管理)
  2. Node.jsとは
  3. package.json
  4. モジュール
    1. Node.jsのモジュール
    2. require
    3. module.exports
  5. グローバル
  6. コアモジュール
    1. pathモジュール
    2. fsモジュール
    3. httpモジュール
  7. まとめ

インストールについて(バージョン管理)

前にインストールしたNode.jsは、バージョンが古くなってきたので、バージョン管理をします
(/usr/local/bin/にあるnodeはとりあえずはそのままにしておきます^^;)
バージョン管理に「nvm」を使用します
「nvm」はユーザーごとにインストールされ、シェルごとに呼び出される設計です


*インストール後、ターミナルは再起動します

# Node.jsのリリースされているバージョンをすべて列挙
nvm ls-remote

# インストール
nvm install バージョン

# LTS(長期サポート)な最新バージョンをインストール
nvm install --lts

# LTSを最新にしたときに現在グローバルにインストールしているモジュールもインストール
nvm install "lts/*" --reinstall-packages-from=current

# インストール済のバージョンを確認
nvm ls

# バージョンを切り替える
nvm use バージョン

# 現在使用しているバージョンを表示
nvm current

Node.jsの偶数バージョンは「LTS」と呼ばれ、長期サポートが保証されています

余談

グローバルインストールとローカルインストールについて
npm install -g 
「-g」オプションをつけると「Node.js」本体にパッケージをインストール(グローバルにインストール)します
「-g」をつけなければ、ローカル(現在のプロジェクトだけ)にインストールします

「npm」については、あとの「package.json」でざっくりと^^;

(余談)/usr/local/bin/は通常のダウンロードでセットアップしたNode.js本体がある場所です

Node.jsとは

??

Node.js はスケーラブルなネットワークアプリケーションを構築するために設計された非同期型のイベント駆動JavaScript 環境です

https://nodejs.org/ja/about/
「JavaScript環境です」とは?

JavaScriptは言語名、Node.jsはJavaScriptの処理系です
処理系とは、その言語を解釈して「0と1」だけの機械語に翻訳するためのソフトウェアです

Node.jsがインストールされた環境であれば、OSに関係なくJavaScriptが実行できます
node ファイル名で実行します

ちなみに、ブラウザにはJavaScriptの処理系が入っているので、インストールすることなくJavaScriptが実行できます

Node.jsには「JavaScriptの実行環境」以外に「npm(JavaScriptのライブラリ管理ツール)」が用意されています

「ネットワークアプリケーションを構築するために設計された」とは?

Node.jsは「Webサーバーソフトウェア(Webサーバー)」や「アプリケーションサーバーソフトウェア(APサーバー)」と置き換えて使うことが可能
だそうですが、それってメチャクチャ難しそう💦

*他の言語は通常APサーバーが必須、PHPはApacheと仲良しなので特別!!

ミドルウェア
ミドルウェアはアプリケーションとOSの間で両者の機能を補佐します
ミドルウェアが存在することで、複雑な動作が可能です
例えば、HTTPリクエストを解釈してプログラムを呼び出す役割が、APサーバーです
Web3層アーキテクチャ
2層のクライアントサーバーモデルを、負荷分散や柔軟なシステム変更のために、3階層に分割したシステム
プレゼンテーション層
HTTPリクエストの送受信とアプリケーション層の呼び出し
アプリケーション層
ビジネスロジック担当、プレゼンテーション層(画面)に入力された値をもとに処理を行い、必要があれば「データ層」に要求します
データ層
「アプリケーション層」からデータ要求があれば、データベースを探索、結果をアプリケーション層に返します
「スケーラブルなネットワークアプリケーションを構築するため」というのは、C10K問題が関係しているようです

C10K問題(英語: C10K problem)とは、Apache HTTP ServerなどのWebサーバソフトウェアとクライアントの通信において、クライアントが約1万台に達すると、Webサーバーのハードウェア性能に余裕があるにも関わらず、レスポンス性能が大きく下がる問題である。

原因
この現象の原因は主に2019年現在も広く用いられているApache HTTP Serverの駆動方式にある

回避方法
C10K問題を回避するためには様々な方法がある。

Nginxなど、C10K問題が起こらないように設計されたソフトウェアを使用する
サーバーサイドでNode.jsなどの駆動方式を持ったソフトウェアを使用する

https://ja.wikipedia.org/wiki/C10K%E5%95%8F%E9%A1%8C
非同期型のとは?

Node.jsは「シングルスレッド」「ノンブロッキングI/O」です
シングルスレッドでは、前の処理が終わらないと次の処理が始まらない
しかし、重い処理が終わるまでの間は、実行中のプログラムは非同期にし(遅延実行して)待ち時間で別の処理ができます

Node.jsの関数は I/O(I/Oとは入力と出力です) を直接実行しないため、実行中のプログラムをブロックしません

マルチスレッドでは、前の処理を待たずに別スレッドが立ち上がります
しかし、ブロッキングI/O(同期処理)で、順番に処理するため実行中のプログラムに待ち時間が発生します

シングルスレッドのイメージ図
イベント駆動とは?
フロー駆動
上から順番に実行されるプログラムです
イベント駆動
イベントが発生すると実行するプログラムです
裏側では無限ループでイベントの発生を監視しています(この無限ループをイベントループといいます)

package.json

npmレジストリ」とは、世界中の開発者が作った Node.jsのパッケージが集められた場所です
「npmレジストリ」からパッケージをインストールして利用します

通常「Node.jsプロジェクト」を1つ作る時は「npmパッケージ」を1つ作ります
パッケージを管理するためのファイルが「package.json」です
プロジェクトのフォルダに移動してnpm init -y (-yは質問を省略するオプションです)で「package.json」を作成します

パッケージ管理が必要な理由は、パッケージ間の複雑な依存関係です

1つのライブラリーが依存するパッケージは膨大な数で依存関係はカオス状態です
すべての依存パッケージは、ローカルのルートディレクトリ直下に作成される「node_modulesディレクトリ」にダウンロードされます(この場所を編集することは、ほぼありません)
仮置場のようなものです(通常、数百MBもの容量を使います)

npm install パッケージ名 でパッケージをインストールすると、「package-lock.json」にすべての「依存パッケージバージョン」が自動で追加されます
「package-lock.json」はバージョンを固定するために必要です

npm installを実行する場合
「package-lock.json」が存在しないとき
「package.json」に基づいてdependency (dependencyとは、そのパッケージが使用している他のパッケージ)がインストールされ、実際にインストールされたすべてのパッケージバージョンが「package-lock.json」に書かれます
「package-lock.json」が存在するとき
package-lock.json」に基づいてインストールします
しかし、「package.json」指定されたバージョンとの矛盾があれば、「package.json」が優先され、実際にインストールされたバージョンが「package-lock.json」に書かれます
「package-lock.json」を優先したい場合
npm ciを実行します

ちなみに、依存関係でエラーになった場合、「node_modules」と「package-lock.json」を削除し、npm installを試します

モジュール

モジュールとは、使い道を決めて「処理をひとまとまりにして、切り出したもの」です
モジュール化することで、モジュールの再利用やモジュール単位での機能交換など、効率的になります

Node.jsのモジュール

Node.jsのモジュールは、3パターンです
  • 自作モジュール
  • npmパッケージ(インストールして利用します)
  • コアモジュール (Node.jsに組み込みまれているモジュール:インストールせずに利用します)

require

「require」メソッドは、モジュールを読み込むときに使います(モジュールを使う側)

requireメソッドは、エクスポートされたモジュールを戻り値として返します
戻り値を定数に代入し、その定数を使うことでモジュールを扱えます
const obj = require("モジュールのパスまたはモジュール名")

引数は
「自作モジュール」の場合は「モジュールのパス」
「npmパッケージ」や「コアモジュール」の場合は「モジュール名」です

*「require」で一度呼ばれたファイルはキャッシュされます

module.exports

モジュールを切り出すときは、「module.exports」を使います(モジュールにするファイルで使います)

モジュールを作成する場合
切り出しは、「module.exports」や「exports」を使用します
「module.exports」と「exports」の違いは?

「moduleオブジェクト」はグローバルオブジェクトです
「moduleオブジェクト」は「exportsプロパティ」を持っています
だから「module.exports」の場合、module.exports = {...} のようにプロパティ名を設定せずに値を格納できます

しかし「exports」の場合、exports={...} ではモジュール化になりません(新いオブジェクトを作っただけです)
exportsの場合は、任意のプロパティ名を設定して再利用する値を格納します

値は、オブジェクトだけではありません(文字列・数値・配列・関数・クラス)

基本的には「module.exports」を利用して、「複数の値をまとめたオブジェクト」で切り出すのかなと思います^^;
const hoge = function(){
  //処理内容
};

module.exports = {
  name: "太郎",
  hoge,
};

グローバル

ブラウザとの比較
グローバルオブジェクトグローバル変数の利用
Node.jsglobalモジュール内のどこでも利用可能
モジュール間では利用不可能
ブラウザwindowモジュール内、間どちらも利用可能
グローバルオブジェクトのプロパティ・メソッドの一例 
どこからでも利用でき、利用時にglobalは省略できます
__dirname
実行中ソースコードの ディレクトリパスを取得
__filename
実行中ソースコードの ファイルパスを取得
module / exports / requite()
モジュール関連
process
実行環境に関する情報
使用例 
「.envファイル」で環境変数とその値を指定
presess.env.定数名で値を取得します

コアモジュール

コアモジュールは、Node.jsに組み込みまれているモジュールです
コアモジュールはインストールせずに、利用します

下記のコマンドは、コアモジュールを一覧で表示します
node -e "console.log(require('module').builtinModules);"

pathモジュール

pathモジュールはよく利用します
pathモジュールはファイルパスからディレクトリ名やファイル名を文字列として取得します
const path = require('path')

// ディレクトリ名を取得  /a/b
path.dirname('/a/b/c.txt')

// ファイル名を取得  c.txt
path.basename('/a/b/c.txt')

fsモジュール

sample.txtファイルを読み取り、copy.txtに書き込みをします(非同期処理します)

fsモジュールの「readFile」メソッドは、非同期ファイルを読み込み
fs.readFile(ファイルのパス, 文字コード, コールバック関数)

「writeFile」メソッドは、非同期でファイルを書き込みます
fs.writeFile(ファイルのパス, ファイルの中身, コールバック関数)
*文字コードはデフォルトがutf8

今回は、コールバックではなく「util.promisify」でプロミス化します
const fs = require("fs");
const path = require("path");
const util = require("util");

const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);

readFile(path.join(__dirname, "sample.txt"), "utf8")
.then(data => {
  return writeFile(path.join(__dirname, "copy.txt"), data);
})
.catch(err => {
 console.log(err.message);
});

*重たいファイルを読み込んで処理する場合はストリームを使います(ストリームは小分けにして処理する仕組みです)

httpモジュール

簡単なHTTPサーバー
const http = require('http');
const server = http.createServer((request, response)=>{
  response.end('hello');
});
server.listen(3000);

まとめ

「JavaScriptのライブラリ管理ツール」として使っていたNode.jsですが、「PHPとなにがちがうのだろう?」と漠然とした疑問は、ある程度解消^^;

Node.jsで、一般的なウェブ開発タスクは直接サポートされていないため
通常はフレームワークを利用します
クライアントサイドのアプリケーションでは「Electron」
サーバーサイドアプリケーションでは「Express」が有名です
とても奥が深そうです💦