apeescape2.com
  • メイン
  • リモートの台頭
  • プロジェクト管理
  • 技術
  • その他
技術

ページのリロード全体でのデータの永続化:Cookie、IndexedDB、およびその間のすべて

Webサイトにアクセスしているとします。ナビゲーションリンクの1つを右クリックし、新しいウィンドウでリンクを開くことを選択します。何が起こるべきですか?ほとんどのユーザーと同じように、新しいページには、リンクを直接クリックした場合と同じコンテンツが含まれていると思います。唯一の違いは、ページが新しいウィンドウに表示されることです。ただし、Webサイトがシングルページアプリケーション(SPA)の場合、このケースを慎重に計画しない限り、奇妙な結果が表示される可能性があります。

llc税分類sまたはc

SPAでは、一般的なナビゲーションリンクは、ハッシュマーク(#)で始まるフラグメント識別子であることが多いことを思い出してください。リンクを直接クリックしてもページは再読み込みされないため、JavaScript変数に保存されているすべてのデータが保持されます。しかし、新しいタブまたはウィンドウでリンクを開くと、ブラウザはページをリロードし、すべてのJavaScript変数を再初期化します。したがって、何らかの方法でそのデータを保持するための手順を実行しない限り、これらの変数にバインドされたHTML要素は異なる表示になります。

ページのリロード全体でのデータの永続化:Cookie、IndexedDB、およびその間のすべて



ページのリロード全体でのデータの永続化:Cookie、IndexedDB、およびその間のすべて つぶやき

F5キーを押すなどして、ページを明示的にリロードした場合にも、同様の問題が発生します。サーバーから変更を自動的にプッシュするメカニズムを設定しているので、F5キーを押す必要はないと思うかもしれません。しかし、私が一般的なユーザーであれば、ページをリロードすることは間違いありません。たぶん、私のブラウザが画面を間違って塗り直したように見えるか、最新の株価があることを確認したいだけです。

APIはステートレスである可能性があり、人間の相互作用はそうではありません

RESTful APIを介した内部リクエストとは異なり、人間のユーザーによるWebサイトとのやり取りはステートレスではありません。 Webユーザーとして、私はあなたのサイトへの訪問を、ほとんど電話のようなセッションと考えています。営業またはサポートラインに電話をかけるときと同じように、ブラウザが私のセッションに関するデータを記憶していることを期待しています。担当者は、電話の前半で話されたことを覚えていると思います。

セッションデータの明らかな例は、私がログインしているかどうか、ログインしている場合はどのユーザーとしてログインしているかです。ログイン画面を通過すると、サイトのユーザー固有のページを自由にナビゲートできるようになります。新しいタブまたはウィンドウでリンクを開いたときに別のログイン画面が表示された場合、それはあまりユーザーフレンドリーではありません。

別の例は、ショッピングカートの内容です。 eコマース 地点。 F5キーを押すとショッピングカートが空になると、ユーザーは動揺する可能性があります。

で書かれた従来の複数ページのアプリケーションでは PHP 、セッションデータは$ _SESSIONスーパーグローバル配列に格納されます。ただし、SPAでは、クライアント側のどこかにある必要があります。 SPAにセッションデータを保存するには、主に4つのオプションがあります。

  • クッキー
  • フラグメントの識別
  • Webストレージ
  • IndexedDB

4キロバイトのCookie

Cookieは、ブラウザの古い形式のWebストレージです。これらは元々、サーバーから受信したデータを1つのリクエストに保存し、後続のリクエストでサーバーに送り返すことを目的としていました。ただし、JavaScriptからは、Cookieを使用して、Cookieあたり最大4 KBのサイズ制限まで、ほぼすべての種類のデータを保存できます。 AngularJSは、Cookieを管理するためのngCookiesモジュールを提供します。どのフレームワークでも同様の機能を提供するjs-cookiesパッケージもあります。

作成したCookieは、ページの再読み込みであろうとAjaxリクエストであろうと、リクエストごとにサーバーに送信されることに注意してください。ただし、保存する必要のあるメインセッションデータがログインユーザーのアクセストークンである場合は、とにかくすべてのリクエストでこれをサーバーに送信する必要があります。この自動Cookie送信を、Ajaxリクエストのアクセストークンを指定する標準的な手段として使用しようとするのは自然なことです。

この方法でCookieを使用することは互換性がないと主張するかもしれません RESTful 建築。ただし、この場合、APIを介した各リクエストはステートレスであり、いくつかの入力といくつかの出力があるため、問題ありません。入力の1つがCookieを介して面白い方法で送信されているだけです。ログインAPIリクエストがアクセストークンをCookieで返送するように手配できる場合は、クライアント側のコードでCookieを処理する必要はほとんどありません。繰り返しになりますが、これは、通常とは異なる方法で返されるリクエストからの別の出力です。

Cookieには、Webストレージに比べて1つの利点があります。ログインフォームに「ログインしたままにする」チェックボックスを指定できます。セマンティクスでは、チェックを外したままにすると、ページをリロードしたり、新しいタブやウィンドウでリンクを開いたりしてもログインしたままになると思いますが、ブラウザを閉じると必ずログアウトされます。共有コンピュータを使用している場合、これは重要な安全機能です。後で説明するように、Webストレージはこの動作をサポートしていません。

では、このアプローチは実際にはどのように機能するのでしょうか。サーバー側でLoopBackを使用しているとします。 Personモデルを定義し、組み込みのUserモデルを拡張して、ユーザーごとに維持するプロパティを追加しました。を構成しました 人 RESTを介して公開されるモデル。次に、server / server.jsを微調整して、目的のCookieの動作を実現する必要があります。以下はserver / server.jsで、slcループバックによって生成されたものから始まり、マークされた変更が加えられています。

var loopback = require('loopback'); var boot = require('loopback-boot'); var app = module.exports = loopback(); app.start = function() { // start the web server return app.listen(function() { app.emit('started'); var baseUrl = app.get('url').replace(//$/, ''); console.log('Web server listening at: %s', baseUrl); if (app.get('loopback-component-explorer')) { var explorerPath = app.get('loopback-component-explorer').mountPath; console.log('Browse your REST API at %s%s', baseUrl, explorerPath); } }); }; // start of first change app.use(loopback.cookieParser('secret')); // end of first change // Bootstrap the application, configure models, datasources and middleware. // Sub-apps like REST API are mounted via boot scripts. boot(app, __dirname, function(err) { if (err) throw err; // start of second change app.remotes().after('Person.login', function (ctx, next) { if (ctx.result.id) { var opts = {signed: true}; if (ctx.req.body.rememberme !== false) { opts.maxAge = 1209600000; } ctx.res.cookie('authorization', ctx.result.id, opts); } next(); }); app.remotes().after('Person.logout', function (ctx, next) { ctx.res.cookie('authorization', ''); next(); }); // end of second change // start the server if `$ node server.js` if (require.main === module) app.start(); });

最初の変更は、「シークレット」をCookie署名シークレットとして使用するようにCookieパーサーを構成し、それによって署名されたCookieを有効にします。 LoopBackはCookieの「authorization」または「access_token」のいずれかでアクセストークンを検索しますが、そのようなCookieに署名する必要があるため、これを行う必要があります。実際、この要件は無意味です。 Cookieに署名することは、Cookieが変更されていないことを確認することを目的としています。ただし、アクセストークンを変更する危険はありません。結局のところ、通常のパラメーターとして、署名されていない形式でアクセストークンを送信することもできます。したがって、署名されたCookieを他の目的で使用している場合を除き、Cookieの署名の秘密を推測するのが難しいことを心配する必要はありません。

従業員のスプレッドシート計算機の実際のコスト

2番目の変更は、Person.loginメソッドとPerson.logoutメソッドの後処理を設定します。ために Person.login 、結果のアクセストークンを取得し、署名されたCookieの「承認」としてクライアントに送信します。クライアントは、資格情報パラメータremembermeにもう1つのプロパティを追加して、Cookieを2週間永続化するかどうかを示すことができます。デフォルトはtrueです。ログインメソッド自体はこのプロパティを無視しますが、ポストプロセッサはそれをチェックします。

ために Person.logout 、このCookieをクリアします。

これらの変更の結果は、StrongLoop APIExplorerですぐに確認できます。通常、Person.loginリクエストの後、アクセストークンをコピーして右上のフォームに貼り付け、[アクセストークンの設定]をクリックする必要があります。しかし、これらの変更により、それを行う必要はありません。アクセストークンはCookieの「承認」として自動的に保存され、後続のリクエストごとに返送されます。 ExplorerがPerson.loginからの応答ヘッダーを表示しているとき、JavaScriptがSet-Cookieヘッダーを表示することは許可されていないため、Cookieは省略されます。しかし、安心してください、クッキーはそこにあります。

クライアント側では、ページのリロード時に、Cookieの「承認」が存在するかどうかを確認します。その場合は、現在のuserIdのレコードを更新する必要があります。おそらくこれを行う最も簡単な方法は、ログインが成功したときにuserIdを別のCookieに保存して、ページのリロード時に取得できるようにすることです。

フラグメント識別子

SPAとして実装されているWebサイトにアクセスしているとき、ブラウザのアドレスバーのURLは「https://example.com/#/my-photos/37」のようになります。このフラグメント識別子部分「#/ my-photos / 37」は、セッションデータとして表示できる状態情報のコレクションです。この場合、私はおそらく私の写真の1つ、IDが37の写真を見ています。

フラグメント識別子内に他のセッションデータを埋め込むことを決定できます。前のセクションでは、アクセストークンがCookieの「承認」に保存されているため、何らかの方法でuserIdを追跡する必要があったことを思い出してください。 1つのオプションは、別のCookieに保存することです。しかし、別のアプローチは、それをフラグメント識別子に埋め込むことです。ログインしている間、アクセスするすべてのページに「#/ u / XXX」で始まるフラグメント識別子が含まれるように決定できます。ここで、XXXはuserIdです。したがって、前の例では、userIdが59の場合、フラグメント識別子は「#/ u / 59 / my-photos / 37」になる可能性があります。

理論的には、アクセストークン自体をフラグメント識別子に埋め込んで、CookieやWebストレージの必要性を回避することができます。しかし、それは悪い考えです。アクセストークンがアドレスバーに表示されます。カメラを持って私の肩越しに見ている人は誰でも画面のスナップショットを撮ることができ、それによって私のアカウントにアクセスできます。

最後に、フラグメント識別子をまったく使用しないようにSPAを設定することができます。代わりに、「http://example.com/app/dashboard」や「http://example.com/app/my-photos/37」などの通常のURLを使用し、サーバーはトップレベルのHTMLを返すように構成されています。これらのURLのいずれかの要求に応答するSPA。次に、SPAは、フラグメント識別子ではなく、パス(「/ app / dashboard」や「/ app / my-photos / 37」など)に基づいてルーティングを行います。ナビゲーションリンクのクリックを傍受し、 History.pushState() 新しいURLをプッシュしてから、通常どおりルーティングを続行します。また、ポップステートイベントをリッスンして、ユーザーが[戻る]ボタンをクリックしたことを検出し、復元されたURLでルーティングを続行します。これを実装する方法の詳細は、この記事の範囲を超えています。ただし、この手法を使用すると、フラグメント識別子の代わりにセッションデータをパスに格納できることは明らかです。

第11章の破産では、債務者のみが再編の計画を提案することができます。

Webストレージ

Webストレージは、JavaScriptがブラウザ内にデータを保存するためのメカニズムです。 Cookieと同様に、Webストレージはオリジンごとに分離されています。保存された各アイテムには名前と値があり、どちらも文字列です。ただし、Webストレージはサーバーからは完全に見えず、Cookieよりもはるかに大きなストレージ容量を提供します。 Webストレージには、ローカルストレージとセッションストレージの2種類があります。

ローカルストレージのアイテムは、すべてのウィンドウのすべてのタブに表示され、ブラウザを閉じた後も保持されます。この点で、それは非常に遠い将来の有効期限を持つクッキーのように動作します。したがって、ユーザーがログインフォームで「ログインしたままにする」にチェックを入れた場合にアクセストークンを保存するのに適しています。

セッションストレージのアイテムは、それが作成されたタブ内にのみ表示され、そのタブを閉じると消えます。これにより、その存続期間は他のCookieの存続期間とは大きく異なります。セッションCookieは、すべてのウィンドウのすべてのタブに引き続き表示されることを思い出してください。

AngularJSSDKを使用する場合 ループバック 、クライアント側は自動的にWebストレージを使用して、アクセストークンとuserIdの両方を保存します。これは、js / services /lb-services.jsのLoopBackAuthサービスで発生します。 RememberMeパラメーターがfalse(通常は「ログイン状態を維持する」チェックボックスがオフになっていることを意味します)でない限り、ローカルストレージを使用します。falseの場合、セッションストレージを使用します。

その結果、[ログインしたままにする]をオフにしてログインし、新しいタブまたはウィンドウでリンクを開くと、そこにログインできなくなります。ログイン画面が表示される可能性があります。これが許容できる動作であるかどうかは、自分で判断できます。それぞれが異なるユーザーとしてログインする複数のタブを持つことができる優れた機能だと考える人もいるかもしれません。または、共有コンピュータを使用する人がほとんどいないと判断する可能性があるため、[ログインしたままにする]チェックボックスを完全に省略できます。

では、AngularJS SDK for LoopBackを使用することにした場合、セッションデータの処理はどのようになりますか?サーバー側で以前と同じ状況が発生したとします。Personモデルを定義し、Userモデルを拡張し、RESTを介してPersonモデルを公開しました。 Cookieを使用しないため、前述の変更は必要ありません。

クライアント側では、最も外側のコントローラーのどこかに、現在ログインしているユーザーのuserIdを保持する$ scope.currentUserIdのような変数があるか、ユーザーがログインしていない場合はnullがあります。次に、ページの再読み込みを適切に処理するには、このステートメントをそのコントローラーのコンストラクター関数に含めるだけです。

$scope.currentUserId = Person.getCurrentId();

とても簡単です。コントローラの依存関係として「Person」をまだ追加していない場合は追加します。

IndexedDB

IndexedDBは、ブラウザに大量のデータを保存するための新しい機能です。これを使用すると、シリアル化せずに、オブジェクトや配列など、任意のJavaScriptタイプのデータを格納できます。データベースに対するすべてのリクエストは非同期であるため、リクエストが完了するとコールバックを受け取ります。

IndexedDBを使用して、サーバー上のデータとは関係のない構造化データを保存できます。例としては、カレンダー、やることリスト、ローカルでプレイされるセーブドゲームなどがあります。この場合、アプリケーションは実際にはローカルアプリケーションであり、Webサイトはそれを配信するための手段にすぎません。

現在、InternetExplorerとSafariはIndexedDBを部分的にしかサポートしていません。他の主要なブラウザはそれを完全にサポートしています。ただし、現時点での重大な制限の1つは、FirefoxがプライベートブラウジングモードでIndexedDBを完全に無効にすることです。

IndexedDBを使用する具体的な例として、 スライディングパズルアプリケーション PavolDanišによるもので、移動するたびに、最初のパズルであるAngularJSロゴに基づく基本的な3x3スライディングパズルの状態を保存するように調整します。ページをリロードすると、この最初のパズルの状態が復元されます。

私は フォーク これらの変更が加えられたリポジトリの一部であり、すべてapp / js / Puzzle /slidingPuzzle.jsにあります。ご覧のとおり、IndexedDBの基本的な使用法でさえかなり複雑です。以下にハイライトを示します。まず、ページの読み込み中に関数restoreが呼び出され、IndexedDBデータベースが開きます。

/* * Tries to restore game */ this.restore = function(scope, storekey) { this.storekey = storekey; if (this.db) { this.restore2(scope); } else if (!window.indexedDB) { console.log('SlidingPuzzle: browser does not support indexedDB'); this.shuffle(); } else { var self = this; var request = window.indexedDB.open('SlidingPuzzleDatabase'); request.onerror = function(event) { console.log('SlidingPuzzle: error opening database, ' + request.error.name); scope.$apply(function() { self.shuffle(); }); }; request.onupgradeneeded = function(event) { event.target.result.createObjectStore('SlidingPuzzleStore'); }; request.onsuccess = function(event) { self.db = event.target.result; self.restore2(scope); }; } };

ザ・ request.onupgradeneeded イベントは、データベースがまだ存在しない場合を処理します。この場合、オブジェクトストアを作成します。

データベースが開かれると、関数 restore2 が呼び出され、指定されたキー(この場合は実際には定数「Basic」になります)を持つレコードを検索します。

/* * Tries to restore game, once database has been opened */ this.restore2 = function(scope) { var transaction = this.db.transaction('SlidingPuzzleStore'); var objectStore = transaction.objectStore('SlidingPuzzleStore'); var self = this; var request = objectStore.get(this.storekey); request.onerror = function(event) { console.log('SlidingPuzzle: error reading from database, ' + request.error.name); scope.$apply(function() { self.shuffle(); }); }; request.onsuccess = function(event) { if (!request.result) { console.log('SlidingPuzzle: no saved game for ' + self.storekey); scope.$apply(function() { self.shuffle(); }); } else { scope.$apply(function() { self.grid = request.result; }); } }; }

そのようなレコードが存在する場合、その値がパズルのグリッド配列に置き換わります。ゲームの復元でエラーが発生した場合は、以前と同じようにタイルをシャッフルします。グリッドはタイルオブジェクトの3x3配列であり、それぞれがかなり複雑であることに注意してください。 IndexedDBの大きな利点は、シリアル化せずにそのような値を格納および取得できることです。

を使用しております $ apply モデルが変更されたことをAngularJSに通知するため、ビューが適切に更新されます。これは、更新がDOMイベントハンドラー内で行われているため、AngularJSはそれ以外の場合は変更を検出できないためです。 IndexedDBを使用するAngularJSアプリケーションは、この理由からおそらく$ applyを使用する必要があります。

銀行の財務機能

ユーザーによる移動など、グリッド配列を変更するアクションの後に、関数saveが呼び出され、更新されたグリッド値に基づいて、適切なキーでレコードが追加または更新されます。

/* * Tries to save game */ this.save = function() { if (!this.db) { return; } var transaction = this.db.transaction('SlidingPuzzleStore', 'readwrite'); var objectStore = transaction.objectStore('SlidingPuzzleStore'); var request = objectStore.put(this.grid, this.storekey); request.onerror = function(event) { console.log('SlidingPuzzle: error writing to database, ' + request.error.name); }; request.onsuccess = function(event) { // successful, no further action needed }; }

残りの変更は、適切なタイミングで上記の関数を呼び出すことです。あなたはレビューすることができます コミット すべての変更を表示します。 3つの高度なパズルではなく、基本的なパズルに対してのみ復元を呼び出していることに注意してください。 3つの高度なパズルにはapi属性があるという事実を利用しているため、通常のシャッフルを実行します。

高度なパズルも保存して復元したい場合はどうなりますか?それにはいくつかのリストラが必要になります。高度なパズルのそれぞれで、ユーザーは画像ソースファイルとパズルのサイズを調整できます。したがって、この情報を含めるには、IndexedDBに格納されている値を拡張する必要があります。さらに重要なのは、復元からそれらを更新する方法が必要になることです。これは、このすでに長い例では少し多いです。

結論

ほとんどの場合、セッションデータを保存するにはWebストレージが最善の策です。すべての主要なブラウザで完全にサポートされており、Cookieよりもはるかに大きなストレージ容量を提供します。

サーバーがCookieを使用するように既に設定されている場合、またはすべてのウィンドウのすべてのタブでデータにアクセスできるようにする必要があるが、ブラウザーを閉じたときにCookieが削除されるようにする場合は、Cookieを使用します。

すでにフラグメント識別子を使用して、ユーザーが見ている写真のIDなど、そのページに固有のセッションデータを保存しています。フラグメント識別子に他のセッションデータを埋め込むこともできますが、これはWebストレージやCookieに勝る利点はありません。

IndexedDBを使用すると、他のどの手法よりもはるかに多くのコーディングが必要になる可能性があります。ただし、保存している値が複雑なJavaScriptオブジェクトであり、シリアル化が難しい場合、または トランザクションモデル 、それならそれは価値があるかもしれません。

デザインの原則を打ち破る(インフォグラフィック付き)

Uxデザイン

デザインの原則を打ち破る(インフォグラフィック付き)
宿題をする:AWS認定ソリューションアーキテクト試験の7つのヒント

宿題をする:AWS認定ソリューションアーキテクト試験の7つのヒント

バックエンド

人気の投稿
残り火データ:残り火データライブラリの包括的なチュートリアル
残り火データ:残り火データライブラリの包括的なチュートリアル
あなたのデザインを後押しするための創造性の練習
あなたのデザインを後押しするための創造性の練習
アイコンの使いやすさとデザインのベストプラクティス
アイコンの使いやすさとデザインのベストプラクティス
2020年のプライベートエクイティの状態
2020年のプライベートエクイティの状態
Salesforceバックエンドエンジニア
Salesforceバックエンドエンジニア
 
ビッグデータ:医薬品のR&D窮状の処方箋
ビッグデータ:医薬品のR&D窮状の処方箋
スクラムの5つの誤った希望とそれらを修正する方法
スクラムの5つの誤った希望とそれらを修正する方法
GWTがブラウザの拡張現実を解き放つ方法
GWTがブラウザの拡張現実を解き放つ方法
宿題をする:AWS認定ソリューションアーキテクト試験の7つのヒント
宿題をする:AWS認定ソリューションアーキテクト試験の7つのヒント
最大フローと線形割り当て問題
最大フローと線形割り当て問題
人気の投稿
  • nodejs静的サイトジェネレーター
  • ポーターのファイブフォースモデルの背後にある経験則は何ですか?
  • 大規模なコンピューターでの大量処理の固有の効率は、_____と呼ばれます。
  • スポーツマネジメントのユニークな側面の1つは、間接的な収入が直接的な収入を超えることが多いことです。
  • ギリシャの債務危機とは
  • ノードサーバーとは
カテゴリー
Webフロントエンド 製品ライフサイクル データサイエンスとデータベース ツールとチュートリアル モバイル 人とチーム 投資家と資金調達 計画と予測 アジャイルタレント Kpiと分析

© 2021 | 全著作権所有

apeescape2.com