Skip to content

Instantly share code, notes, and snippets.

@nojaja
Last active May 2, 2020 17:44
Show Gist options
  • Select an option

  • Save nojaja/660d7d7dbf5a6711f950481c797dab57 to your computer and use it in GitHub Desktop.

Select an option

Save nojaja/660d7d7dbf5a6711f950481c797dab57 to your computer and use it in GitHub Desktop.
インストーラー作成

はじめに

Electron (旧称: Atom-Shell)は, Atomエディタを開発するために生まれた クロスプラットフォームデスクトップアプリケーションエンジンです。 簡単に言うと、HTML+CSS+Javascript(Node.js)+Chromiumを用いてデスクトップアプリケーションを作成できるという便利なエンジンです。

Electronって?

・クロスプラットフォーム型の実行フレームワーク 👉 Mac、Windows、Linux上で動く

・Webの技術(HTML5やJavaScript)で作ったものをデスクトップアプリケーション化できる

・オープンソース、商用利用可能

・開発元はGitHub社 (もともとはAtomエディタのために作られた)

実用例

・Slack ・Atom ・Kobito ・Docker GUI ・Visual Studio Code (Microsoft)

開発環境

今回の自分の開発環境は次のようになっています.OSはWindowsを使いましたがNode.js(npm)が使えれば何でもとくには関係ないと思います.

  • Windows 10 Pro
  • Node.js v11.13.0
  • npm 6.9.0

Vue CLIのインストール

今回の開発はVue CLIを使って行いました.適当にググってみるとVue CLIの2と3の情報があり使い方も違っているので注意です.今回はVue CLI 3を使っています. npmを使ってグローバルにVue CLI 3をインストールします.

$ npm install -g @vue/cli
$ vue --version
3.4.0

Vue.jsプロジェクトの作成

GUIでVue.jsプロジェクトの作成

下のコマンドを実行すると、ブラウザが立ち上がる。 以下にできる内容をまとめた。

vue ui

プロジェクトの作成ページ

ディレクトリなどの指定が可能。 スクリーンショット 2018-12-01 0.11.06.png

アプリケーション起動

プロジェクトの作成をしたら「npm run serve」によって起動します。

cd hello-project
npm run serve

「npm run」は「packeage.jsonのscripts」に追加されているコマンドが実行されるので、 「serve」に書かれた「vue-cli-service serve」が実行されます。

"scripts": {
       "serve": "vue-cli-service serve",
       "build": "vue-cli-service build",
       "lint": "vue-cli-service lint"
},

起動しました。 スクリーンショット 2018-12-01 11.06.40.png

パッケージインストール

パッケージのインストールをするとpackage.jsonなどに書き込まれる。

GUIからパッケージインストール

プロジェクトを作成したら右上のメニューにインストールボタンがある。 スクリーンショット 2018-12-01 0.39.09.png

パッケージを検索をしてインストール可能。 スクリーンショット 2018-12-01 0.39.16.png

コマンドでパッケージインストール

vue add @vue/eslint

次にパッケージによって選択肢が出るので、選択するとインストールされる。 スクリーンショット 2018-12-01 11.18.39.png

オプションをつけると選択画面なしでインストールできる。

vue add @vue/eslint --config airbnb

GUIからタスクの実行

npm runで実行できるコマンドは「vue ui」のからGUIで操作ができる。

タスクの場所

  1. 作成したプロジェクトを選択する。

スクリーンショット 2018-12-01 11.34.00.png

  1. GUIの左端のタスクを押すと一覧が表示される。

スクリーンショット 2018-12-01 11.27.17.png

serveの例

サーバ起動と各ファイルの読み込み速度などが見れる。 スクリーンショット 2018-12-01 11.28.56.png

スクリーンショット 2018-12-01 11.29.27.png

lintの例

タスクの実行とその結果を見ることができる。 スクリーンショット 2018-12-01 11.27.26.png

2.実際に使うときに必要となる細かな設定

・設定するであろう内容 ・webpackで実装していたプロジェクトをVueCLI3に移行する手順。

vue.config.js

webpack.config.jsなどに書いていた設定はこちらに書く。 プロジェクトに「vue.config.js」ファイルを追加する。 他の設定値は公式リファレンスから参照

module.exports = {
  pages: {
    index: {
      // 最初に実行されるファイル名
      entry: 'src/index.js',
      // テンプレートファイル
      template: 'public/index.html',
      // 出力されるファイル名
      filename: 'index.html',
      // タイトルの設定
      // <title><%= htmlWebpackPlugin.options.title %></title>
      title: 'Index Page',
      // チャンクの指定
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    },
    // サブページをせっているするとエントリーページのファイル以外はこちらが参照される。
    // テンプレートファイルは、 `public/subpage.html`
    // public/subpage.htmlがなかったら `public/index.html`が呼び出される。
    // 出力されるファイル名は `subpage.html`となる。
    subpage: 'src/subpage/main.js'
  },
  // キャッシュバスティングのためにファイル名にハッシュをつけている。
  // デフォルトでtrueなので、falseの場合のみ指定
  filenameHashing: true,
  // ポートなどの設定
  devServer: {
    port: 5000,
    contentBase: path.resolve(__dirname, 'public'),
    host: 'localhost',
  },
  css: {
    // 毎回読み込んでおくscssファイルの指定
    loaderOptions: {
      sass: {
        data: `@import "@/val.scss";`
      }
    }
  },
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
        },
      ]
    },
    resolve: {
      alias: {
        'vue$': 'vue/dist/vue.esm.js',
        '@': path.resolve(__dirname, 'src/'),
      }
    },
    plugins: [
      // plugin
    ]
  }
}

packege.json

packege.jsonの例。 パッケージを最新にしておかないとエラーする。

{
  "name": "なまえ",
  "version": "0.1.0",
  "scripts": {
    "lint": "vue-cli-service lint",
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build --mode develop",
    "build_prod": "vue-cli-service build"
  },
  "dependencies": {
    "@babel/runtime": "^7.2.0",
    "vue": "^2.5.16",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.2.0",
    "@vue/cli-plugin-eslint": "^3.2.0",
    "@vue/cli-service": "^3.2.0",
    "@vue/eslint-config-standard": "^4.0.0",
    "@vue/test-utils": "^1.0.0-beta.24",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "css-loader": "^0.26.2",
    "node-sass": "^4.9.0",
    "sass-loader": "^7.0.1",
    "vue-template-compiler": "^2.5.16",
  }
}

packge.jsonのその他設定

必要なら下の設定も記述。

  // eslintの設定。「.eslintrcファイル」に書いても良い。
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },  
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended",
      "@vue/standard"
    ],  
    "rules": {}, 
    "parserOptions": {
      "parser": "babel-eslint"
    }   
  },
  // postcssの設定。「.postcssrcファイル」に書いても良い。
  "postcss": {
    "plugins": {
      "autoprefixer": {}
    }   
  },
  // browserslistの設定
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]

ビルド

vue.config.jsの設定を変更したらビルドし直す。

npm run build

エラー例

エラーがある場合は、以下のように出る。 「*」が付いている箇所がエラーなので、そこをチェックして直す。

These dependencies were not found:

// 今回は、「vue.config.js」に書かれている、
// 「src/index.js」と「subpage/main.js」が見つからないというエラーだった。
* /Users/tomo/github/vue_cli3/hello-world/src/index.js in multi ./src/index.js
* /Users/tomo/github/vue_cli3/hello-world/src/subpage/main.js in multi ./src/subpage/main.js

Vuetifyの追加

せっかくWebアプリを作るならカッコいいデザインで作りたいです.VuetifyはVue.jsに対応したマテリアルデザインのCSSフレームワークです.UIコンポーネントがいろいろと揃っているのと,ドキュメントもしっかりとしているのでお手軽にカッコいい画面が作れます.

https://vuetifyjs.com/ja/

Vuetifyは既存のアプリに自前でnpm install,importして使っていくことも簡単にできますが,今回はVue CLIから追加して使いました.このときも対話形式となっていますが,今回はDefaultを選択しました.

$ vue add vuetify

インストールの他,Vuetifyのimportや必要なCSSの読み込みも行うようにファイルが更新されています.また先ほど確認したWelcomeページもVuetify仕様に更新されています. これだけでVuetifyを使っていく準備はOKです.

01.png

Vue RouterとVuexを使う

はじめにプロジェクトを作成するときにRouterやVuexを選択していると,すぐに使っていける状態になっています.どちらもちょっとしたWebアプリを作るような段階からでも便利なものになっていると思うので,ぜひVue.jsの入門段階からでも触ってみるといいと思います.

Vue Router

Vue RouterはVue.js公式ルータです.複数のページの切り替えを簡単に実現することができます. すでにrouter.jsがプロジェクト内に存在しているので,その中を見れば使い方はなんとなくわかるかと思います.pathで指定したURLとcomponentで指定したVueコンポーネントが結びつきます.nameは任意ですが名前を付けておくとその名前を利用して遷移先を定義することができます.

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      component: () => import('./views/About.vue')
    }
  ]
})

その他の詳しい使い方はこちらで確認してください. https://router.vuejs.org/ja/

Vuex

VuexはVue.jsアプリケーションの状態を保持するコンテナです.複数のVueコンポーネント間で共通のデータを扱うことができます. 通常は親子関係のあるVueコンポーネント間ではpropemitを使って情報を受け渡すことができますが,直接的な親子関係にないVueコンポーネント間では直接的には情報のやり取りが行えず構造が複雑になってしまいます.Vuexを使うとこれをシンプルに解決することができます.

こちらもすでにstore.jsがプロジェクト内に存在しています.簡単な例としてstatemutationsに実装を追加しています.stateに共有したいデータ,mutationsにそのデータの更新処理を定義します.

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
  }
})

stateの情報を取得するときにはstore.stateから,mutationsの処理を呼びさすときにはstore.commitを使って呼び出すことができます.

<template>
  <p>{{$store.state.count}}</p>
</template>

<script>
export default {
  mounted: function () {
    this.$store.commit('increment')
  }
}
</script>

その他の詳しい使い方はこちらで確認してください. https://vuex.vuejs.org/ja/

Elecronを試してみる

最後におまけ要素ですが,Vue.jsで作ったWebアプリをElectronアプリにして動かしてみます. ElectronはWindows/macOS/Linuxで実行できるデスクトップアプリをHTML+CSS+JavaScriptといったWeb技術で開発できるフレームワークです.有名どころだとAtomやVSCodeがElectronでできています. Vue CLI 3を使うと簡単にElectronで動かすことができたので紹介します.

まずは次のコマンドでelectron-builderをインストールします.バージョンの選択がありましたが今回は4.0.0を選択しました.(自分の環境だと3はエラーが出てインストールできませんでした.)

$ vue add electron-builder

package.jsonを見るとnpm-scriptsにElectron関連のコマンドが追加になっています.electron:serveelectron:buildから起動や実行ファイルのビルドを行うことができます.

$ npm run electron:serve    # Electronで起動
$ npm run electron:build    # 実行ファイルの生成

01.png

electron-builderを使うための設定

まず、package.json内に以下のプロパティを設定している必要がある。

  • name
  • description
  • version
  • author

また、electron-builder自身の設定もpackage.json内に記述する。 dependenciesscriptsと並列にbuildというプロパティを設定する。

"build": {
    "appId": "com.electron.worc",
    "mac": {
        "target": "dmg"
    },
    "directories": {
        "output": "docs"
    }
}

appId

アプリの識別子。
MacではCFBundleIdentifier、WindowsではApplication User Model IDとして用いられる。 デフォルトはcom.electron.${name}という形式。

Apple Developer Programに登録していないと警告が出るが、そのままパッケージングは進められる。

ターゲット

起動パラメータで選択、ただし他のOSで作成は環境面で難しいのでDockerを利用するか、GIT ACTIONSを利用するのがよい

electron-builder --mac --x64 --publish never
electron-builder --win --x64 --publish never
electron-builder --linux --x64 --publish never

Menuを追加する

ここまで作ったReadUsをよりデスクトップアプリケーションらしくするために, メニューを追加してみます.

ユーザがREADMEの探索元となるディレクトリを指定できるようにしてみます. 既にfileUtilには, 探索起点ディレクトリであるbaseDirを引数として指定できるようにしてあるため, あとはUI部分を作るだけです.

Electronには, アプリケーションメニューとコンテキストメニューの2種類があります. アプリケーションメニューはメニューバーに表示されるメニュー, コンテキストメニューは画面を右クリックした際に表示されるメニューです.

今回はアプリケーションメニューを追加します. アプリケーションメニューはBrowserProcess側で追加を行うため, background.jsを下記のようにします.

'use strict';

const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
const {crashReporter, dialog} = require('electron');

crashReporter.start({
  productName: 'YourName',
  companyName: 'YourCompany',
  submitURL: 'https://your-domain.com/url-to-submit',
  autoSubmit: true
});

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

app.on('ready', function() {
  // メニューをアプリケーションに追加
  Menu.setApplicationMenu(menu);

  openWindow(process.cwd());
});

function openWindow (baseDir) {
  let win = new BrowserWindow({width: 800, height: 600});
  win.loadURL('file://' + __dirname + '/index.html?baseDir=' + encodeURIComponent(baseDir));
  win.on('closed', function () {
    win = null;
  });
}

// メニュー情報の作成
const template = [
  {
    label: 'ReadUs',
    submenu: [
      {label: 'Quit',
        accelerator: 'Command+Q',
        click: function () {
          app.quit();
        }}
    ]
  }, {
    label: 'File',
    submenu: [
      {
        label: 'Open',
        accelerator: 'Command+O',
        click: function () {
          // 「ファイルを開く」ダイアログの呼び出し
          dialog.showOpenDialog({properties: ['openDirectory']}, function (baseDir) {
            if (baseDir && baseDir[0]) {
              openWindow(baseDir[0]);
            }
          })
        }
      }
    ]
  }, {
    label: 'View',
    submenu: [
      {label: 'Reload',
        accelerator: 'Command+R',
        click: function () {
          BrowserWindow.getFocusedWindow().reload();
        }},
      {
        label: 'Toggle DevTools',
        accelerator: 'Alt+Command+I',
        click: function () {
          BrowserWindow.getFocusedWindow().toggleDevTools();
        }
      }
    ]
  }
];

const menu = Menu.buildFromTemplate(template);

MenuモジュールのbuildFromTemplate関数にtemplateを与え, アプリケーションの起動時(app.on('ready', ...))にて, setApplicationMenuを実行することでアプリケーションメニューを追加しています. メニューの元となっているtemplateについて, acceleratorでショートカットキーの指定, clickにクリックされた際に動作するハンドラ関数を設定可能です.

ユーザにディレクトリを開かせるOpenメニューを含め, 追加したメニューは以下の4つです.

  • ReadUs
  • Quit: アプリケーションを終了させる.
  • File
  • Open: 探索の起点となるディレクトリを選択させ, 新規にRendererProcessを立ち上げる.
  • View
  • Reload: RendererProcessを再描画させる.
  • Toggle Devtools: Chromiumの開発者ツールの表示を切り替える.

Openメニューでは, Electronのdialogモジュールを使っています. dialogモジュールのshowOpenDialog関数は「ファイルを開く」ダイアログを表示することができます. 今回はディレクトリを開かせたいので, openDirectoryプロパティをセットしました.

dialogモジュールにはshowOpenDialog以外にも数種類のダイアログを表示するための機能が備わっています(詳細)

ViewメニューのReload, Toggle Devtoolsの2つは, エンドユーザにとっては無くても構わないのですが, 開発時に便利な機能です. 両方ともBrowserWindow自体に備わっている機能を利用しています.

さて, Openメニューで選択したディレクトリ情報は, RendererProcess側にクエリストリングとして渡しています. 先述のindex.jsにてvar baseDir = process.cwd() としていた箇所を下記で置き換えれば, ディレクトリパスの受け渡し処理が完成します.

var matched = location.search.match(/baseDir=([^&]*)/);
var baseDir = matched && decodeURIComponent(matched[1]);

capt_menu.png

参考文献

VueCLI3導入とできることまとめ https://qiita.com/bokotomo/items/8dc619cc271f4c69658a

Vue.jsでちょっとSPAなWebアプリ作ってみたときのメモ https://qiita.com/y-tsutsu/items/e8544ec5fe1b9a69ea35

electron-builderを使ってdmgファイルを生成する http://shibe97.hatenablog.com/entry/2017/02/17/090000

Electronでアプリケーションを作ってみよう https://qiita.com/Quramy/items/a4be32769366cfe55778

Electronの環境構築(for Windows) https://qiita.com/TigRig/items/64d55b5fc5483b01c3b5

GistEditor作成メモ

Vue.jsプロジェクトの作成

GUIでVue.jsプロジェクトの作成

下のコマンドを実行すると、ブラウザが立ち上がる。

vue ui

新しいプロジェクトを作成する

  1. プロジェクトフォルダ:GistEditor
  2. パッケージマネージャ:デフォルト
  3. プリセットを選択する:デフォルトプリセット
  4. プロジェクトを作成する押下
  5. プラグインの項目でvue-router追加
  6. プラグインの項目でvuex追加
  7. vuetify導入、導入プラグインの項目でvue-cli-plugin-vuetify追加
  8. electron導入、導入プラグインの項目でvue-cli-plugin-electron-builder追加 v5.0.0

GUIからタスクの実行

