Erectron + React + TypeScript でアプリ開発をする。

前回書いた記事の続きで、
Electron + React.js でアプリ開発をする。

今回は、TypeScriptを利用する場合について記載していきます。

React.js や、様々なライブラリを使っていくと、型チェックが欲しいもの。
TypeScriptを使うとそこらへんちゃんとやってくれたり、
Javaのようなジェネリクスのようなものを書けたりするので、これから覚えて損はなさそう。

こちらも今回、作ったものはテンプレートとして下記リポジトリで公開しておきます。
https://gitlab.com/tyabuta/template-electron-reactjs-typescript

主要ツール

使っていく主要なものは以下のとおり。

  • Electron
  • React.js
  • TypeScript
  • Webpack

React.jsはjsxを使うので、トランスパイラが必要になってくる。
また、TypeScriptもjsコードへの変換が必要がとなる。

そこらへんを全部請け負ってくれるのが、Webpackだ!

Babel, Gulp などの技術もあるが最近は、
わかりやすくまとめられているWebpackが個人的にはオススメ構成である。

ディレクトリ構成

最初に最終的にどんなディレクトリ構成になるのかを記載しておきます。

.
├── index.js
├── node_modules
├── package.json
├── public
│   ├── build
│   │   ├── app.js
│   │   ├── app.js.map
│   │   ├── vendor.js
│   │   └── vendor.js.map
│   └── index.html
├── src
│   └── index.tsx
├── tsconfig.json
└── webpack.config.js

public/build ディレクトリは、src配下のファイルがwebpackにより
TypeScritpのトランスパイル、JSXのトランスパイルがされたうえで
バンドルされたファイルが出力されます。(app.js)

設定しないといけないファイルは、

  • package.json
  • webpack.config.js
  • tsconfig.json

最低限必要なコーディングファイルは、

  • index.js
  • public/index.html
  • src/index.tsx

プロジェクト作成

npm init でpackage.jsonを作成したあとは、以下のパッケージをインストールする。

npm install

# electron
npm install -S electron

# react.js
npm install -S react react-dom

# TypeScript
npm install -D typescript
npm install -D awesome-typescript-loader
npm install -D source-map-loader

# React.js の型定義ファイル
npm install -D @types/react
npm install -D @types/react-dom

# webpack
npm install -D webpack

TypeScritpの場合、型定義ファイルが必要になってくる。
@typs/react などがそれで、インストールし忘れた場合は、
Webpackでのコンパイル時に以下のような@types のインストールを促すエラーメッセージがでてくれる。

ERROR in [at-loader] ./src/index.tsx:1:24
    TS7016: Could not find a declaration file for module 'react'. 'node_modules/react/index.js' implicitly has an 'any' type.
  Try `npm install @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react';`

ERROR in [at-loader] ./src/index.tsx:2:27
    TS7016: Could not find a declaration file for module 'react-dom'. 'node_modules/react-dom/index.js' implicitly has an 'any' type.
  Try `npm install @types/react-dom` if it exists or add a new declaration (.d.ts) file containing `declare module 'react-dom';`

設定ファイル類

package.json

package.json はこんな感じになります。

"main" にかかれているindex.jsファイルが、
electronでのメインプロセス側の処理になります。

"dependencies" には、electronとreactが入って、
"devDependencies" には、TypeScirptとWebpackがインストールされている状態ですね。

{
  "name": "template-electron-reactjs-typescript",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "electron": "electron .",
    "webpack": "webpack",
    "webpack-watch": "webpack --watch --display-error-details"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "electron": "^1.8.2",
    "react": "^16.2.0",
    "react-dom": "^16.2.0"
  },
  "devDependencies": {
    "@types/react": "^16.0.40",
    "@types/react-dom": "^16.0.4",
    "awesome-typescript-loader": "^3.5.0",
    "source-map-loader": "^0.2.3",
    "typescript": "^2.7.2",
    "webpack": "^3.11.0"
  }
}

webpack.config.js

こちらが、webpack.config.js です。

const path = require('path');
const webpack = require('webpack');

module.exports = {
    target:"electron-renderer",
    devtool: 'source-map',

    entry: {
        app: './src',
    },

    // バンドルファイルの出力場所
    output: {
        filename: "[name].js",
        path: path.resolve(__dirname, 'public/build')
    },

    resolve: {
        modules: [
            // srcディレクトリをimport解決のrootに設定
            path.resolve(__dirname, "src"),
            "node_modules",
        ],
        extensions: [".ts", ".tsx", ".js", ".jsx"]
    },

    module: {
        rules: [
            {
                // TypeScript のトランスパイル設定
                test: /\.tsx?$/, 
                loader: "awesome-typescript-loader"
            },
        ]
    },

    plugins: [
        // node_modules配下を、vendor.js としてバンドルする。
        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",
            minChunks: function (module) {
                return module.context && module.context.indexOf("node_modules") !== -1;
            }
        })
    ]
};

tsconfig.json

{
  "compilerOptions": {
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es6",
    "jsx": "react",
    "baseUrl": "./src"
  }
}

webpack.config.js と tsconfig.json
お互いに似たような設定項目があるが、どちらの設定が有効になっているのかはちょっとわからない。。

tsconfig側のoutputs等は無視されているようだ。
sorceMapとかも無視されていそう。
(未確認)

コーティング

index.js

ほぼ、electron公式に書いてあるサンプルソースそのままです。

loadURLのところで、
public/index.html をブラウジングするように書き換えているだけになります。

const {app, BrowserWindow} = require('electron');
const path = require('path');
const url = require('url');

let win;

function createWindow() {
    win = new BrowserWindow({width: 800, height: 600});
    win.loadURL(url.format({
        pathname: path.join(__dirname, 'public', 'index.html'),
        protocol: 'file:',
        slashes: true
    }));

    // Open the DevTools.
    win.webContents.openDevTools();

    win.on('closed', () => {
        win = null
    })
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit()
    }
});

app.on('activate', () => {
    if (win === null) {
        createWindow()
    }
});

public/index.html

React.js のルート要素になるdivタグと、
public/build配下に出力されたファイルを、scriptタグで読み込んでいるだけです。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Hello World!</title>

        <script src="build/vendor.js"></script>
    </head>

    <body>
        <div id="root"></div>
        <script src="build/app.js"></script>
    </body>
</html>

src/index.tsx

React.js のハロワのサンプルソースを、
TypeScript用にインポート文を書き換えただけです。

import * as React from 'react';
import * as ReactDOM from 'react-dom';

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);

起動してみる

webpackでバンドル。

npx webpack
[at-loader] Using typescript@2.7.2 from typescript and "tsconfig.json" from tsconfig.json.
[at-loader] Checking started in a separate process...
[at-loader] Ok, 0.604 sec.

Hash: 047f28bef06b8cab15f4
Version: webpack 3.11.0
Time: 2957ms
        Asset       Size  Chunks                    Chunk Names
       app.js  402 bytes       0  [emitted]         app
    vendor.js     721 kB       1  [emitted]  [big]  vendor
   app.js.map  458 bytes       0  [emitted]         app
vendor.js.map     853 kB       1  [emitted]         vendor
  [13] ./src/index.tsx 253 bytes {0} [built]
    + 25 hidden modules

エレクトロン起動!!

npx electron .

おしまい

TypeScriptだと、Jsxのトランスパイルもやってくれるので
Babel等のインストールが不要です。

そのかわり、tsconfig.json を用意しないといけないのがまた面倒ですね。
設定項目が分散しているせいで、余計にわかりにくのが辛い。

コメントを残す