60 行の JavaScript ルーター
18148 ワード
私は自分で Web アプリケーションを構築していましたが、NPM パッケージと JS フレームワークが大きく複雑になっているため、JS フレームワークをインストールせず、今回はゼロからアプリを構築することにしました.
新しい Web アプリを作成するには、Router がページの変更を処理する必要があります.これは私の試みです.
では、ルーターは Web アプリケーションに対して実際に何をするのでしょうか.
アプリは、開いている URL を読み取り、必要なコンテンツを表示できる必要があります.たとえば、ページ www.mybook.com/user/1 を開くと、ページはユーザー 1 の情報をレンダリングする必要があります. ページは URL の変更をリッスンする必要があるため、ユーザーを www.mybook.com/post/my-latest-news にリダイレクトするボタンまたは画像をクリックすると、ページは更新されず、代わりに古いコンテンツが削除され、新しい必要なコンテンツがレンダリングされます.コンテンツをレンダリングするこの方法は、通常、シングル ページ アプリケーションまたは SPA と呼ばれます. ページには URL 履歴メモリが必要なので、ブラウザで戻るボタンまたは進むボタンを押したときに、アプリケーションは表示するページを認識する必要があります. ルーターがルートを定義し、ユーザーがそのルートに到達したときに何らかのアクションを実行できるようにしたいと考えています.
例えば
また、ルーターが URL のパラメーターを受け入れるようにしたいと考えています.
たとえば、
それが基本だと思います.
ルート変更のリッスンには、popstate listener API を使用します.
URL履歴には、ブラウザHistory APIを使用します
このルーターのコードは Github にあります.
PS!
まず、
ES6 ネイティブ モジュール インポートを使用します.これは非常に簡単で、ほとんどのブラウザーで既にサポートされています.
ファイルからルータークラスを新しいものとして直接エクスポートするか、次のようなことを行うことができます
PS!
ルートを変更するには、以下のコードを使用する必要があります.これは、URL の変更もこの方法でブラウザーの履歴に保存されるためです.
Web 上で SPA アプリを作成する場合、発生する可能性のあるエラーに注意する必要があります.
www.mybook.com/user/1 などの URL のページを読み込もうとすると、通常、バックエンドは
これは、バックエンドが
それを修正するために、バックエンドの 404 ルートを
そのため、ルートが見つからないバックエンド送信の代わりに、SPA アプリのメイン ファイルが送信され、ルートに関する情報があるため、SPA アプリ ルーターは正しいページをレンダリングします.
ローカルでデバッグするために、
このコンソール コマンドは、現在のフォルダーで
本番環境では、バックエンド プロキシとして Caddy を使用しています.
だからここに私がすべての 404 リクエストを Caddy の
新しい Web アプリを作成するには、Router がページの変更を処理する必要があります.これは私の試みです.
では、ルーターは Web アプリケーションに対して実際に何をするのでしょうか.
例えば
router.on("/post/my-latest-news", (params) => {
// In here, I remove old content and render new one
})
たとえば、
"/post/:id"
は、どの投稿を表示するかを決定するときに、パラメーターとして id 値を提供します.それが基本だと思います.
ルート変更のリッスンには、popstate listener API を使用します.
URL履歴には、ブラウザHistory APIを使用します
JavaScript の実装
このルーターのコードは Github にあります.
class Router {
constructor() {
this.routes = new Map();
this.current = [];
// Listen to the route changes, and fire routeUpdate when route change happens.
window.onpopstate = this.routeUpdate.bind(this);
}
// Returns the path in an array, for example URL "/blog/post/1" , will be returned as ["blog", "post", "1"]
get path() {
return window.location.pathname.split('/').filter((x) => x != '');
}
// Returns the pages query parameters as an object, for example "/post/?id=2" will return { id:2 }
get query() {
return Object.fromEntries(new URLSearchParams(window.location.search));
}
routeUpdate() {
// Get path as an array and query parameters as an object
const path = this.path;
const query = this.query;
// When URL has no path, fire the action under "/" listener and return
if (path.length == 0) {
this.routes.get('/')(path);
return;
}
// When same route is already active, don't render it again, may cause harmful loops.
if (this.current.join() === path.join()) return;
// Set active value of current page
this.current = path;
// Here I save the parameters of the URL, for example "/post/:page", will save value of page
let parameters = {};
// Loop though the saved route callbacks, and find the correct action for currect URL change
for (let [route, callback] of this.routes) {
// Split the route action name into array
const routes = route.split('/').filter((x) => x != '');
const matches = routes
.map((url, index) => {
// When the route accepts value as wildcard accept any value
if (url == '*') return true;
// Route has a parameter value, because it uses : lets get that value from the URL
if (url.includes(':')) {
parameters[url.split(':')[1]] = path[index];
return true;
}
// The new URL matches the saved route callback url, return true, meaning the action should be activated.
if (url == path[index]) return true;
return false;
})
.filter((x) => x);
// When the router has found that current URL, is matching the saved route name, fire the callback action with parameters included
if (matches.length == routes.length && routes.length > 0) {
callback({ path, parameters, query });
}
}
}
// Listen for route changes, required route name and the callback function, when route matches.
on(route, callback) {
this.routes.set(route, callback);
}
// Fire this function when you want to change page, for example router.change("/user/1")
// It will also save the route change to history api.
change(route) {
window.history.pushState({ action: 'changeRoute' }, null, route);
window.dispatchEvent(new Event('popstate'));
}
}
export default new Router();
ルーターの使用
PS!
You should also, add
<base href="/">
in the header of your HTML file, so the front-end router, will always start the URL path from the start, and will not keep appending to the URL.
まず、
Router
をインポートします.ES6 ネイティブ モジュール インポートを使用します.これは非常に簡単で、ほとんどのブラウザーで既にサポートされています.
import Router from '/libraries/router.js';
ファイルからルータークラスを新しいものとして直接エクスポートするか、次のようなことを行うことができます
window.router = new Router()
PS!
My personal preference is to create the page as webcomponent or lit.js and then just swap the components when route is active.
Router.on('/home', (event) => {
// Replace and render page content here
});
Router.on('/post/:id', (event) => {
// Replace and render page content here
// You can get parameter with, event.parameters.id
});
ルート変更
ルートを変更するには、以下のコードを使用する必要があります.これは、URL の変更もこの方法でブラウザーの履歴に保存されるためです.
Router.change("/account")
バックエンドのセットアップ
Web 上で SPA アプリを作成する場合、発生する可能性のあるエラーに注意する必要があります.
www.mybook.com/user/1 などの URL のページを読み込もうとすると、通常、バックエンドは
404
エラー、ページが見つかりませんを送信します.これは、バックエンドが
/user/1
のルートを定義していないためです.ルート検索はフロントエンド側で行われる必要があります.それを修正するために、バックエンドの 404 ルートを
index.html
ファイルまたは使用しているファイルにリダイレクトします.そのため、ルートが見つからないバックエンド送信の代わりに、SPA アプリのメイン ファイルが送信され、ルートに関する情報があるため、SPA アプリ ルーターは正しいページをレンダリングします.
バックエンド プロキシに使用するツール
ローカルでデバッグするために、
Node.js
と http-server を使用していますこのコンソール コマンドは、現在のフォルダーで
http-server
を実行し、失敗したすべての要求をメインの index.html
にリダイレクトし、JS ルーターが引き継ぎます.http-server -p 8080 . --proxy http://localhost:8080?
本番環境では、バックエンド プロキシとして Caddy を使用しています.
だからここに私がすべての 404 リクエストを Caddy の
index.html
に送信するコード例があります.try_files
の部分は、失敗したルートがリダイレクトされる場所です.https://www.mybook.com {
root * /srv/www/mybook
try_files {path} /index.html
encode zstd gzip
file_server
}
Reference
この問題について(60 行の JavaScript ルーター), 我々は、より多くの情報をここで見つけました https://dev.to/tonis2/javascript-router-in-60-lines-4n72テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol