CodeZine(コードジン)

特集ページ一覧

「Wijmo(ウィジモ)」とElectron、Reactに、TypeScriptを組み合わせてスムーズなアプリ開発を実現

ECMAScript 5に準拠した高速・軽量なJavaScript UIライブラリ「Wijmo 5」の活用 第17回

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2021/01/14 12:00

 本連載では、グレープシティが提供するJavaScriptライブラリWijmo(ウィジモ)について、サンプルとともに利用例を紹介します。WijmoはさまざまなJavaScriptフレームワークやライブラリと組み合わせて利用できますが、今回はJavaScriptライブラリReactと、Web技術でデスクトップアプリが開発できるフレームワークElectron、そしてTypeScriptを利用し、アプリケーションを開発します。

はじめに

 前回に引き続き、グレープシティがHTML/JavaScript環境に向けて提供するJavaScriptライブラリWijmo(ウィジモ)と、さまざまなJavaScriptフレームワークやライブラリと組み合わせてアプリケーションを作成します。

 前回は、Web技術デスクトップアプリを開発するフレームワークであるElectronとReactを利用しました。今回はさらにTypeScriptを組み合わせていきます。

 なお、今回もサンプルコードは以下のリポジトリに用意しています。

TypeScriptとは

 Microsoftが開発するTypeScriptは、動的型言語であるJavaScript(ECMAScript)に、静的型を導入した言語です。

 ECMAScriptは、国際機関が制定しているECMA-262という規格で、JavaScriptから言語仕様のみを抜き出したものです。毎年6月ごろに仕様が追加されています。JavaScriptとは、言語仕様としてのECMAScriptに、Webブラウザ向けの仕様であるDOM(Document Object Model)などを総合したものと言えます。

 すなわち、TypeScriptはECMAScriptの上位互換として、静的型の機能を追加した言語仕様ということです。また、TypeScriptのコードをECMAScriptに変換するコンパイラ(TypeScript compiler)も提供されています。

 今やJavaScript開発はSPA(Single Page Application)が当たり前となり、片手間で開発できるものではなくなってきています。TypeScriptはそういった大規模・高度化している開発を、ある程度安全にできるため、世界的にもシェアを伸ばし続けている人気の言語です。

 コンパイラの設定によっては型チェックなどを緩めることも可能なので、JavaScriptのコードを段階的に移行できることも人気のポイントです。

型の恩恵

 例えば、Reactのコンポーネントを型定義しておくと、呼び出し元で誤ったプロパティを入れたときや、必須プロパティが足りないときに、コンパイラが静的にエラーを出してくれます。

// Labelコンポーネント
const Label: React.FC<{
  color: string;
  label: string;
  size: number;
}> = ({ color, label, size }) => {
  return <label style={{ color, fontSize: ${size}px }}>{label}</label>;
};

 こうして定義したLabelコンポーネントは、<Label color="#cc0000" label="ラベル" size={24} />のように利用します。

Labelコンポーネントを正しく呼び出した場合
Labelコンポーネントを正しく呼び出した場合

 このコンポーネントは、colorlabelsizeの3つのパラメータの指定が必須であり、colorlabelは文字列(string)で、sizeは数値型(number)でなければいけません。

 例えば、<Label color={1} label="ラベル" size={24} />のように誤ったプロパティを渡そうとすると、Type 'number' is not assignable to type 'string'.という、数値型と文字列型が食い違っていることを知らせるエラーが出ます。

型を間違えた場合
型を間違えた場合

 また<Label color="#cc0000"/>のように必須プロパティを省略した場合には、Type '{ color: string; }' is missing the following properties from type 'Props': label, sizeという、labelsizeが足りないことを知らせるエラーが出ます。

必須パラメータを省略した場合
必須パラメータを省略した場合

 このようにTypeScriptを使えば、関数の引数やReactコンポーネントのプロパティに指定すべき値を必須にしたり、型を指定したりすることで、不注意でバグを出してしまう可能性を減らせます。

