お仕事で Knockout.js + Electron を触る可能性があるので
まずはチュートリアルってことで TODOアプリ を作成してみました
Knockout.jsとは
Knockout.jsは以下の特徴を持ったMVVMライブラリです
- エレガントな依存トラッキング
- 宣言型 バインディング
- 拡張が容易
他のMVVMに比べて非常にシンプルでわかりやすい、というのが第一印象です
インストール
公式サイトでは <script>
タグでの読み込みが紹介されていますが
今回は Electron で動かすので npm モジュールとしてインストールします
$ npm install --save knockout
ついでに electron
webpack
webpack-dev-server` をインストールします
$ npm install --save electron webpack webpack-dev-server
webpackの設定
webpackの設定を以下のようにします
const path = require('path');
module.exports = {
context: path.resolve(__dirname, './src'),
entry: './index.js',
output: {
filename: 'bundle.js',
// webとelectron両方で動かすため path と publicPath を同じ値にしておく
path: path.resolve(__dirname, 'dist'),
publicPath: 'dist'
},
devServer: {
contentBase: path.resolve(__dirname, './src'),
port: 3000,
},
// electron用の設定??
target: 'electron-renderer'
};⏎
今回はwebとelectron両方で動かしたかったので
pathとpublicPathの値を同じパスにしました。
target
の値を electron-renderer
にすると
electron用にビルドしてくれる?みたいですが正直よくわかりませんでした。。。
またpackage.jsonにもコマンドを追加しておきます
"scripts": {
"webpack": "webpack-dev-server --hot --inline --target web",
"electron": "webpack && electron ."
},
UI
TODOアプリの見た目はこちらのサイトからお借りしました
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<link rel="stylesheet" href="style.css">
<script src="../dist/bundle.js"></script>
</head>
<body>
<div class="header">
<h2>My To Do List</h2>
<input type="text" placeholder="Title..." data-bind="value: taskSummary">
<span class="addBtn" data-bind="click: addTask">Add</span>
</div>
<ul data-bind="foreach: todoList">
<li data-bind="text: summary, css: {'checked': isDone}, click: $root.toggleDone"></li>
</ul>
</body>
</html>
data-bind
属性の部分が knockout.jsのデータバインドを宣言している部分となります
シンプルでわかりやすいですね
ロジック
const ko = require('knockout')
function Viewmodel() {
let self = this
self.todoList = ko.observableArray([
{summary: "hogehoge", isDone: true},
{summary: "hogehoge", isDone: true},
{summary: "hogehoge3", isDone: true}
])
self.toggleDone = (task) => {
let updateTask = {
summary: task.summary,
isDone: !task.isDone
}
self.todoList.replace(task, updateTask)
}
self.taskSummary = ko.observable('')
self.addTask = _ => {
let newTask = {
summary: self.taskSummary(),
isDone: false
}
self.todoList.push(newTask)
self.taskSummary('')
}
}
window.onload = _ => {
ko.applyBindings(new Viewmodel())
}
Viewmodelを作成し、そちらを ko.applyBindings
に渡しています。
ES2015で書くんだったら class で作成したほうがよかったかもしれないです
ko.applyBindings 関数は DOMが準備された後に呼び出さないとエラーになるため
windows.onloadの中で読んでいます
Viewmodelではプロパティの値を ovservable
observableArray
でラッピングしています
こうすることでプロパティの更新をknocokout.jsが検知し、UIを変更します
observavleArrayの特定の要素のみ更新する方法でちょっとハマりましたが
observableArray#replace 関数を使うことで特定の要素のみ更新できるようです
electron
Electronで動かすための main.js を書きます
// electronモジュールを読み込み
const electron = require('electron');
const {app} = electron;
const {BrowserWindow} = electron; //ウィンドウを表す[BrowserWindow]はelectronモジュールに含まれている
// 新しいウィンドウ(Webページ)を生成
let win;
function createWindow() {
// BrowserWindowインスタンスを生成
win = new BrowserWindow({width: 800, height: 600});
// index.htmlを表示
win.loadURL(`file://${__dirname}/src/index.html`);
// デバッグするためのDevToolsを表示
win.webContents.openDevTools();
// ウィンドウを閉じたら参照を破棄
win.on('closed', () => { // ()は function ()と書いていい
win = null;
});
}
// アプリの準備が整ったらウィンドウを表示
app.on('ready', createWindow);
// 全てのウィンドウを閉じたらアプリを終了
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (win === null) {
createWindow();
}
});
ネットに落ちていたサンプルコードをそのままコピペしただけになります。。。
作成したもの
デモ: https://tamanugi.github.io/knockout_electron/src/index.html
リポジトリ: https://github.com/tamanugi/knockout_electron
# 感想とか
とてもシンプルでとっつきやすいライブラリだと感じました。
他のMVVMとくらべても覚えることが少なく、非常にわかりやすいです
どちらかというとWebpackとelctronについて勉強しないとだめですね。。。(webpackムズカシイ