- Contents -
前回書いた記事の続きで、
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 を用意しないといけないのがまた面倒ですね。
設定項目が分散しているせいで、余計にわかりにくのが辛い。