serveを実行

サーバ起動と各ファイルの読み込み速度などが見れる。 http://localhost:8080/#/ にアクセス

Electronでプロセス間通信をする

https://electronjs.org/docs/api/ipc-main

参考文献

Vue.js + Electron でマークダウンエディタをサクッとつくってみた http://kokushin.hatenablog.com/entry/2016/04/26/Vue_js_%2B_Electron_%E3%81%A7%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%E3%82%92%E3%82%B5%E3%82%AF%E3%83%83%E3%81%A8%E3%81%A4%E3%81%8F%E3%81%A3%E3%81%A6

# macでもwindowsでも動くインストーラを作る
Mac環境のElectronでWindowsインストーラーとアップデートに対応させる
https://spyweb.media/2017/10/16/mac-electron-build-windows-installer-and-auto-updater/
Electronの環境構築(for Windows)
https://qiita.com/TigRig/items/64d55b5fc5483b01c3b5
Electronでアプリケーションを作ってみよう
https://qiita.com/Quramy/items/a4be32769366cfe55778
Windowsインストーラ風で
インストーラ本体は自動更新
内部処理はgulp利用
コンソール表示
vue使う
起動時にnodeとエージェント入れる
#
NSIS
https://nsis.sourceforge.io/Main_Page

terminal作成メモ

Vue.jsプロジェクトの作成

GUIでVue.jsプロジェクトの作成

下のコマンドを実行すると、ブラウザが立ち上がる。

vue ui

新しいプロジェクトを作成する

  1. プロジェクトフォルダ:terminal
  2. パッケージマネージャ:デフォルト
  3. プリセットを選択する:デフォルトプリセット
  4. プロジェクトを作成する押下
  5. electron導入、導入プラグインの項目でvue-cli-plugin-electron-builder追加 v5.0.0

プロジェクト依存

  • stylus
  • stylus-loader
  • xterm
  • xterm-addon-fit
  • xterm-addon-web-links
  • execa
  • iconv-lite
  • fix-path

GUIからタスクの実行

electron:serveを実行

electron起動

Electronでプロセス間通信をする

https://electronjs.org/docs/api/ipc-main

//background.js S -> C win.webContents.send('terminal', buffer.toString()); //background.js C -> S async ipcMain.on('asynchronous-message', (event, arg) => { console.log(arg) // prints "ping" event.reply('asynchronous-reply', 'pong') }) //background.js C -> S sync ipcMain.on('synchronous-message', (event, arg) => { console.log(arg) event.returnValue = 'pong' }) //render.js C -> S -> C sync console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"

//render.js C -> S async ipcRenderer.send('asynchronous-message', 'ping')

//render.js S -> C async ipcRenderer.on('terminal', (event, arg) => { console.log(arg); // prints "pong" })

exec

  win.webContents.on('did-finish-load', () => {
    let command = "dir";
    let args = [];

    const child = execa(command, args, {
          cwd: __dirname,
          stdio: ['inherit', 'pipe', 'pipe'],
          shell: true
        })

    child.stdout.on('data', buffer => {
      console.log(buffer.toString());
      win.webContents.send('terminal', buffer.toString());
    })

    child.stderr.on('data', buffer => {
      console.log(buffer.toString());
      win.webContents.send('terminal', buffer.toString());
    })
    child.on('exit', (code, signal) => {
      console.log('Task exit', command, args, 'code:', code, 'signal:', signal)
     })

    child.on('error', error => {
     console.error(error)
     win.webContents.send('terminal', error.message.toString());
    });

  });

参考文献

Vue.js + Electron でマークダウンエディタをサクッとつくってみた http://kokushin.hatenablog.com/entry/2016/04/26/Vue_js_%2B_Electron_%E3%81%A7%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%E3%82%92%E3%82%B5%E3%82%AF%E3%83%83%E3%81%A8%E3%81%A4%E3%81%8F%E3%81%A3%E3%81%A6

ElectronでBrowserWindowと通信する(ipc編) https://techium.hatenablog.com/entry/2016/03/21/083000

pty.js + xterm.js = shell in your browser https://dev.to/davidk01/ptyjs--xtermjs--shell-in-your-browser-1f9c

JavaScript/Node.jsでエンコーディング変換を扱うことができる、iconv-liteを試す https://kazuhira-r.hatenablog.com/entry/20180212/1518428304

vue-term https://github.com/gwangyi/vue-term/blob/master/src/VueTerm.vue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment