ElectronとVueでメモ帳アプリを作ろう【n○te風】

ElectronとVueでメモ帳アプリを作ろう【n○te風】

ElectronとVueを使って、n○te風のシンプルなメモ帳デスクトップアプリを作成するチュートリアル。HTMLやCSS、JavaScriptだけでデスクトップアプリが作れるElectronの入門記事です。

💭 Electron使ってみたい。デスクトップアプリを作ってみたい。

そんな方向けの記事になっています。

✔︎記事の信頼性
この記事を書いている僕は文系大学生でしたが、プログラミングを学習して、Web系エンジニアとして東京の自社開発会社さんで内定をいただきました。

■少し宣伝■
もし文系から新卒でエンジニア就活しよう!って方は、noteが参考になると思うので、そちらもご覧ください。
>>文系大学生でも新卒エンジニア内定を勝ち取る作戦【面接テンプレ付き】
バッチャンnoteの記事内容

というわけで、今回はElectronがどんなものかを把握しつつ、自作で『簡単なn○te風のメモアプリ』を作ってみましょう。

▼完成はこんな感じ

Electronメモアプリ完成画像

とても簡単で1時間もあればできてしまうと思います。それでは、やってみましょう。

💻 Electronとは

ザックリ言うと、HTMLやCSS、JavaScriptなどのWeb言語でデスクトップアプリが作れてしまう優れもの。

今ではあまり有名ではありませんが、プログラミングエディターの定番『VScode』や『Atom』もElectronで作られているようです。

パッケージ化すれば、Mac、Windows、Linuxどれにも対応していますし、配布もできるのは面白そうですよね。

また、今回『CONSOLE DOT LOG』の記事を参考に改良させていただいております。

この方の記事では、Electronのバージョンの違いで、上手く作動しないようだったのでJavaScriptでカバーしました。

💬 Webの言語だけでできるのありがたい。

🔧 Electronでメモアプリを作る手順

まず、今回使う言語の紹介です。できる限り流れも解説しているので、初見でもできると思います。

  • Electron
  • HTML
  • CSS
  • JavaScript
  • Bootstrap
  • Vue

npmコマンドを使うので、node.jsがダウンロードできているかをターミナルなどで確認しましょう。

$ node -v
v10.15.3
$ npm -v
6.4.

こんな感じで出てくれば準備OKです。

📦 インストールや設定

今回はお試しということなので、テンプレを公式が作ってくれているので、それをgitでクローンしましょう。

$ cd 作業ディレクトリ

$ git clone https://github.com/electron/electron-quick-start

クローン(ダウンロード)できたら、electron-quick-startファイルができているか確認したら、自由に変更してください。

(僕は『n0te』という名前にします。)

名前をつけたフォルダに移動して、npmで一式ダウンロードします。

$ cd n0te

$ npm install

これでElectron自体は動くので、一度画面を見てみましょう!

$ npm start

デスクトップにアプリが開いて『Hello World』が出ていれば、成功ですよ〜。

ElectronでHelloWorldの画像

💬 これであなたもElectron初心者の仲間入り!

⚙️ Electron設定

Electronの設定を『main.js』で様々な変更をできます。

例えば、ウィンドウのサイズや位置、アプリアイコン設定などのようにできるので、気になる方は調べてみてください。

今回は最低限、ここだけは!という部分を書いておきます。

main.jsのnodeIntegration: trueにしましょう。

nodeintegrationをtrueにする画像

これはElectronのバージョン5から変わった仕様なので、僕はつまづきました。

これをtrueにすることで、nodeがElectronに入ってくることを許す記述です。

ただし、nodeでファイル操作できてしまうので、パッケージしたアプリをネットなどに公開する場合は気をつけた方がいいようです。

同様に、『index.html』のContent-Security-Policyをコメントにしましょう

<meta charset="UTF-8">

<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->

<!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> //消すかコメントにする

<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">

こちらもクロスサイトスクリプティングを防ぐためのものなので、ネットに公開しない限り大丈夫なので、外しましょう。

残っていると保存処理ができないので、注意してください。

ちなみに、使い慣れたWebページのディベロッパーツールが使いたい方は

『main.js』のmainWindow.webContents.openDevTools() のコメントを外すと、初期にディベロッパーツールを開くことができます。

// Open the DevTools.
mainWindow.webContents.openDevTools()//コメント外す
$ npm start

Electronのディベロッパーツールの画像

💬 わざとエラーを出しているので、気にしないでください。笑

📚 そのほかの設定

これでElectronの設定は終わったので、それ以外の設定(VueやBootstrap)していきましょう。

まずは、VueとBootstrapをインストールします。

$ npm install vue --save

$ npm install bootstrap --save

