シングルページ アプリケーションが今日非常に人気となっているため、かつては驚異的だったフロントエンド ルーティングが、主要なフレームワークの基本標準となっています。各フレームワークは強力なルーティング機能を提供するため、ルーティングの実装は複雑になります。ルーティングの内部実装を理解するのはまだ少し難しいですが、ルーティング実装の基本原則を理解するだけであれば比較的簡単です。本記事では、フロントエンドルーティング、ハッシュ、ヒストリーの主流の実装方法を対象に、ネイティブ JS/React/Vue の 6 つのバージョンを参考として提供します。各バージョンの実装コードは 25 行から 40 行程度です (空白行を含む)。 フロントエンドルーティングとは何ですか?ルーティングの概念はサーバーから来ており、ルーティングは URL と処理機能間のマッピング関係を記述します。 Web フロントエンドのシングルページ アプリケーション SPA (シングル ページ アプリケーション) では、ルーティングは URL と UI 間のマッピング関係を表します。このマッピングは一方向です。つまり、URL を変更すると UI が更新されます (ページは更新されません)。 フロントエンドルーティングを実装するにはどうすればいいですか?フロントエンド ルーティングを実装するには、次の 2 つの主要な問題に対処する必要があります。 ページを更新せずに URL を変更するにはどうすればよいですか? URL が変更されたことを検出するにはどうすればよいでしょうか? 次の 2 つの主要な質問には、それぞれハッシュと履歴の実装を使用して回答します。 ハッシュ実装
歴史の実装
フロントエンドルーティング実装のネイティブJSバージョン前のセクションで説明した 2 つの実装方法に基づいて、ハッシュ バージョンと履歴バージョンのルーティングがそれぞれ実装されます。この例では、ネイティブ HTML/JS 実装を使用し、フレームワークに依存しません。 ハッシュベースの実装操作効果: HTML部分: <本文> <ul> ref=""> <!-- ルートを定義する --> <li><a href="#/home" rel="external nofollow" >ホーム</a></li> <li><a href="#/about" rel="external nofollow" >について</a></li> ref=""> <!-- ルートに対応する UI をレンダリングします --> <div id="ルートビュー"></div> </ul> </本文> JavaScript部分: // ページはロード後にハッシュ変更をトリガーしません。ここでは、ハッシュ変更イベントをアクティブにトリガーします。window.addEventListener('DOMContentLoaded', onLoad) // ルートの変更をリッスンする window.addEventListener('hashchange', onHashChange) // ルーティングビュー var routerView = null 関数onLoad(){ ルータービュー = document.querySelector('#routeView') ハッシュ変更() } // ルートが変更されたら、ルートに応じて対応するUIをレンダリングします 関数onHashChange() { スイッチ (location.hash) { ケース '#/home': routerView.innerHTML = 'ホーム' 戻る ケース '#/about': routerView.innerHTML = '概要' 戻る デフォルト: 戻る } } 履歴ベースの実装操作効果: HTML部分: <本文> <ul> <li><a href='/home'>ホーム</a></li> <li><a href='/about'>について</a></li> <div id="ルートビュー"></div> </ul> </本文> JavaScript部分: // ページはロード後にハッシュ変更をトリガーしません。ここでは、ハッシュ変更イベントをアクティブにトリガーします。window.addEventListener('DOMContentLoaded', onLoad) // ルートの変更をリッスンする window.addEventListener('popstate', onPopState) // ルーティングビュー var routerView = null 関数onLoad(){ ルータービュー = document.querySelector('#routeView') オンポップステート() href=""> //<a> タグのクリック イベントのデフォルトの動作をインターセプトします。クリックされると、pushState を使用して URL を変更し、手動 UI を更新することで、リンクをクリックしたときに URL と UI を更新する効果を実現します。 var linkList = document.querySelectorAll('a[href]') linkList.forEach(el => el.addEventListener('click', 関数(e) { e.preventDefault() history.pushState(null, '', el.getAttribute('href')) オンポップステート() })) } // ルートが変更されたら、ルートに応じて対応するUIをレンダリングします 関数onPopState(){ スイッチ (場所.パス名) { '/home'の場合: routerView.innerHTML = 'ホーム' 戻る ケース '/about': routerView.innerHTML = '概要' 戻る デフォルト: 戻る } } フロントエンドルーティング実装のReactバージョンハッシュベースの実装操作効果: 使用方法は react-router と似ています。 <ブラウザルーター> <ul> <li> <Link to="/home">ホーム</Link> </li> <li> <Link to="/about">について</Link> </li> </ul> <Route path="/home" render={() => <h2>ホーム</h2>} /> <Route path="/about" render={() => <h2>概要</h2>} /> </ブラウザルーター> ブラウザルータの実装 デフォルトのクラス BrowserRouter をエクスポートし、React.Component を拡張します { 状態 = { 現在のパス: utils.extractHashPath(window.location.href) }; onHashChange = e => { const currentPath = utils.extractHashPath(e.newURL); console.log("onHashChange:", 現在のパス); this.setState({ currentPath }); }; コンポーネントマウント() { window.addEventListener("ハッシュ変更"、this.onHashChange); } コンポーネントのマウントを解除します(){ window.removeEventListener("ハッシュ変更"、this.onHashChange); } 与える() { 戻る ( <RouteContext.Provider 値 = {{currentPath: this.state.currentPath}}> {this.props.children} </ルートコンテキスト.プロバイダー> ); } } ルートの実装 エクスポートデフォルト({パス、レンダリング})=>( <ルートコンテキスト.コンシューマー> {({currentPath}) => currentPath === パス && render()} </RouteContext.Consumer> ); リンクの実装 export default ({ to, ...props }) => <a {...props} href={"#" + to} />; 履歴ベースの実装操作効果: 使用方法は react-router と似ています。 <履歴ルーター> <ul> <li> <Link to="/home">ホーム</Link> </li> <li> <Link to="/about">について</Link> </li> </ul> <Route path="/home" render={() => <h2>ホーム</h2>} /> <Route path="/about" render={() => <h2>概要</h2>} /> </履歴ルーター> HistoryRouter 実装 デフォルトのクラス HistoryRouter をエクスポートし、React.Component を拡張します { 状態 = { 現在のパス: utils.extractUrlPath(window.location.href) }; onPopState = e => { 定数 currentPath = utils.extractUrlPath(window.location.href); console.log("onPopState:", 現在のパス); this.setState({ currentPath }); }; コンポーネントマウント() { window.addEventListener("popstate", this.onPopState); } コンポーネントのマウントを解除します(){ window.removeEventListener("popstate", this.onPopState); } 与える() { 戻る ( <RouteContext.Provider 値 = {{currentPath: this.state.currentPath, onPopState: this.onPopState}}> {this.props.children} </ルートコンテキスト.プロバイダー> ); } } ルートの実装 エクスポートデフォルト({パス、レンダリング})=>( <ルートコンテキスト.コンシューマー> {({currentPath}) => currentPath === パス && render()} </RouteContext.Consumer> ); リンクの実装 エクスポートデフォルト({to、...props})=>( <ルートコンテキスト.コンシューマー> {({ onPopState }) => ( <a href="" {...小道具} onClick={e => { e.preventDefault(); window.history.pushState(null, "", to); ポップステート(); }} /> )} </RouteContext.Consumer> ); Vue バージョンのフロントエンドルーティング実装ハッシュベースの実装操作効果: 使用方法は vue-router に似ています (vue-router はプラグイン メカニズムを通じてルートを挿入しますが、これにより実装の詳細が隠されます。コードを直感的に保つために、ここでは Vue プラグインのカプセル化は使用されません)。 <div> <ul> <li><router-link to="/home">ホーム</router-link></li> <li><router-link to="/about">について</router-link></li> </ul> <ルータービュー></ルータービュー> </div> 定数ルート = { '/家': { テンプレート: '<h2>ホーム</h2>' }, '/について': { テンプレート: '<h2>概要</h2>' } } constアプリ = 新しいVue({ el: '.vue.hash', コンポーネント: 'ルータービュー': ルータービュー、 'ルーターリンク': ルーターリンク }, 作成前() { this.$routes = ルート } }) ルータビューの実装: <テンプレート> <コンポーネント:is="routeView" /> </テンプレート> <スクリプト> '~/utils.js' から utils をインポートします エクスポートデフォルト{ データ () { 戻る { ルートビュー: null } }, 作成された(){ this.boundHashChange = this.onHashChange.bind(this) }, マウント前() { window.addEventListener('hashchange', this.boundHashChange) }, マウントされた(){ onHashChange() は、 }, 破棄する前に() { window.removeEventListener('hashchange', this.boundHashChange) }, メソッド: { ハッシュ変更() { 定数パス = utils.extractHashPath(window.location.href) this.routeView = this.$root.$routes[path] || null console.log('vue:hashchange:', パス) } } } </スクリプト> ルータリンクの実装: <テンプレート> <a @click.prevent="onClick" href=''><スロット></スロット></a> </テンプレート> <スクリプト> エクスポートデフォルト{ 小道具: { から: 文字列 }, メソッド: { クリック時(){ window.location.hash = '#' + this.to } } } </スクリプト> 履歴ベースの実装操作効果: 使用方法は vue-router と似ています。 <div> <ul> <li><router-link to="/home">ホーム</router-link></li> <li><router-link to="/about">について</router-link></li> </ul> <ルータービュー></ルータービュー> </div> 定数ルート = { '/家': { テンプレート: '<h2>ホーム</h2>' }, '/について': { テンプレート: '<h2>概要</h2>' } } constアプリ = 新しいVue({ el: '.vue.history', コンポーネント: 'ルータービュー': ルータービュー、 'ルーターリンク': ルーターリンク }, 作成された(){ this.$routes = ルート this.boundPopState = this.onPopState.bind(this) }, マウント前() { window.addEventListener('popstate', this.boundPopState) }, 破棄前() { window.removeEventListener('popstate', this.boundPopState) }, メソッド: { onPopState (...引数) { this.$emit('popstate', ...引数) } } }) ルータビューの実装: <テンプレート> <コンポーネント:is="routeView" /> </テンプレート> <スクリプト> '~/utils.js' から utils をインポートします エクスポートデフォルト{ データ () { 戻る { ルートビュー: null } }, 作成された(){ this.boundPopState = this.onPopState.bind(this) }, マウント前() { this.$root.$on('popstate', this.boundPopState) }, 破棄する前に() { this.$root.$off('popstate', this.boundPopState) }, メソッド: { onPopState (e) { 定数パス = utils.extractUrlPath(window.location.href) this.routeView = this.$root.$routes[path] || null console.log('[Vue] popstate:', パス) } } } </スクリプト> ルータリンクの実装: <テンプレート> <a @click.prevent="onClick" href=''><スロット></スロット></a> </テンプレート> <スクリプト> エクスポートデフォルト{ 小道具: { から: 文字列 }, メソッド: { クリック時(){ history.pushState(null, '', this.to) this.$root.$emit('popstate') } } } </スクリプト> まとめフロントエンド ルーティングのコア実装原理は非常にシンプルですが、特定のフレームワークと組み合わせると、フレームワークによって動的ルーティング、ルーティング パラメーター、ルーティング アニメーションなどの多くの機能が追加され、ルーティングの実装が複雑になります。この記事は、フロントエンドルーティングのコア部分の実装のみを分析し、ハッシュモードと履歴モードに基づくネイティブ JS/React/Vue の 3 つの実装、合計 6 つの実装バージョンを参考として提供します。お役に立てば幸いです。 すべての例は Github リポジトリで入手できます: https://github.com/whinc/web-router-principle 参照するシングルページルーティングのいくつかの実装原則の詳細な説明 シングルページアプリケーションルーティングの実装原則: React-Routerを例に これで、React でブラウザの自動更新を実装するためのサンプルコードに関するこの記事は終了です。React ブラウザの自動更新に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: VMware12.0 インストール Ubuntu14.04 LTS チュートリアル
>>: SSDストレージを有効にしたMySQLインスタンスの詳細な説明
データベースを作成する右クリック - 新しいデータベースを作成ライブラリ名を入力し、文字セットと並べ...
まず、コンテナが稼働している必要がありますコンテナのCONTAINER IDは、sudo docke...
目次1. サブルート構文2. 例1. 2つの新しいコンポーネントを作成し、その内容を変更する2. ル...
ブロガーは 1 ~ 2 か月間 MySQL を使用していませんでしたが、今日この問題に遭遇しました。...
最近、Zabbix データベースを MySQL 5.6 から 5.7 にアップグレードしたときに、マ...
フレックスレイアウトFlex は Flexible Box の略で、「柔軟なレイアウト」を意味します...
目次背景制限の最適化最適化方法1. カバーインデックスを使用する2. サブクエリの最適化3. 遅延連...
知識への依存Go クロスコンパイルの基礎Dockerの基礎Dockerfileカスタムイメージの基本...
1. アイデアMySQL に 1,000,000 件のレコードを挿入するのにたった 6 秒しかかかり...
バージョン1.4.2公式ドキュメントドッカーハブ起動する環境変数SEATA_CONFIG_NAMEを...
前提条件: Mac、zsh がインストールされ、bash のときに mysql がダウンロードされ、...
異なるサーブレット パスを構成するときに、次の 2 つのエラーが発生しました。 java.lang....
HTML画像にハイパーリンクを追加すると醜い青い枠線が表示される次のように:解決: CSS スタイル...
IPSec の概要IPSec (インターネット プロトコル セキュリティ): ネットワーク層と適用さ...
TensorFlow をディープラーニングに使うとビデオメモリ不足がよく起こるので、GPU 使用状況...