純粋なJavaScriptでWebページを生成するときに継承を使用する

こんにちは、Habr!



私はフロントエンド開発者ではありませんが、ビジネスアプリケーションに関連してWEBインターフェイスのラピッドプロトタイピングのタスクがある場合があります。 業界の詳細は、多くの類似したエンティティ(および対話形式)であり、OOP、具体的には継承を使用すると非常に簡単になります。 WEBの世界では、コンポジションは主に複雑性と戦うために使用されると聞きましたが、継承を使用したかったのです-より強固で接続されたアプリケーション構造(弱く接続されたコンポーネントとは対照的)を提供し、主題領域をよく反映します。 タスクはこのように聞こえます-継承階層によってリンクされたサーバー上のデータ構造があります、ブラウザで同様のインタラクティブフォーム(ページ)の階層を作成する必要があります。ここでは、マークアップ、スタイル、および動作が継承されます-当然、エンティティを再定義する機能があります。



自分に設定した制限は次のとおりです。





プロの「トップランナー」にとって、上記のすべては気味の悪いことに聞こえますが、私は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つのファイルに並んで配置されます。 コンテキストを保存すると、複数ページの階層フォーム、マスターアシスタントなどを構築できます。



短所:





実際の例はこちらです。



PS:この情報に感謝します-継承がWEBフレームワークで、そして一般的にフロントエンド開発で使用されるかどうか。



ありがとう



All Articles