『index.html』にBootstrap、CSS、Vueを読み込みます。

<title>Hello World!</title>
    <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css"> //BootstrapのCSSを読み込み
    <link rel="stylesheet" href="style.css"> //自作CSSを読み込み
  </head>
  <body>
    <script src="./renderer.js"></script>
    <script src="node_modules/vue/dist/vue.min.js"></script> //Vueの読み込み
  </body>

読み込み順番の差で「Bootstrapが効かない」とかあるので、気をつけてください。

Bootstrapにはjsは使用しません。というのも、今回はVueと素のjsを使うからです。

🎨 デザインしていく

デザインですが、僕のコードHTMLとCSSは最後に貼っておくので、コピペしてもらっても構いません。

<nav class="navbar navbar-light bg-light">
        <a class="navbar-brand">n0te</a>
        <button class="btn float-right open-btn mr-sm-2">保存する</button>
      </nav>
        <div class="container">
          <div class="margin-area mt-2">
            <div class="resultmessage"></div>
        </div>
          <div class="form-group mt-3">
            <textarea class="form-control memo" id="blog" rows="20"></textarea>
          </div>
        </div>

直下にstyle.cssを新規ファイルで作成して、デザインを作りましょう。

@charset "UTF-8";

body {
    margin: 0;
    padding: 0;
    color: #222222;
    font-size: 14px;
    font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,Segoe UI,Hiragino Kaku Gothic ProN,Hiragino Sans,ヒラギノ角ゴ ProN W3,Arial,メイリオ,Meiryo,sans-serif;
}

.memo {
    height:90%;
    display:block;
    padding:10px;
    line-height: 36px;
    font-size: 18px;
}

.margin-area {
    bottom:0;
    width:100%;
    padding:10px;
}

.open-btn {
    background-color: #2cb696;
    color: #fff;
}

.resultmessage {
    opacity:0.7;
}

ちなみに、フォントやカラーは本家と同じです。笑

デザインはできました。見てみましょう。

$ npm start

Electronでコーディング中の画像

💬 色とかフォント変えるだけでここまで本家と似るとは…

⚡ 動きをつけていく

あとはVueで『保存』を押したら、テキストファイルができるよな処理ができたら、終わりです。

あと一息!頑張ろう!ここもコピペしてもOKですが、一応解説します。

Vueは全体を

で囲まないと反応しないので、入れます。

<body>
    <div id="app">
      <nav class="navbar navbar-light bg-light">
        <a class="navbar-brand">n0te</a>
                  <中略>
    </div>

同様にVueの型をscriptタグで作ります。

<script src="node_modules/vue/dist/vue.min.js"></script>
    <script>
      new Vue({
        el:"#app"
      })
    </script>

ここからは処理が複雑?になるので、流れをまず解説しちゃいます。

全体の流れとしては

ステップ1

テキストエリアに文字を入力する▶︎inputTextにデータが入る

ステップ2

保存ボタンを押す▶︎saveText()のイベントが動く▶︎resultMessageに"保存完了"の文字が表示される

ステップ3

保存処理▶︎JSでクリックされた瞬間に、そのURLをダウンロードすることで、保存。

という感じです。

⓵: テキストエリアに文字を入力する▶︎inputTextにデータが入る

v-model="inputText"をテキストエリアに入れて、空っぽのデータを作っておく。

