vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

ASP.NET Core + TypeScript ってみる 3

はじめに

今回は少~しだけ fetch の話と、 webpack を使った import / export の話です。

全然 ASP.NET Core が出てこない? 

。。。(..)_

fetch について

IE 対応

※前回使用した fetch は、 Firefox などでは問題なく動作するのですが、 IE ではまた fetch が見つからないとエラーになります。

ということで、 polyfill 版である whatwg-fetch を使用することにします。

npm install --save-dev whatwg-fetch

下記のように使うことができます。

import 'whatwg-fetch'

public async function loadBooks(): Promise< Array< Book>> {
        return await window.fetch("/books",
            {
                mode: 'cors',
            })
            .then((response) => response.json())
            .then((myJson) => JSON.parse(JSON.stringify(myJson)) as Array< Book>);
    }

(互換性モード?知らない子ですね)

import については後述します

fetch のオプション

fetch API の中で特に気になるのが、第二引数として渡しているオプション。

ここについて調べて。。。と思ったのですが、 MDN に詳しく載っているのでそこ参照ということで。。。

ただ、例えば mode や credentials といった設定で、特にログイン時にどうなるの?といった疑問はあるため次回以降で色々試してみたいと思います。

webpack を使う

さて、次は取得した Book データを Table に出力したいのですが、そろそろコード量が多くなってきました。

C# でクラスを分割するように、 import / export を使うことで TypeScript のコードも分割することができます。

その。。。はずだったのです。

WebAccessor.ts

class Book {
    public id: number = -1;
    public authorId : string = "";
    public name: string = "";
    public available: boolean = false;
}
class WebAccessor {
    public async loadBooks(): Promise< Array< Book>> {
        return await fetch("/books",
            {
                mode: 'cors',
            })
            .then((response) => response.json())
            .then((myJson) => JSON.parse(JSON.stringify(myJson)) as Array< Book>);
    }
}
export {WebAccessor, Book};

一部ラムダ式に変更してはいるものの、やっていることは前回と同じです。

Page.ts

import {WebAccessor} from "./WebAccessor";

async function greeting(){
    var accessor = new WebAccessor();
    let books = await accessor.loadBooks();
    if(books !== null &&
        books.length > 0){
        alert(books[0].name);
    }
    else{
        alert('failed');
    }
}

npx tsc -b のコンパイルは問題なく完了します。

で、これを実行するとどうなるか。

ReferenceError: exports is not defined

/(^o^)\

どうやら、 import / export がサポートされているのは ECMAScript 6 からで、今回のように古いバージョンに対応するためには RequireJS などを使って動的に解決するか、 webpack などを使って依存する JavaScript のファイルを一つのファイルにまとめる(バンドルする)必要があるようです。

そこで前から気になっていたこともあり、 webpack を使ってみることにしました。

準備

↑などを参考に、まずは必要なものをインストールします。

npm install --save-dev webpack webpack-cli ts-loader

設定ファイルをプロジェクト直下に作成します。

tsconfig は JSON ですが、こちらは普通の JavaScript ファイルなのでちょっと注意が必要?かもしれません。

webpack.config.js

var path = require('path');

module.exports = {
    mode: 'development',
    entry: './wwwroot/ts/Page.ts',
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    resolve: {
        extensions: [ '.tsx', '.ts', '.js' ]
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'wwwroot/js')
    }
};
  • mode: 無くても実行はできますが、警告が出るため素直に development / production を指定します。
  • entry: Page.ts をベースに、 ts-loader を使って依存している '.tsx' 、 '.ts' 、 '.js' ファイルをバンドルするようです。
  • output: 出力先のファイル名、ディレクトリを指定しています。

実行、そして次の課題

↓のコマンドを実行すると、 wwwroot > js に bundle.js というファイルが出来上がります。

npx webpack

後は、 HTML で読み込む JavaScript ファイルを bundle.js に置き換えます。

Index.html

< !DOCTYPE html>
< html lang="jp">
< head>
    < meta charset="UTF-8">
    < title>Home< /title>
< /head>
< body>
    Hello
    < button onclick="greeting()">Message< /button>
    < script src="../js/bundle.js">< /script>
< /body>
< /html>

これを実行すると...

ReferenceError: greeting is not defined

/(^o^)\

どうやら、デフォルトでグローバル変数や関数は定義できない(呼べない?)ようになっているようです。

なんとか解決

結局このように書くことで無事呼ぶことができるようになりました。

webpack.config.js

~省略~
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'wwwroot/js'),
        library: 'Page',
        libraryTarget: 'umd'
    }
};

Index.html

~省略~
< body>
    Hello
    < button onclick="Page.greeting()">Message< /button>
    < script src="../js/bundle.js">< /script>
< /body>
< /html>

動いた~、は良いのですが、この library とか libraryTarget って何なのでしょうか。

output.library について

によると、 JavaScript (今回は TypeScript ですが)の関数をライブラリとして外部( HTML )に公開することができる仕組みである、とのこと。

webpack のドキュメントを見ると、書き方がいくつかあり (libraryTarget で指定)、今回使用した umd の他、 var 、 amd が紹介されています。

例えば libraryTarget として var を指定した場合、下記のようになります ( Index.html は同じです)。

webpack.config.js

~省略~
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'wwwroot/js'),
        library: 'Page',
        libraryTarget: 'var'
    }
};

Page.ts

import {WebAccessor} from "./WebAccessor";

async function greeting(){
    try {
        var accessor = new WebAccessor();
        let books = await accessor.loadBooks();
        if (books !== null &&
            books.length > 0) {
            alert(books[0].name);
        }
    }catch (e) {
        alert(e.message)
    }
}
var Page = greeting();

TypeScript だと書き方が異なるということなのか、 amd の方法は静的解析の時点でエラーになってしまい、うまくできませんでした。

ドキュメントが読み込めていないのだと思いますが、 var と umd それぞれの利点がよく分かっていないため、なんとなく umd の方がまとまってて良さそうだな~くらいに思ってはいます。

参照