概要
以下をやってみた記録。良記事に感謝。
- https://qiita.com/EBIHARA_kenji/items/25e59f7132b96cb886f3
- https://qiita.com/EBIHARA_kenji/items/e6da1c3d6d16cf07b60a
- https://qiita.com/EBIHARA_kenji/items/1a043794014dc2f3a7db (途中まで)
環境
Node.jsとnpmのバージョン
$ node -v
v10.13.0
$ npm -v
6.4.1
npmプロジェクトの作成
フォルダとpackage.json作成
$ mkdir electron-react-app
$ cd electron-react-app
$ npm init
package.json
{
"name": "electron-react-app",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
gitで作業を記録することにする。
以降、記事には記載しないが、適時コミットしてワークショップを進める。
gitの初期設定
$ git init
$ git add .
$ git commit -m "npm init"
$ git remote add origin (your remote repo)
$ git push -u origin master
日本語対策
$ set LANG=ja_JP.UTF-8
.gitignore
node_modules/
/dist/
必要なライブラリをインストールする
各ライブラリの詳細は、元記事参照。
各種ライブラリインストール
$ npm install --save react react-dom redux react-redux styled-components
$ npm install --save-dev electron typescript tslint webpack webpack-cli ts-loader tslint-loader
$ npm install --save-dev @types/react @types/react-dom @types/redux @types/react-redux
ここから二日目の内容です。
TypeScript コンパイラ・オプションファイルの作成
tsconfig.json作成
$ "./node_modules/.bin/tsc" --init
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"jsx": "react",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"sourceRoot": "./tsx",
"inlineSourceMap": true,
"inlineSources": true
},
"include": [
"./ts/**/*"
]
}
tslint 設定ファイル の作成
tslint.json作成
$ "./node_modules/.bin/tslint.cmd" --init
tslint.json
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"quotemark": [
true,
"single",
"jsx-double"
]
},
"rulesDirectory": []
}
webpack.config.js の作成
2か所タイポがあったのは秘密(以下は修正済み)。
webpack.config.js
const path = require('path');
module.exports = {
// node.js で動作することを指定する
target: 'node',
// 起点となるファイル
entry: './ts/index.tsx',
// webpack watch したときに差分ビルドができる
cache: true,
// development は、source map fileを作成。再ビルド時間の短縮などの設定となる
mode: 'development', // "production" | "development" | "none"
// ソースマップのタイプ
devtool: 'source-map',
// 出力先設定 __dirname は node ではカレントディレクトリのパスが格納される変数
output: {
path: path.join(__dirname, 'dist'),
filename: 'index.js'
},
// ファイルタイプ毎の処理を記述する
module: {
rules: [{
// 正規表現で指定する
// 拡張子 .ts または .tsx の場合
test: /\.tsx?$/,
// ローダーの指定
// TypeScript をコンパイルする
use: 'ts-loader'
}, {
// 拡張子 .ts または .tsx の場合
test: /\.tsx?$/,
// 事前処理
enforce: 'pre',
// TypeScript をコードチェックする
loader: 'tslint-loader',
// 定義ファイル
options: {
configFile: './tslint.json',
// airbnb という JavaScript スタイルガイドに従うには下記が必要
typeCheck: true,
},
}],
},
// 処理対象のファイルを記載する
resolve: {
extensions: [
'.ts',
'.tsx',
'.js', // node_modules のライブラリ読み込みに必要
]
},
};
HTML の作成
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Electronチュートリアル</title>
</head>
<body>
<div id="contents"></div>
<script src="dist/index.js"></script>
</body>
</html>
main.js の作成
main.js
const {
app,
BrowserWindow
} = require('electron')
// レンダープロセスとなるブラウザ・ウィンドウのオブジェクト。
// オブジェクトが破棄されると、プロセスも終了するので、グローバルオブジェクトとする。
let win
function createWindow() {
// ブラウザウィンドウの作成
win = new BrowserWindow({
width: 800,
height: 600
})
// index.htmlをロードする
win.loadFile('index.html')
// 起動オプションに、"--debug"があれば開発者ツールを起動する
if (process.argv.find((arg) => arg === '--debug')) {
win.webContents.openDevTools()
}
// ブラウザウィンドウを閉じたときのイベントハンドラ
win.on('closed', () => {
// 閉じたウィンドウオブジェクトにはアクセスできない
win = null
})
}
// このメソッドは、Electronが初期化を終了し、
// ブラウザウィンドウを作成する準備ができたら呼び出される。
// 一部のAPIは、このイベントが発生した後にのみ使用できる。
app.on('ready', createWindow)
// 全てのウィンドウオブジェクトが閉じたときのイベントハンドラ
app.on('window-all-closed', () => {
// macOSでは、アプリケーションとそのメニューバーがCmd + Qで
// 明示的に終了するまでアクティブになるのが一般的なため、
// メインプロセスは終了させない
if (process.platform !== 'darwin') {
app.quit()
}
});
app.on('activate', () => {
// MacOSでは、ドックアイコンがクリックされ、
// 他のウィンドウが開いていないときに、アプリケーションでウィンドウを
// 再作成するのが一般的です。
if (win === null) {
createWindow()
}
});
コンパイル確認用スクリプトの記述
ts/index.tsx
import React from 'react';
import ReactDom from 'react-dom';
const container = document.getElementById('contents');
ReactDom.render(
<p>こんにちは、世界</p>,
container,
);
コンパイルの確認
webpack実行とelectron起動
$ "./node_modules/.bin/webpack"
$ "./node_modules/.bin/electron" ./
npm script を利用する
package.json
diff --git a/package.json b/package.json
index a98d36d..f69de75 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,8 @@
"description": "",
"main": "main.js",
"scripts": {
+ "build": "webpack",
+ "start": "electron ./",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
webpack実行とelectron起動
$ npm run build
$ npm start
ここから三日目の内容です。
child_state の作成
ts/IUser.ts
export default interface IUser {
name: string;
}
export const initUser: IUser = {
name: '',
};
component の作成
ラベル付きテキストボックスの作成
ts/components/TextBox.tsx
import React from 'react';
// 親コンポーネントから渡されるプロパティを定義する
interface IProps {
// ラベル文字列
label: string;
// テキストボックスのタイプ
type: 'text' | 'password';
// テキストボックスに表示する値
value: string;
// 値の確定時にその値を親プロパティが取得するためにコールバック関数を提供する
onChangeText: (value: string) => void;
}
export class TextBox extends React.Component<IProps, {}>{
// DOMエレメントをレンダリングする
public render() {
// ラベルが設定されていない場合は、label を出力しない
const label = (!!this.props.label) ?
<label>{this.props.label}</label> :
null;
return (
<span>
{label}
<input name="username" type={this.props.type} value={this.props.value}
onChange={this.onChangeText}></input>
</span>
);
}
// 値を変更したら、store.dispatch で action を reducer に渡して、state を更新する。
// state が更新されたら component の prop が更新され、再レンダリングされ、テキストボックスの内容が変更される。
private onChangeText = (e: React.ChangeEvent<HTMLInputElement>) => {
this.props.onChangeText(e.target.value);
}
}
続きは次回。
感想
- Electronはもちろんだけど、周辺ツールについても、とても勉強になる
- TypeScriptの書き方も同上
以上