JWF(JavaScript Window Framework)で開発する、SPA(SinglePageApplication)開発
0. 動作画面
1. そもそもSPAって何?
某雑誌の名前ではありません。Web開発においてHTMLデータをリロードせず、ページ内のDOMを操作しUIを構築する作り方です。
- SPAの利点
- ページの更新待ちが発生しない
- 応答が早い
- データのやりとりが最小限で済む
- SPAの欠点
- 開発方法に慣れるまでキツい
- 覚えることが沢山ある
その他の欠点でJavaScriptを読み込むので、初期表示までに時間がかかると言われていますが、それは組み方が悪いか、使っているフレームワークがお大便なだけなのです。HTTP2のServerPushを使えば、初期HTMLファイルと同時に.jsを送りつけることも可能です。さらにはページに表示する構造が複雑になってくると、HTMLで組まれたテキストの字句解析の時間より、JavaScriptの構文解析とプログラムによるDOM生成の方が速度が早くなります。
2.SPAの基本、ジークジオン
最も重要なのはJavaScriptによるDOM操作です。ついにJavaScriptでジオンのモビルスーツの操作ができるようになったとか思わないでください。もしかしたらJavaScriptで動いている可能性はゼロではありませんが、その話ではありません。サブタイトルが間違っています。今回はDocument Object Modelの事です。HTMLの中の構造をオブジェクトとして操作していきます。
下手なフレームワークに触れる前に、このDOM操作には慣れてください。手習いとしては、「document.querySelectorで拾ってきたHTMLElementに適当な操作をする」と言うのを繰り返すのをオススメします。jQueryとかは基礎の理解を阻害するので使わない方が良いです。到達ラインとしては、JavaScriptのコードで、表示したいものを表示したい位置にきっちり吐き出せるレベルが理想です。
3.「下積みなんてやってられないよ!」というためのフレームワーク
SPAを扱うためのフロントエンドフレームワークは有名どころがいくつかあります。それらに私は馴染めませんでした。HTMLやそれに近いもの記述した後に、そこに割り込んでいくスタイルが好きになれないのです。
理想の設計思想としては、ほとんどの部分をJavaScriptのコードで完結させることです。初期ページ以外でHTMLを一切書かない、そしてそれを不便な状況としない、それが理想的な状態です。フレームワーク自体も、基本的にJavaScriptでDOM操作を行っているので、HTMLをテキストで記述している部分は皆無です。
4.JavaScript-Window-Frameworkの使い方。
ということで、JavaScript-Window-Frameworkの紹介をしていこうと思います。
JavaScript-Window-Frameworkは、SPAでフロントエンド開発をするためのフレームワークです。一つのコンポーネントをウインドウという単位で管理します。ウインドウは特定の場所に貼り付けたり、フレームを作って可動式の仮想ウインドウとすることも出来ます。ブラウザのクライアント領域全体に展開することも可能です。
以下、開発環境が整いやすいようにnpm版を紹介します。
4.1 開発環境の構築とサンプルテンプレート
Node.jsが入っていてnpmが使える状態が前提です。使い方を確認するための環境を構築するコマンドが入っています。
- パッケージファイルの作成
npm -y init
- JavaScript-Window-Frameworkのインストール
npm -D i javascript-window-framework
- サンプルテンプレートの作成(WebPackの設定ファイルなどを自分で作る場合は不要)
npx init-jwf
- WebPackがらみで使いそうなモジュールを一通りインストール(自分で必要なものを設定するなら不要)
npm -D i typescript dts-bundle ts-loader node-sass style-loader sass-loader css-loader url-loader source-map-loader webpack webpack-cli webpack-dev-server
- サンプルのビルド
npx webpack
dist/index.html をブラウザで開く
- webpack-dev-serverを利用する場合
npx webpack-dev-server
http://localhost:8080/ をブラウザで開く
4.2 サンプルの紹介
動作が分かりやすいように全てフレームウインドウ化してありますが、ブラウザのクライアント領域全体に展開することも出来ます。あとはあまりオススメしませんが、HTMLを普通に記述して、特定のノードの中に貼り付けることも出来ます。
- [1] ウインドウを表示するだけのサンプル
フレームウインドウはデフォルトでサイズ変更とドラッグによる移動機能を持っています
モバイル系のブラウザでも操作可能です
function Sample001() {
const win = new JWF.FrameWindow() //フレームウインドウの作成
win.setTitle('Sample01 ウインドウを表示') //タイトルの設定
win.setPos() //位置を中心に設定
}
- [2] ウインドウの位置とサイズとイベントの扱い
サイズ変更やウインドウで発生したイベントのトラップが可能です
function Sample002() {
const win = new JWF.FrameWindow() //フレームウインドウの作成
win.setTitle('Sample02 位置サイズ指定') //タイトルの設定
win.setSize(100, 100) //サイズの変更
win.setPos(10, 10) //位置指定
const client = win.getClient() //クライアントノードの取得
win.addEventListener('active', (e) => {
client.innerText = e.active ? 'アクティブになった' : '非アクティブになった'
})
}
- [3] リストビューの使い方
WindowsのListViewに似せて作っています
ヘッダクリックによるソート、ヘッダ枠のリサイズ、縦横スクロール時のヘッダの固定など
実は有名どころのフロントエンドフレームワークでこれを実装しているものはありません
実際にこれをDOM操作で作ると、とんでもなく面倒くさいんです
function Sample003() {
const listView = new JWF.ListView({ frame: true }) //リストビューをフレーム付きで作成
listView.setTitle('Sample03 リストビュー') //タイトルの設定
listView.addHeader(['番号', '名前', '攻撃力']) //ヘッダの作成
listView.addItem([1, '竹槍', 5]) //アイテムの設定
listView.addItem([2, '棍棒', 10])
listView.addItem([3, '銅の剣', 10])
listView.setPos(30, 30) //位置指定
listView.addEventListener('itemClick', (e) => { //アイテムクリック処理
const name = listView.getItemText(e.itemIndex, 1) //リストビューからデータを取り出す
new JWF.MessageBox('メッセージ', name + "が選択された") //メッセージボックスの表示
})
}
- [4] ツリービューの使い方
ツリービューは比較的簡単に実装可能なので、比較的よく見かけます
function Sample004() {
//ツリービューの作成
let treeView = new JWF.TreeView({ frame: true })
treeView.setTitle('Sample04 ツリービュー') //タイトルの設定
//サイズ設定
treeView.setSize(300, 300)
//ルートアイテムに対して名前の設定
treeView.getRootItem().setItemText('ルートアイテム')
//アイテムを作成
let item:JWF.TreeItem
item = treeView.addItem('アイテム1')
item.addItem('アイテム1-1')
item.addItem('アイテム1-2')
item = treeView.addItem('アイテム2')
item.addItem('アイテム2-1')
item.addItem('アイテム2-2')
item.addItem
//アイテムが選択された場合のイベント
treeView.addEventListener('itemSelect', function (e) {
const name = e.item.getItemText() //リストビューからデータを取り出す
new JWF.MessageBox('メッセージ', name + "が選択された") //メッセージボックスの表示
})
}
- [5] 可動式分割バーのサンプル
Webの過渡期にframesetというのがありましたが、動きとしては同じような動作をします
幅か足りなくなると、自動的に引っ込みます
function Sample005() {
const frame = new JWF.FrameWindow()
frame.setTitle('Sample005 分割ウインドウ') //タイトル設定
const splitter = new JWF.Splitter() //分割バーの作成
frame.addChild(splitter, 'client') //分割バーをフレームウインドウに乗せる
const tree = new JWF.TreeView() //ツリービューの作成
const list = new JWF.ListView() //リストビューの作成
splitter.addChild(0, tree, 'client') //splitterの分割領域0にtreeを追加
splitter.addChild(1, list, 'client') //splitterの分割領域1にlistを追加
//分割バーの分割サイズと方向設定(WestEast、左右)
//weは左が領域0、右が領域1
//nsにすると上下分割も可能
splitter.setSplitterPos(200, 'we')
//表示領域が300を切ると、動的なオーバーレイ表示にする
splitter.setOverlay(true, 300)
//treeにアイテムを追加
tree.getRootItem().setItemText('最上位アイテム')
for (let j = 0; j < 5; j++) {
let item = tree.addItem("アイテム" + j, true)
for (let i = 0; i < 5; i++)
item.addItem("サブアイテム" + j + "-" + i, false)
}
//アイテムが選択された場合のイベント
tree.addEventListener('itemSelect', (e)=> {
const value = e.item.getItemText()
if (value) {
const no = list.getItemCount()
const date = (new Date()).toLocaleString()
list.addItem([no.toString(), value, date])
}
})
//listにヘッダを追加
list.addHeader(['番号', ['名前', 200], '時刻'])
//位置とサイズの設定
frame.setSize(800, 600)
frame.setPos()
}
5.HTMLを切り離し、DOMの操作を最小限に
Web技術はHTMLの進化とともに発展してきました。今となってはGUIを構築する上での主要な選択肢となっています。ただしSPAにおいては、HTMLをベタ書きしていくと、その取り扱いに対して足を引っ張られることが多々あります。できるだけメインプログラムからは切り離して扱う方が、見通しも良くなりプログラムの扱いも簡単になります。ということで、できる限り分離した結果がこのフレームワークです。
6.TypeScriptに特化
フレームワークを利用するプログラムは素のJavaScriptでも書くことが出来ますが、フレームワーク自体はTypeScriptで書いています。利点は型定義ファイル**.d.ts**が簡単に作れるからです。入力補完やドキュメントの表示が、開発環境から利用できるこの利点は途轍もなく大きいのです。敗北者にならないように、strictを付けてコンパイルをかけています。ただしasとanyは結構使っています。
7.ソースコード類
-
フレームワークソースコード
https://github.com/JavaScript-WindowFramework/javascript-window-framework -
サンプルソースコード
https://github.com/JavaScript-WindowFramework/jwf_sample01 -
リファレンス兼TypeDocViewerのサンプル
https://javascript-windowframework.github.io/TypeDocViewer/dist/
https://github.com/JavaScript-WindowFramework/TypeDocViewer
8.デザインをなんとかしたい
一人で作っている限界です。私にデザインセンスがないので、見た目が微妙なのです。こればっかりはどうにもなりません。想像したとおりのものを作ることは出来ても、想像の段階で微妙なのだから救いようがありません。ということで、こういう感じにした方が良いとか、こういう機能があると便利とかいうご意見があったら、お待ちしております。