| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A simple but powerful router module
[!CRITICAL] This package requires URLPattern to be polyfilled before any paths are registered. A common polyfill recommended by MDN can be found here.
npm install @aegisjsproject/routerYou do not even need to npm install this or use any build process. You may either import it directly from a CDN or use an importmap.
<script type="importmap">
{
"imports": {
"@aegisjsproject/router": "https://unpkg.com/@aegisjsproject/router[@version]/router.mjs",
"@aegisjsproject/state": "https://unpkg.com/@aegisjsproject/state[@version]/state.mjs"
}
}
</script>At its core, this package matches URLs matching a URLPattern to modules to be dynamically imported. This yields a powerful but minimal package size, dynamic loading of "View"s as-needed, high reusability of code, and potentially nearly instant navigations, especially when used in conjunction with service workers and caches. Just create a script that has a default export that is a Document, DocumentFragment, HTMLElement and especially a custom element or web component, and map the URLPatterns to their respective modules.
import { init, navigate, back, forward, reload } from '@aegisjsproject/router';
init({
'/product/:productId': '@scope/views/product.js',
'/test/': '@scope/views/home.js',
'/search?q=:query': '@scope/views/search.js',
'/img': '/views/img.js',
'/path/page-:page(\\d+)': '@scope/foo.js',
}, {
preload: true, // Preload all registered modules
notFound: './views/404.js', // Set custom 404 module
rootNode: '#root', // Declares element for base of content updates
interceptRoot: document.body, // Use `MutationObserver` to observer `<a>` elements and intercept navigations
signal: controller.signal, // An `AbortSignal`, should you want to disable routing funcitonality
});
document.querySelectorAll('[data-link]').forEach(el => {
el.addEventListener('click', ({ currentTarget }) => {
const { link, ...state } = currentTarget.dataset;
navigate(link, state);
});
});
document.querySelectorAll('[data-nav]').forEach(el => {
el.addEventListener('click', ({ currentTarget }) => {
switch (currentTarget.dataset.nav) {
case 'back':
back();
break;
case 'forward':
forward();
break;
case 'reload':
reload();
default:
throw new TypeError(`Invalid nav button type: ${currentTarget.dataset.nav}.`);
}
});
});At the core, this router module just uses URLPatterns in a map, mapped to a source for a module. When a URL is navigated to, it finds the pattern that the URL matches, dynamically imports that module, and passes the current state and URL and the results of urlPattern.exec(url) to the function or constructor.
You may register paths via either registerPath() or through an object given to the init() function. registerPath() allows for the use of new URLPattern() to be used, but as init() requires an object, its keys must be strings to be converted into URLPattern through new URLPattern(key, moduleSrc).
If you call the init() function, the popstate listener will be added automatically and the module for the current page will be loaded. Should you want more manual loading, you may also call addListener() on your own.
There is also a MutationObserver that adds click event handlers to intercept clicks on same-origin <a>s. This observer watches for <a>s in the children of what it is set to observe and calls event.preventDefault() to avoid the default navigation, then calls navigate(a.href).
Note
While the MutationObserver automatically adds the necessary click handlers on all <a> and <form> elements under its root, it cannot reach into Shadow DOM. For any web component with shadow, you should call interceptNav(shadow) in either the constructor or connectedCallback.
For all "Views"/modules that export a function or constructor, they are given an AbortSignal which is aborted on any navigation. This can and should be used for any necessary cleanup/freeing up memory, such as aborting any pending requests and removing event listeners.
You can register a module for 404 pages using either set404() or by passing it via { notFound } in init(). This component or function will be given the current state and URL and can be dynamically generated.
You can preload modules for views by using preloadModule() or by passing { preload: true } in init(). Preloading modules will make navigation effectively instant and potentially without network traffic, however it will increase initial load times (though it defaults to a low priority).
Important
Be advised that there may be a functional difference between using the router in the context of a <script type="module"> vs as a non-module, namely in the availability of import.meta.resolve() for preloading. Also, that importmaps are not quite univerally supported yet. For best compatibility, you SHOULD use either absolute or relative URLs when declaring modules for routes, though use of module specifiers (e.g. @scope/package) is supported in certain contexts, with decent browser support.
This currently uses @aegisjsproject/state for state mangement. It is a lightweight wrapper around history.state that uses BroadcastChannel to sync state changes between tabs/windows. It should be noted that this is global state and not specific to some component, so please avoid generic names and be aware of the global nature.
| Back | FazBrowse Home | New Git URL |