LoginSignup
18
19

More than 5 years have passed since last update.

Angular2 & TypeScript で サーバサイドとクライアントサイドのコードを共通化する

Posted at

Angular2のBetaがリリースされたので、Angular2 & TypeScript で、サーバサイドとクライアントサイドのコードを共通化(isomorphic)できるかを試していました。
いくつか、ハマった点があったので整理しておきたいと思います。

TypeScript の module タイプの違い

Angular2のチュートリアルでは、tsconfig.jsonのmoduleは、systemになっており、systemjsを利用してモジュールをロードする方式になっています。Browser上で動かすには、systemjsで上手く動きます。

トランスパイル前のTypeScript
export * from './model/user';
export * from './util/validator';
systemでトランスパイルされたTypeScript
System.register(['./model/user', './util/validator'], function(exports_1) {
    function exportStar_1(m) {
        var exports = {};
        for(var n in m) {
            if (n !== "default") exports[n] = m[n];
        }
        exports_1(exports);
    }
    return {
        setters:[
            function (user_1_1) {
                exportStar_1(user_1_1);
            },
            function (validator_1_1) {
                exportStar_1(validator_1_1);
            }],
        execute: function() {
        }
    }
});
//# sourceMappingURL=model.js.map

しかし、NodeJSで動かそうとすると、systemjsの設定を書かなければならず、面倒でした。NodeJSで動かすならcommonjsの方が楽です。

commonjsでトランスパイルされたTypeScript
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require('./model/user'));
__export(require('./util/validator'));
//# sourceMappingURL=model.js.map

commonjsであれば、NodeJSでそのまま実行することができます。

<解決方法> tsconfig.json を 2つ用意する

NodeJSでsystemjsの設定を頑張れば、できるかもしれないですが、面倒だったので諦めて、サーバサイドとクライアントサイドのTypeScriptのコンパイルを分けることで、moduleの読み込み方法の違いによる問題を解決しました。

フォルダを下記のように分け、clientフォルダにもtsconfig.jsonを作ります

├── client            :クライアントのソース
│   └── tsconfig.json :クライアント用のtsconfig.jcon
├── server            :サーバのソース
├── share             :共通のソース
└── tsconfig.json     :サーバ用のtsconfig.json

サーバ用のtsconfig.jsonでは、excludeにclientを入れ、トランスパイル対象から外します。

サーバサイド用のtsconfig.json(プロジェクト直下に配置)
{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false,
        "outDir": "dist/server"
    },
    "exclude": [
        "node_modules",
        "client",
        "dist"
    ]
}

クライアント用のtsconfig.jsonはclientフォルダに置くのでそのままにします。

クライアントサイド用のtsconfig.json(clientディレクトリに配置)
{
    "compilerOptions": {
        "target": "es5",
        "module": "system",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false,
        "outDir": "../dist/client"
    },
    "exclude": [
        "node_modules"
    ]
}

これで、コマンドラインから、tsc -wtsc -p ./client -w を起動すると、同じTypeScriptのファイルをクライアントでもサーバでも動かすことができます。

concurrently で複数のコマンドを同時に起動する

ちなみに、複数のコマンドを実行するのは面倒なので、concurrentlyを使うと便利です。

package.json
{
  "name": "my-project",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "tsc:client": "tsc -w -p ./client/",
    "tsc:server": "tsc -w",
    "start": "concurrent \"npm run tsc:server\" \"npm run tsc:client\" "
  },
  "dependencies": {
  },
  "devDependencies": {
    "concurrently": "^1.0.0",
    "livereload": "^0.4.0",
    "typescript": "^1.7.3"
  },
  "license": "ISC"
}

とpackage.jsonを作っておくと、npm startで2のコンパイラーを一つのコマンドで動かすことができます。

作ったモノ:

Angular2 + Typescript + Express + MongoDB のサンプルプロジェクト※

※ ただし、angular/universal が動かせず、サーバサイドレンダリングはできていません。

18
19
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
18
19