私はフロントエンド開発者ではありませんが、ビジネスアプリケーションに関連してWEBインターフェイスのラピッドプロトタイピングのタスクがある場合があります。 業界の詳細は、多くの類似したエンティティ(および対話形式)であり、OOP、具体的には継承を使用すると非常に簡単になります。 WEBの世界では、コンポジションは主に複雑性と戦うために使用されると聞きましたが、継承を使用したかったのです-より強固で接続されたアプリケーション構造(弱く接続されたコンポーネントとは対照的)を提供し、主題領域をよく反映します。 タスクはこのように聞こえます-継承階層によってリンクされたサーバー上のデータ構造があります、ブラウザで同様のインタラクティブフォーム(ページ)の階層を作成する必要があります。ここでは、マークアップ、スタイル、および動作が継承されます-当然、エンティティを再定義する機能があります。
自分に設定した制限は次のとおりです。
- WebベースのWEBインターフェイスの生成(このタスクを簡単に解決する必要がある)は時代遅れであると考えており、クライアント上でUIを厳密に生成するアプローチを厳守し、サーバーにはデータストレージと重い計算のみを残しています(はい、PWAを信じています)。
- インターフェースは、純粋なHTMLのテキスト形式である必要があります-HTMLのオブジェクトラッパー(Dartなど)に我慢できません。これは、SQLのさまざまなラッパーで苦しめられていたためです。 )、彼らは予想よりもはるかに遅く、食いしん坊でした。 この痕跡はしっかりと私の心の中にあり、おそらく90年代のように、SQL、HTML、CSS-テキストを常に書くでしょう。 そして、イベントハンドラーでさえ、スクリプトとして割り当てるのではなく、マークアップ<input onkeydown = '' doit(this) ''>にハングアップすることを好みます。 質問は宗教的なものであり、誰と一緒に行われないかを理解しています。 一方、古い宣言言語が悪くないのに、なぜ新しい宣言型言語を学ぶのか。
既製のソリューションの表面的な検索では結果が得られず、多くのフレームワークに対処する時間がありませんでした。特に、継承のクラスとカプセル化されたモジュールがあり、大人のように純粋なJSでバイクを切ることにしました その結果、次のアーキテクチャが登場しました。
-各ページへのエントリポイントは、HTMLではなくJavascriptである必要があります。 私の場合、ページは単一のJSモジュールファイルで表されます。デフォルトでは、このページのレイアウト、スタイル、および動作を定義するクラスのみがエクスポートされます。
-ページクラスは相互に継承でき、すべてがHEADコンテンツ、基本スタイル、基本BODYコンテンツ(フッター、ナビゲーションなど)、および基本ハンドラー関数を定義する同じ基本祖先に戻ります。
-各ページは、一度アクセスすると、ユーザーが入力したデータやサーバーから受信したデータとともに、DOMツリーのクローンをメモリに保存します。 ページを再入力すると、DOM(つまり、マークアップ+スタイル+スクリプト+データ)が復元されます。 データで満たされたページを再入力することは、飽和したデスクトップフォームを多くの関連する小さなフォームに分割する必要があるモバイルデバイスのコンテキストで特に役立ちます。
-すべてのページは、互いの保存されたDOMにアクセスできます。 したがって、共通のセッションオブジェクトを持つ必要はありません。各フォームにはデータ自体が保存され、ウィンドウオブジェクトにリンクを追加するだけです。
プロの「トップランナー」にとって、上記のすべては気味の悪いことに聞こえますが、私は6月として、結果のソリューションの美しさと簡潔さに非常に満足しており、この記事をここに残すことにしました-それは他の6月に役立つかもしれません
簡単な例として、3ページのアプリケーション 。 最初のページはホームで、2番目のユーザーはデータファイルをアップロードし、3番目のページでは式を入力し、2番目のページのデータに対する計算結果を受け取ります。 さらに、彼らが言うように、「話は安い、コードを見せて」。
アプリケーションへのエントリポイントはindex.htmlです。 ホームページクラスをインポートし、インスタンス化して表示します。 また、次のようなマークアップで使用されるグローバルナビゲーション関数もインポートします。<button onclick = '' nav( 'Page_Home') ''>
<!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <script type="module"> import Page_Home from './Page_Home.js' (new Page_Home()).show() import {nav} from './Nav.js' window.nav = nav </script> </body> </html>
すべてのページの基本祖先 -さまざまなマークアップブロック、ハンドラー関数(存在する場合)、初期初期化メソッドload()、およびdisplay(view()メソッドを返すメソッドが含まれます。
// module Page_.js export default class Page_ { // HEAD head() { return ` <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JS OOP</title> <style></style> `} // , style() { return ` .menubar {background-color: silver; font-weight: bold} .a {color: darkblue} .a:hover {color: darkred; cursor: pointer} .acurr {color: darkred; background-color: white} ` } // BODY body() { return ` <div class="menubar"> <span class="a" onclick="nav('Page_Home')"> Home </span> <span class="a" onclick="nav('Page_Upload')"> Upoad data </span> <span class="a" onclick="nav('Page_Calculate')"> Calculate </span> </div> <div id="content"></div> `} // , content() { return ` `} // DOM ( HEAD BODY) constructor() { this.headsave = undefined this.bodysave = undefined } // , load() { document.head.innerHTML = this.head() document.querySelector('head > style').innerHTML = this.style() document.body.innerHTML = this.body() document.querySelector('body > #content').innerHTML = this.content() } // // DOM , DOM // window // window.page // show() { if (window.page !== undefined) { window.page.headsave = document.head.innerHTML window.page.bodysave = document.body.cloneNode(true) } window.page = this if (window[this.constructor.name] === undefined) { window[this.constructor.name] = this this.load() } else { document.head.innerHTML = this.headsave document.body = this.bodysave } let a = document.querySelector('[onclick = "nav(\'' + this.constructor.name + '\')"]'); if (a !== null) { a.className = 'acurr' } } }
ホームページ -コンテンツを返すメソッドのみをオーバーライドします。
// module Page_Home.js import Page_ from './Page_.js' export default class Page_Home extends Page_ { content() { return ` <h3>Hi, geek !</h3> `} }
ファイルダウンロードページ -コンテンツを再定義し、スタイルを1つ追加し、新しいfselect()ハンドラーを導入します。 ハンドラーがマークアップでどのように割り当てられているかに注意してください-グローバル変数ページを通して、常に現在のページへのリンクが含まれています。
// module Page_Upload.js import Page_ from './Page_.js' export default class Page_Upload extends Page_ { content() { return ` <br> <input type="file" onchange="page.fselect(this)"/> <br><br> <textarea id="fcontent"></textarea> `} style() { return super.style() + ` textarea {width: 90vw; height: 15em} `} fselect(elem) { let fr = new FileReader() fr.readAsText(elem.files[0]) fr.onload = (ev) => { document.querySelector('#fcontent').value = ev.target.result } } }
計算ページ -コンテンツの再定義、ページタイトルの変更、ハンドラーの追加。
// module Page_Calculate.js import Page_ from './Page_.js' export default class Page_Calculate extends Page_ { content() { return ` <br> <label for="formula">Formula:</label><br> <textarea id="formula" style="width:90vw; height:5em">data.length</textarea> <br><br> <button onclick="page.calc()">Calculate...</button> <br><br> <div id = "result"></div> `} load() { super.load() let t = document.querySelector('head > title') t.innerHTML = 'Calculation result - ' + t.innerHTML } calc() { let formula = document.querySelector('#formula').value if (!formula) { return alert('Formula is empty !') } let datapage = window.Page_Upload; if (datapage === undefined) { return nodata() } let data = datapage.bodysave.querySelector('#fcontent').value if (!data) { return nodata() } document.querySelector('#result').innerHTML = 'Result: ' + eval(formula) function nodata() { alert('Data is not loaded !') } } }
グローバルナビゲーション機能 。 要求されたページがメモリにない場合は、クラスから作成します。それ以外の場合は、アクティブにします。 残念ながら、ナビゲーションモジュールはすべてのページのクラスを静的にインポートする必要があります。 動的インポートが最終的にブラウザに実装されると、機能のみが残ります。
// module Nav.js import Page_Home from './Page_Home.js' import Page_Upload from './Page_Upload.js' import Page_Calculate from './Page_Calculate.js' export function nav(pagename) { if (window[pagename] === undefined) { eval('new ' + pagename + '()').show() } else { window[pagename].show() } }
実際、これは私が見せたかったすべてです-ページは非常にコンパクトであることが判明しました(継承の階層が深いほど、子孫はよりコンパクトになります)、スタイルと関数は厳密にカプセル化され、マークアップとコードは1つのファイルに並んで配置されます。 コンテキストを保存すると、複数ページの階層フォーム、マスターアシスタントなどを構築できます。
短所:
- JSの継承は構文的に少し奇妙に実装されていますが、それに慣れることができます。 複数形はありませんが、このタスクにはほとんど必要ありません。
- JS内にHTMLとCSSの断片があり、ツールチップとオートコンプリートが機能しないことをエディターに説明するのは困難ですが、これは解決できると思います。
実際の例はこちらです。
PS:この情報に感謝します-継承がWEBフレームワークで、そして一般的にフロントエンド開発で使用されるかどうか。
ありがとう