IDE支援

 TypeScriptの効能はこれだけではありません。

 Web開発で人気のあるVisual Studio CodeはTypeScriptコンパイラを使って、IDE機能を実現しています。JavaScriptで書かれたコードでも、ある程度のIDEによる支援を受けられますが、本領を発揮できるのはTypeScriptのコードです。

 先ほど紹介したLabelのようなコンポーネントがいっぱいあるプロジェクトに新しく入ったとします。いちいちラベルコンポーネントの使い方を他の人に聞き回るのも非効率でしょう。

 VSCodeでReact+TypeScript開発を行う場合、その程度の情報であれば、エディタ(IDE)が教えてくれます。

function App() {
  return <L
}

 「L」まで打ち込んだ状態で、スペルを覚えていなかったとしても、IDEが候補を出してくれます。

候補が出る
候補が出る

 先頭にあるLabelを選択すると、

function App() {
  return <Label
}

 この通り補完されます。

 パラメータはどうでしょうか? 先ほどの必須パラメータを省略したときと同じ挙動を示してくれます。

Labelをマウスホバーするとエラーとして教えてくれる
Labelをマウスホバーするとエラーとして教えてくれる

 Labelコンポーネントがどう定義されているか表示されていますし、エラーメッセージとしてcolorlabelsizeが必須パラメータであることを教えてくれます。

 では、パラメータを入力していきましょう。colorのためにcを打ち込んだ時点で候補が出てきます。

function App() {
  return <Label c
}
cまで打ち込んだときの候補
cまで打ち込んだときの候補

 colorchildren?の2択に絞り込めます。children?のように末尾に?がついているものは省略可能です。

 ここでもcolorstring型であると教えてくれるので、colorに該当しそうな文字列を指定すれば大丈夫です。

 同様の手順を踏んで、colorlabelsizeを指定すれば、正しくLabelコンポーネントを利用できるでしょう。

JSDocを活用する

 コンポーネントの設計が明確、つまりプロパティの名前がわかりやすく、型が適切であれば使い方がある程度理解できるはずですが、より適切な開発をするためには JSDocと呼ばれる、コード内に記述するドキュメントを活用するといいでしょう。

/** ラベルコンポーネント */
const Label: React.FC<{
  /** CSSのcolorで指定可能なカラーコード */
  color: string;
  /** ラベルの内容 */
  label: string;
  /** フォントサイズ(px) */
  size: number;
}> = ({ color, label, size }) => {
  return <label style={{ color, fontSize: ${size}px }}>{label}</label>;
};

 このように/** 中身の説明 */をコメントするだけで、参照可能なドキュメントになります。コンポーネント名や関数名、引数名やプロパティを、マウスホバーすると参照できます。

Label
Label

 Labelをマウスホバーした場合は「ラベルコンポーネント」という説明が表示されるようになりました。

color
color

 colorなら「CSSのcolorで指定可能なカラーコード」という説明が表示されます。

TypeScriptでより快適な開発を

 ここまでの説明の通り、TypeScriptはECMAScriptに静的型を導入した言語であり、大規模・複雑化するJavaSrcipt開発を、より安全に行える人気の言語です。関数や、Reactコンポーネントなどに型をつけることで、引数やプロパティの型を、適切に制限できるため、誤った使い方を事前に検出できます。

 VSCodeなどのIDEを使えば、コード入力中の候補や、使い方についての表示を出してくれるため開発効率を上げることができます。特にJSDocと呼ばれるコード内ドキュメントを活用すれば、IDE上で簡単に参照できます。

 次のページではReact+TypeScript+Electron+Wijmoのコードを書きながらTypeScriptのパワーを見ていきましょう。