<textarea  v-model="inputText" class="form-control memo" id="blog" rows="20"></textarea>
new Vue({
        el:"#app",
        data: {
          inputText: '',
        }

これで、テキストエリアに文字を入力するとdataの inputTextにその文字が入るようになりました。

⓶:保存ボタンを押す▶︎saveText()のイベントが動く▶︎resultMessageに"保存"が表示される

<button @click="saveText()" class="btn float-right open-btn mr-sm-2">保存する</button>

保存ボタンが押されたら、イベントが動くように、ボタンに『@click="saveText()"』をつけます。

data: {
          inputText: '',
          resultMessage: ''
        },
        methods: {
          saveText() {
            },
         }

dataの下(『,』コンマを忘れずに)にメソッドをまとめる部分を作り、saveText()のイベントの処理を作っておきます。(中の処理は⓷で解説)

また、『保存完了』が出るように resultMessageをデータが入る箱を作っておきます。

<div class="margin-area mt-2">
            <div v-text="resultMessage" class="resultmessage"></div>
        </div>

同様に、resultMessageに『保存完了』の文字が表示されるようの処理を書いておく。

⓷:保存処理▶︎JSでクリックされた瞬間に<a href="">を作り出して、そのURLをダウンロードすることで、保存。

saveText() {
              if (this.inputText) {
                  const blob = new Blob([this.inputText], {type: 'text/plain'});
                  let link = document.createElement('a');
                  link.href = URL.createObjectURL(blob);
                  link.download = [this.inputText]+'.txt';
                  link.click();
                  this.resultMessage = '保存完了'
              } else {
                this.resultMessage = '予期せぬエラーにより保存に失敗しました。'
              }
          },

inputTextの中身があれば、保存。なければ、エラーという仕組みです。

ザックリ言ってしまえば、「高速でリンクを作って、保存する処理」です。

もっと詳しく知りたい方ははこちらの記事が良いと思います。

📚 JavaScriptで文字列をファイル出力する方法

本家のサイトではこの処理をElectronの処理で行っていましたが、バージョンが変わることで、できなくなったようなので、JavaScriptで代用しました。

ツイートを読み込んでいます...

💬 ついに完成!!イェイ!!

📦 パッケージ化

できたアプリをデスクトップ用アプリとして使いたいですよね。

もう本家のnoteを使わなくても、オフラインでn0te心地よくかけます。笑

パッケージ化すると他のデスクトップアプリ同様に使うことができます。

それでは、ラスト!パッケージ化してみましょう。

まず、ディベロッパーツールを使っていた方は消しておいた方がいいですよ。ジャマになってしまいます。

// Open the DevTools.
//  mainWindow.webContents.openDevTools() ←コメントに戻す

確認を追えたら、『electron-packager』をインストールします。

$ npm install -d electron-packager

実行する前に、パッケージしたいフォルダの『一個上の階層』に移動します。

$ cd 『n0te』の一個上の階層

用意ができたら、パッケージ化します。

$ npx electron-packager ./n0te n0te --platform=darwin --arch=x64 --overwrite

▼--platform=〇〇←ここはパッケージ化したいのプラットフォーム
Windows(win32)Mac(darwin)Linux(linux)全てに対応(all)

ファイル指定をできたり、どのフォルダに作るか。上書きするか。など指定できます。

できなければ「electron-packager パッケージ化」と調べれば、環境に合わせた方法が出てくるでしょう。

実行してみて、できていれば完了。

Electronのパッケージした画像

Electronのパッケージしたアプリの画像

完成!!『.app』を開くと動きます(^○^)

💬 おつかれさまでした!!

🎯 まとめ

今回はElectronを使って、note風のメモ帳を作ってみました!

Electronはデスクトップアプリを簡単に作れる楽しい技術なので、これをきっかけに使ってみてください!

それではよき、Electronライフを!(^○^)

最後に今回のコードを貼っておきます。参考にどうぞ。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> -->
    <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>n0te</title>
    <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div id="app">
      <nav class="navbar navbar-light bg-light">
        <a class="navbar-brand">n0te</a>
        <button @click="saveText()" class="btn float-right open-btn mr-sm-2">保存する</button>
      </nav>
        <div class="container">
          <div class="margin-area mt-2">
            <div v-text="resultMessage" class="resultmessage"></div>
        </div>
          <div class="form-group mt-3">
            <textarea  v-model="inputText" class="form-control memo" id="blog" rows="20"></textarea>
          </div>
        </div>
    </div>
    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
    <script src="node_modules/vue/dist/vue.min.js"></script>
    <script>
      new Vue({
        el:"#app",
        data: {
          inputText: '',
          resultMessage: ''
        },
        methods: {
          saveText() {
              if (this.inputText) {
                  const blob = new Blob([this.inputText], {type: 'text/plain'});
                  let link = document.createElement('a');
                  link.href = URL.createObjectURL(blob);
                  link.download = [this.inputText]+'.txt';
                  link.click();
                  this.resultMessage = '保存完了'
              } else {
                this.resultMessage = '予期せぬエラーにより保存に失敗しました。'
              }
          },
        }
      })
    </script>
  </body>
</html>
@charset "UTF-8";

body {
    margin: 0;
    padding: 0;
    color: #222222;
    font-size: 14px;
    font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,Segoe UI,Hiragino Kaku Gothic ProN,Hiragino Sans,ヒラギノ角ゴ ProN W3,Arial,メイリオ,Meiryo,sans-serif;
}

.memo {
    height:90%;
    display:block;
    padding:10px;
    line-height: 36px;
    font-size: 18px;
}

.margin-area {
    bottom:0;
    width:100%;
    padding:10px;
}

.open-btn {
    background-color: #2cb696;
    color: #fff;
}

.resultmessage {
    opacity:0.7;
}
// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
const path = require('path')

function createWindow () {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true,
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow()
  
  app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

参考にさせていただいたサイトを貼っておきます。活用ください。

📚 参考

最後まで読んでいただきありがとうございました!てばさん(@basabasa8770)でした!

この記事をシェア