生産性を描画するアプリを作る

 それでは、ElectronとWijmoを活用したアプリをTypeScriptで書いていきましょう。セットアップ手順は前編に書いた通りです。

  1. プロジェクトの作成、パッケージインストール
  2. package.jsonを変更
  3. public/electron.jsとpublic/preload.jsを追加
  4. src/@types/global.d.tsを追加
  5. @grapecity/wijmo.react.allをインストール

 前編の記事からの続きでも構いませんし、今回新たに、1~5のセットアップをしてから行っても構いません。

 今回は、データをビジュアル化するためにWijmoのチャート「FlexChart」やデータグリッド「FlexGrid」を使います。これらのコンポーネントは、利用者が定義したデータ型の配列を渡して描画できるものです。

 そこでまずはTypeScriptでデータ型を定義してみましょう。

データ型を定義する

 TypeScriptではtype Data = ...のように型を定義します。

// src/types.ts
export type Data = {
  /** 年月2020/01のような形式 */
  ym: string;
  /** 稼働時間合計 */
  time: number;
  /** 生産数 */
  count: number;
};

 src/types.tsData型を定義しました。Data型は年と月を表現するymという文字列、稼働時間のtimeという数値型、生産数のcountという数値型を持ちます。

生産性表示コンポーネントを作成する

 次に、Wijmoのチャートとデータグリッドを組み合わせた、生産性表示コンポーネントProductivityを作成します。

// src/Productivity.tsx
import { useMemo } from "react";
import * as wjGrid from "@grapecity/wijmo.react.grid"; //(1)グリッドを読み込む
import * as wjChart from "@grapecity/wijmo.react.chart"; //(2)チャートを読み込む
import * as wj from "@grapecity/wijmo"; //(3)CollectionView を読み込む
import { Data } from "./types";

//(4)Productivity コンポーネント
/** 生産性描画コンポーネント
 *  @param data 生産性データの配列
 */
const Productivity: React.FC<{ data: Data[] }> = ({ data }) => {
  //(4-1)プロパティで受け取った Data型の配列を、WijmoのCollectionViewに変換する
  const items = useMemo(() => {
    return new wj.CollectionView<Data>(data);
  }, [data]);

  return (
    <div>
      <wjChart.FlexChart itemsSource={items} bindingX="ym">
        <wjChart.FlexChartSeries name="稼働時間" binding="time" />
        <wjChart.FlexChartSeries
          name="生産数"
          binding="count"
          chartType="LineSymbols"
        />
        <wjChart.FlexChartLegend position="Bottom" />
      </wjChart.FlexChart>
      <wjGrid.FlexGrid itemsSource={items}>
        <wjGrid.FlexGridColumn header="年月" binding="ym" width="*" />
        <wjGrid.FlexGridColumn header="稼働時間" binding="time" width="*" />
        <wjGrid.FlexGridColumn header="生産数" binding="count" width="*" />
      </wjGrid.FlexGrid>
    </div>
  );
};

export default Productivity;

 (1)と(2)でそれぞれWijmoのFlexGridとFlexChartを読み込んでいます。

 (3)では、Wijmoのコンポーネントで使われるCollectionViewクラスを読み込んでいます。

 (4)が、今回作るProductivityコンポーネントで、先ほど作成したData型の配列をdataというプロパティで受け取るものです。const Productivity: React.FC<{ data: Data[] }> = ...ProductivityというReactの関数型コンポーネントを定義しています。

 const name = 'Wijmo';のようなシンプルな変数定義と違う点は、: React.FC<{ data: Data[] }>でしょう。これはTypeScriptの型アノテーションと呼ばれるもので、React.FCというReact関数型コンポーネントであり、コンポーネントのプロパティは、{ data: Data[] }つまり、Data型の配列であるdataを持つという宣言をしています。

 少し複雑な型アノテーションであるため、もっとシンプルなケースを説明します。例えば文字列型ならconst name: string = 'Wijmo';のようになりますし、数値型ならconst yaer: number = 2020;になります。

 ただし、実際には数値や文字列の変数定義のように、型がわかりやすいものは、型推論というTypeScriptの賢い機能があるため、わざわざ: string: numberを指定することは必要はありません。

 次に、Productivity関数型コンポーネントの実際の中身を見ていきましょう。

 (4-1)では、ReactHooksのuseMemoというフック関数を使って、Data型の配列を、WijmoのCollectionViewオブジェクトに変換しています。useMemoは、第2引数の配列が変更されない限りは同じ値を返すという仕様をしています。キャッシュに似た仕組みであるメモ化をしてくれるものです。

サンプルデータを描画する

 それでは、作成したProductivityコンポーネントを使って、実際にサンプルデータを用意して描画してみましょう。

// src/App.tsx
import React from "react";
import Productivity from "./Productivity";
import { Data } from "./types";

import "@grapecity/wijmo.styles/wijmo.css";
import "@grapecity/wijmo.cultures/wijmo.culture.ja";

//(1)Data型配列のサンプルデータを作成する関数
const createData: () => Data[] = () => {
  return [
    {
      ym: "2018/09",
      time: 160,
      count: 200,
    },
    {
      ym: "2018/10",
      time: 200,
      count: 300,
    },
    {
      ym: "2018/11",
      time: 120,
      count: 150,
    },
    {
      ym: "2018/12",
      time: 240,
      count: 200,
    },
  ];
};

const App: React.FC = () => {
  return <Productivity data={createData()} />;
};

export default App;

 (1)ではData型配列のサンプルデータを作成する関数を定義しています。ここでは、() => Data[]という型アノテーションをつけています。引数はなしで、戻り値がData型の配列であるという宣言になります。

サンプルデータを表示
サンプルデータを表示

IPCを使ってJSONファイルを読み込んで描画する

 ElectronではIPCを使ってメインプロセスに処理を任せることで、Webブラウザだけではできないことを実現します。任意のファイルを読み書きしたり、Node.jsの豊富なnpmパッケージを利用したり、さまざまなネイティブモジュールを使ったりすることも可能です。

 今度は、Elecronが提供するOSネイティブの、ファイルオープンダイアログを使ってJSONファイルを読み込み、Productivityコンポーネントで描画してみましょう。

global.d.tsを書き換える

// src/@types/global.d.ts

interface Window {
  //(1)対象はWindowというインターフェース
  ipc: {
    openFile: () => Promise<{ filename: string; content: string } | null>; //(2)openFile関数を定義
  };
}

 前編で軽く解説しましたが、改めて解説します。d.tsというのは、TypeScriptで型宣言のみを行うファイルです。

 (1)で通常、Webブラウザ上でプログラミングしているときに使うwindowグローバルオブジェクトにipcというオブジェクトを追加しています。

 (2)でopenFileという関数を定義します。関数の引数はなしでPromiseを返すものです。前編でも使ったPromiseは非同期処理を行うための便利な仕組みです。

 Promise<{ filename: string; content: string } | null>;の型アノテーションは、非同期処理が完了した際に{ filename: string; content: string }というオブジェクトまたはnullを引き渡してくれる型を意味します。

window.ipc.openFile().then((result) => {
  if (result) {
    // nullではないためオブジェクト
    console.log(result.filename);
    console.log(result.content);
  } else {
    console.log("result is NULL!");
  }
});

 このように使います。

preload.jsを書き換える

 次に、public/preload.jsを書き換えます。

// public/preload.js
const { contextBridge, ipcRenderer, remote } = require("electron");

//(1)contextBridgeを使って、メインプロセスの機能をレンダリングプロセスに提供する
contextBridge.exposeInMainWorld("ipc", {
  openFile: () => {
    return ipcRenderer.invoke("openFile");
  },
});

 前編とは異なり、先ほど型定義をした戻り値Promise<{ filename: string; content: string } | null>があるため、ipcRenderer.invoke("openFile")の戻り値をそのままreturnしています。

electron.jsを書き換える

 public/electron.jsの末尾にopenFileの処理を追加します。

//(1)invokeできるopenFileを定義する
ipcMain.handle("openFile", (event) => {
  //(2)処理の中身が非同期なのでasync関数を定義
  const read = async () => {
    //(3)dialog.showOpenDialogでファイルオープンダイアログを開く
    const { canceled, filePaths } = await dialog.showOpenDialog(null, {
      properties: ["openFile"],
      title: "JSONファイルを開く",
      defaultPath: ".",
      filters: [{ name: "JSON file", extensions: ["json"] }],
    });

    //(4)canceledなら、nullを返す
    if (canceled) {
      return null;
    }

    //(5)ダイアログで指定されたファイルをUTF-8とみなして読み込む
    const content = await fs.promises.readFile(filePaths[0], {
      encoding: "utf-8",
    });

    //(6)filenameとcontentのオブジェクトを返す
    return { filename: filePaths[0], content };
  };

  //(7)定義されたasync関数を実行する
  return read();
});

 (1)openFileという名前でinvokeできる定義をしています。

 (2)は前編でも登場したasync関数で、ダイアログの処理とファイル読み込みという、2つの非同期処理を束ねるのに便利なため、関数を宣言しています。async関数の戻り値はPromiseになります。

 (3)ではdialog.showOpenDialog関数で、OSネイティブのファイルオープンダイアログを開いています。

 awaitは前編の記事でも説明した通り、async関数の中でのみ使えるキーワードで、awaitの後ろに指定したPromiseの完了を待つものです。

 dialog.showOpenDialog関数の戻り値はPromiseであり、普通は処理が非同期で行われますが、awaitにより、処理が完了するまで待つという、同期処理と同様の振る舞いになります。Promisethenメソッドを並べるメソッドチェーンをしなくて済むこと、普通のプログラミングと同じように記述できてわかりやすいなどのメリットがあります。

 さて、関数に設定しているオプションの説明をします。

 properties配列にmultifileを追加すれば複数のファイルを開くこともできます。

 defaultPathは、ダイアログの初期のパスです。指定しなければOSが自動で設定し、ホームディレクトリか、前回開いたディレクトリなどになるはずです。今回はカレントディレクトリである.を指定しています。

 filtersは、今回はJSONファイルなので、拡張子がjsonのものだけ開くために設定しています。

 (4)では、ダイアログの結果を見てcanceledの場合nullを返しています。

 (5)ではfilePaths[0]に、ダイアログで指定されたファイル名が、フルパスで入っているため、Node.jsのfs.promises.readFileでファイル読み込みをします。第2引数に{encoding: "utf-8"}を指定しているのはreadFileが返してくるデータをUTF-8文字列に固定するためです。もし別の文字コードが入る可能性があればencodingを指定せず文字コード判定をする必要があるでしょう。

 (6)では、filenameと読み込んだcontentのオブジェクトを返しています。

 (7)では、(2)で定義したasync関数を呼び出しています。async関数の呼び出しなので、戻り値はPromiseです。

 ここまでで、global.d.tsの型定義と、preadload.jsで、メインプロセスへのinvokeと、electron.jsでメインプロセス上で実行されるファイルオープンダイアログおよびファイル読み込み処理の実装を行いました。

 これでやっとレンダラープロセスから、window.ipc.openFileを呼び出すことができるようになりました。

App.tsxを書き換える

 先ほど、createData関数で固定のサンプルデータを生成していたApp.tsxを、openFileによって読み込んだJSONファイルのデータを描画するように書き換えます。

// src/App.tsx
import React, { useState, useCallback } from "react";
import Productivity from "./Productivity";
import { Data } from "./types";

import "@grapecity/wijmo.styles/wijmo.css";
import "@grapecity/wijmo.cultures/wijmo.culture.ja";

const App: React.FC = () => {
  //(1)filename, dataをステート定義
  const [filename, setFilename] = useState("not opend");
  const [data, setData] = useState<Data[]>([]);

  //(2)ダイアログを開く、ボタンのコールバックを定義
  const handleDialog = useCallback(() => {
    //(2-1)window.ipc.openFile関数を呼び出して Promise の処理
    window.ipc.openFile().then((res) => {
      //(2-2)戻り値がnullでなければ
      if (res) {
        // (2-3)setFilenameとsetDataでステートを更新する
        setFilename(res.filename);
        setData(JSON.parse(res.content));
      }
    });
  }, []);

  return (
    <>
      <div>
        <span>{filename}</span>
        <button onClick={handleDialog}>JSON File Open</button>
      </div>
      {data.length > 0 && <Productivity data={data} />}
    </>
  );
};

export default App;

 (1)ではReactHooksのuseState関数でステートを定義しています。filenameは文字列型でファイル名、dataData配列型です。useStateの引数がステートの初期値です。

 (2)ではダイアログを開いてファイルを読み込み、ステート更新を行うためのコールバックを定義しています。

 (2-1)でwindow.ipc.openFile関数を呼び出してPromiseの処理のためにthenメソッドを記述しています。

 (2-2)で戻り値がnullではないことを確認しています。

 (2-3)で戻り値からfilenameを取り出してsetFilenameでファイル名のステートを更新し、contentを取り出してJSON.parseでJSONをデータ化してからsetDataで、データのステートを更新しています。

 これらのステート更新が行われると、再描画が行われて<Productivity data={data}>が更新されます。

JSONファイルを用意して実際に動かす

 では、JOSNファイルを使って実際に動かしてみます。

 まずは2018.jsonというファイルに2018年のデータを入れます。

[
  { "ym": "2018/09", "time": 160, "count": 200 },
  { "ym": "2018/10", "time": 200, "count": 300 },
  { "ym": "2018/11", "time": 120, "count": 150 },
  { "ym": "2018/12", "time": 240, "count": 200 }
]

 次に、2020.jsonというファイルに2020年のデータを入れましょう。

[
  { "ym": "2020/09", "time": 140, "count": 300 },
  { "ym": "2020/10", "time": 180, "count": 400 },
  { "ym": "2020/11", "time": 100, "count": 250 },
  { "ym": "2020/12", "time": 200, "count": 300 }
]

 この状態で実際に動かしてみましょう。electron.jsなどのメインプロセスは書き換えても自動では反映されないため、一度Electronアプリを再起動する必要があります。

Error occurred in handler for 'openFile': No handler registered for 'openFile'

 再起動を忘れると、このようにopenFileのハンドラーが登録されていないエラーが出るはずです。

 再起動して立ち上げ直すと以下の画面になります。

まだJSONを読み込んでない状態
まだJSONを読み込んでない状態

 JSONファイルを選ぶとそれに合わせて更新されます。

2018.jsonを読み込んだ状態
2018.jsonを読み込んだ状態
2020.jsonを読み込んだ状態
2020.jsonを読み込んだ状態

パッケージングする

 開発モードで動かしているだけではわからないこともありますし、配布するためには配布可能な状態にパッケージングする必要があります。

 electron-builderというツールを使えば、とても簡単に配布可能なファイルが生成できます。

package.jsonに設定を追加する

 electron-builderでElectronアプリをパッケージングする際は、package.jsonにいくつもの情報を入れる必要がありますが、作者名や説明文などは省略しようと思えば可能です。どうしても設定しなければいけないものは"homepage": "."だけです。パッケージング時に必要になる情報なため、これがないとelectron-builderの動作が完了してもパッケージングされたアプリが正しく動作しません。

  "homepage": ".",
  "main": "public/electron.js",
  "scripts": {
    "dev": "electron .",
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "pack-app": "npm run build && electron-builder --dir",
    "dist": "npm run build && electron-builder"
  },

 homepageと、scriptspack-appおよびdistを追加しています。

 pack-appおよびdistでは、electron-builderを実行する前に、npm run buildを実行してReactのビルド(バンドル)を行っています。いわゆる普通のWebアプリを作成した上で、Electron用にパッケージングを行うのです。

 このときbuild/ディレクトリには、Reactのビルド時に生成したファイルが出力され、dist/ディレクトリには、electron-builderが生成したファイルが出力されます。もし、パッケージングで困ったときは、build/dist/を消してからやり直すとうまくいくこともあります。

 その後、npm run pack-appを実行すると、dist/<OS>/<アプリ名>でアプリがパッケージングされます。macOSで実行した場合は、dist/mac/wijmo-example1.appになり、Windowsならdist/win-unpacked/wijmo-example1.exeとなります。

npm run pack-app
# Macの場合
# open コマンドで実行可能
open dist/mac/wijmo-example1.app
# Windowsの場合
.\dist\win-unpacked\wijmo-example1.exe

 パッケージングが正しくできているか、動作確認をしておくといいでしょう。以下はWindowsでnpm run pack-appしたときのログです。

> wijmo-example1@0.1.0 build ...\wijmo-example1
> react-scripts build

Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

  222.14 KB  build\static\js\2.4c3d1fdd.chunk.js
  12.18 KB   build\static\css\2.42521bd4.chunk.css
  1.4 KB     build\static\js\3.433c6905.chunk.js
  1.18 KB    build\static\js\nuntime-main.6198875f.js
  834 B      build\static\js\main.ca9ed471.chunk.js
  278 B      build\static\css\main.6dea0f05.chunk.css

The project was built assuming it is hosted at ./.
You can control this with the homepage field in your package.json.

The build folder is ready to be deployed.

Find out more about deployment here:

  https://cra.link/deployment

  • electron-builder  version=22.9.1 os=10.0.19041
  • loaded parent configuration  preset=react-cra
  • description is missed in the package.json  appPackageFile=...\wijmo-example1\package.json
  • author is missed in the package.json  appPackageFile=...\wijmo-example1\package.json
  • writing effective config  file=dist\builder-effective-config.yaml
  • packaging       platform=win32 arch=x64 electron=11.1.0 appOutDir=dist\win-unpacked
  • downloading     url=https://github.com/electron/electron/releases/download/v11.1.0/electron-v11.1.0-win32-x64.zip size=78 MB parts=8
  • downloaded      url=https://github.com/electron/electron/releases/download/v11.1.0/electron-v11.1.0-win32-x64.zip duration=40.874s
  • default Electron icon is used  reason=application icon is not set

 pack-appではなくdistの場合は、配布用のパッケージ(インストーラやZipファイル)が作成されます。

npm run dist

 macOSならdist/wijmo-exampe1-0.1.0-mac.zipdist/wijmo-example1-0.1.0.dmg、Windowsならdist\wijmo-example1 Setup 0.1.0.exeなどが作成されます。

実際に配布する際には

 Electronはクロスプラットフォームなフレームワークであり、electron-builderもがんばればmacOS、Windows、Linuxアプリを1つの環境でパッケージング可能です。しかし、そのためにはいろいろとインストールしなければいけないものや、指定しなければいけないコマンドラインオプションがあります。

 実際に配布するときにはpackage.jsonにauthordescriptionなどの情報、開発者署名、アイコンなどを指定する必要があるでしょう。

 紙面の限りもあり、それらすべてを説明することはできませんが、Electron公式とelectron-builder公式を一通り読めば、必要な情報は得られるはずです。

まとめ

 冒頭でお伝えした通り、年々JavaScript開発は大規模・高度化しているため、静的型が使えるTypeScriptが人気です。Electron+Wijmoの開発においてもTypeScriptを活用すれば、より安全に開発ができるでしょう。

 IPCを使えば、レンダラープロセス(Webブラウザ)ではできないことをメインプロセスに任せることができます。また、electron-builderを使えば、配布可能なパッケージを作成可能です。ぜひチャレンジしてみてください。

関連リンク

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • erukiti(エルキチ)

     TypeScrip+React(とくに React Hooks)をこよなく愛するウェブエンジニア。技術書典に東京ラビットハウスという個人サークルで参加して、JavaScriptなどの技術同人誌を出している。大体いつも締め切りに追い立てられている。

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5