UNPKG

12.4 kBMarkdownView Raw
1<div align="center">
2
3![yrv](Japan_road_sign_201-D.svg)
4
5![Build status](https://github.com/pateketrueke/yrv/workflows/build/badge.svg)
6[![NPM version](https://img.shields.io/npm/v/yrv)](https://www.npmjs.com/package/yrv)
7[![Known Vulnerabilities](https://snyk.io/test/npm/yrv/badge.svg)](https://snyk.io/test/npm/yrv)
8[![donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8MXLRJ7QQXGYY)
9
10</div>
11
12> The `v` is for Svelte
13
14Built on top of [abstract-nested-router](https://www.npmjs.com/package/abstract-nested-router), so you can use nested routers, also:
15
16- Advanced parameters can be used, e.g. `/:id<\d+>` &mdash; [see docs](https://www.npmjs.com/package/abstract-nested-router#params)
17- ARIA-compliant, sets `[aria-current="page"]` on active links
18- Seamless `<base href="..." />` integration
19- Conditionals and redirection through props
20- Fallback `<Route />` handlers
21- Hash and URI-based routes
22- Support for [query-string](https://www.npmjs.com/package/query-string)
23- [REPL ready!](https://svelte.dev/repl/0f07c6134b16432591a9a3a0095a80de?version=3.12.1)
24
25> `yrv` will use any _base-href_ found on the current page to rewrite links and routes.
26
27## Usage
28
29Install `yrv` through NPM or Yarn:
30
31```html
32<script>
33 import { Router, Route, Link } from 'yrv';
34</script>
35
36<Link href="/">Home</Link>
37| <Link href="/World">Hello</Link>
38| <Link href="/not/found">NotFound</Link>
39
40<p>
41 <Router>
42 <Route exact>Hello World</Route>
43 <Route fallback>Not found</Route>
44 <Route exact path="/:name" let:router>Hey {router.params.name}!</Route>
45 </Router>
46</p>
47```
48
49> Notice `fallback` routes can’t be placed at the beginning, otherwise further routes will not be mounted. :bomb:
50
51## Components
52
53> You MUST declare at least, one top-level `Router` to setup the bindings.
54
55### `<Router {path} {pending} {disabled} {condition} {nofallback} />`
56
57This component will hold any given routes as children, `path` is always derived from parent routes.
58
59Available props:
60
61- `{path}` &mdash; Any segment to derive a fullpath from, defaults to `/`
62- `{pending}` &mdash; Svelte-component or String; top-level `pending` support
63- `{disabled}` &mdash; Boolean; Similar to condition, but for bound props
64- `{condition}` &mdash; Function; if given, render only if evaluates to true
65- `{nofallback}` &mdash; If set, non-matched routes will never raise a failure
66
67> Nested routers does not need the same path to be declared inside, e.g. if the router for `/top` has a `/sub` router inside, inner router will use the route `/top/sub`, (the same as declaring `/top/sub` route outside the parent router).
68
69### `<Route {key} {path} {exact} {pending} {fallback} {component} {disabled} {condition} {redirect} let:router />`
70
71Main container for routing, they can hold any component or children.
72
73Available props:
74
75- `{key}` &mdash; The route identity, not its path; defaults to random pseudo-hash
76- `{path}` &mdash; Any segment to derive a fullpath from, default to `/`
77- `{exact}` &mdash; If set, the route will render only if the route exactly matches
78- `{pending}` &mdash; Svelte-component or String; rendered during the loading of dynamic components
79- `{fallback}` &mdash; If set, the route will render only if no more routes were matched
80- `{component}` &mdash; Accepts either a valid svelte-component, a promise, or a dynamic import function
81- `{disabled}` &mdash; Boolean; Similar to `condition`, but for bound props
82- `{condition}` &mdash; Function; if given, the route will render only if evaluates to `true`
83- `{redirect}` &mdash; Alternate redirection location, only if the previous condition was `true`
84- `let:router` &mdash; Injects the `router` context, it also provides `failure` in case of errors
85
86> If you omit `exact`, then `/x` would match both `/` and `/x` routes &mdash; [see docs](https://www.npmjs.com/package/abstract-nested-router#params)
87
88When `yrv` adds a new route, it'll use any given `key` from its props &mdash; once routes are detached they're also removed from the router registry, due to that, the next time the same route is mounted a new key is generated (if isn't present already).
89
90```html
91<script>
92 import SvelteComponent from 'path/to/svelte-component.svelte';
93</script>
94
95<Link href="/">Home</Link>
96| <Link href="/svelte-component">Svelte component</Link>
97| <Link href="/promise">Promised component</Link>
98| <Link href="/lazy">Lazy component</Link>
99
100<p>
101 <Router>
102 <Route exact>Hello World</Route>
103 <Route exact path="/svelte-component" component={SvelteComponent}/>
104 <Route exact path="/promise" component="{import('path/to/other-component.svelte')}"/>
105 <Route exact path="/lazy" component="{() => import('path/to/another-component.svelte')}"/>
106 </Router>
107</p>
108```
109
110> Behind the scenes, for making dynamic-imports work, the bundler _should_ inline them or just write-out the required chunks to make it work natively (`<script type="module" />`) or through `shimport`, etc.
111
112### `<Link {go} {href} {open} {title} {exact} {reload} {replace} {class} />`
113
114In order to navigate, you can use `Link` components, or regular links, etc.
115
116> All `href` values MUST be absolute, only links starting with `/` or `#` are allowed.
117
118Available props:
119
120- `{go}` &mdash; History shortcut (see below)
121- `{href}` &mdash; New location, default to `/`
122- `{open}` &mdash; Same behavior as `<a target="_blank">`
123- `{title}` &mdash; HTML title-attribute value
124- `{button}` &mdash; If set, will use button-tag instead
125- `{exact}` &mdash; Determine if link should match exactly to be set as active
126- `{reload}` &mdash; Use `location.href` instead
127- `{replace}` &mdash; Use `history.replaceState()` instead
128- `{class}` &mdash; Custom class-name for the mounted anchor
129
130> The value for `open` can be a string including the window specs, e.g. `width=400,height=200` &mdash; a `on:close` event will be fired once the opened window is closed.
131
132Normal `on:click` events are still allowed, so you can use:
133
134```html
135<Link on:click={() => location.reload()}>Reload</Link>
136```
137
138> Active _links_ will gain the `[aria-current]` attribute, and `[disabled]` if they're buttons.
139
140Aditionally, you can setup `go` to move around:
141
142- `"back"` &mdash; String; if given, will invoke `history.back()`
143- `"fwd"` &mdash; String; if given, will invoke `history.fwd()`
144- `n` &mdash; Number; if given, it'll be used to invoke `history.go(n)`
145
146> If navigating through `history` is not possible a normal redirection will run. :anchor:
147
148## Public API
149
150- `navigateTo(path[, options])` &mdash; Change the location, supported options are:
151 - `reload` &mdash; If true, it will use `document.location.href` instead
152 - `replace` &mdash; If true, it will use `history.replaceState()` instead
153 - `params` &mdash; Used to replace `:placeholders` from given path
154 - `queryParams` &mdash; Additional search-params for the new location
155- `$router` &mdash; Store with shared routeInfo details, similar to `let:router`
156
157> `yrv` gracefully degrades to `location.hash` on environments where `history` is not suitable, also it can be forced through `router.hashchange = true`.
158
159### Route Info
160
161Route changes are propagated through stores, if you want to listen too just subscribe, e.g.
162
163```js
164import { router } from 'yrv';
165
166router.subscribe(e => {
167 if (!e.initial) console.log(e);
168});
169```
170
171Using this technique you gain access to the same detail object as `let:router` does.
172
173> Notice the `initial` property is present as soon the store is initialized, consecutive changes will not have it anymore.
174
175### IE11 support
176
177Support for IE11 is _granted_ if you include, at least, the following polyfills before your application:
178
179```html
180<script>if (!!window.MSInputMethodContext && !!document.documentMode)
181 document.write('<script src="https://polyfill.io/v3/polyfill.min.js?features=default,Promise,Object.getOwnPropertyDescriptors"><\/script>');</script>
182<script src="your-app.js"></script>
183```
184
185> `document.write()` is used because conditional comments were dropped in IE10, so this way you can conditionally load polyfills anyway.
186
187Also, you MUST [enable either `buble` or `babel`](https://github.com/sveltejs/svelte/issues/2621) within your build pipeline to transpile down to ES5.
188
189### Frequently Asked Questions
190
191**How to conditionally render a `<Router />` component?**
192
193Both Route/Router components support the `disabled` and `condition` props, but:
194
195- Use `condition` to allow/disallow route-dispatching dynamically
196- Use `disabled` to skip from rendering, it will add/remove the route
197
198This new `disabled` prop would work as you're expecting:
199
200```html
201<Router disabled={!showNavBar}>
202 ...
203</Router>
204```
205
206**What means the `exact` property and how it works?**
207
208Say you have three routes:
209
210- `/a` (exact)
211- `/a/b` (non-exact)
212- `/a/b/c` (exact)
213
214Now, you navigate from `/a` to `/a/b/c`:
215
216- Since `/a` was active, and it was exact, `yrv` clears out the `routeInfo` for that route.
217- Since `/a/b` is not exact, `yrv` activate this route because is half-way to the final route.
218
219> If you plan to have more routes nested, then the route will never be `exact` (at least at top-levels).
220
221This is also true for `<Link />` components &mdash; as soon as they match the `[aria-current]` attribute will be added on them to denote _active_ links.
222
223If the link for `/a` were also `exact`, then it'll be _active_ if the matching route is `/a` only.
224
225**Why `path` can't be an empty string like other routers does?**
226
227Even if browsers treat `http://localhost:8080` and `http://localhost:8080/` as the same thing I wanted to keep paths clear as possible.
228
229Internally `yrv` normalizes any given URI to keep a trailing slash, so `/foo` is `/foo/` for matching purposes.
230
231Also, the default path is usually `/` so there's no point on having to declare anything else:
232
233```html
234<Route>OK</Route>
235<Route path="/">OK</Route>
236```
237
238**What is `routeInfo` and how can I access it outside routes?**
239
240This object is very similar to what you get with `let:router` inside components.
241
242Use the `$router` store to access it, e.g.
243
244```html
245<script>
246 import { router } from 'yrv';
247</script>
248<pre>{JSON.stringify($router, null, 2)}</pre>
249```
250
251**Why does Yrv not work with Parcel or webpack/snowpack?**
252
253If you're getting any of the errors below:
254
255- store.subscribe is not a function
256- Class constructor SvelteComponent cannot be invoked without 'new'
257- 'on_outro' is not exported by [...]
258- 'target' is a required option
259
260Make sure you're using the right settings:
261
2621. Add mainFields into resolve config, e.g. `mainFields: ['svelte', 'browser', 'module', 'main']`
2632. Remove `exclude: /node_modules/` from `svelte-loader` config
264
265> If you're using an online tool that is not the official Svelte REPL the behavior is unexpected and no further support will be granted.
266
267**Can I use hash-based routes _à la_ Gmail? e.g. `index.html#/profile`, `index.html#/book/42`?**
268
269Yes, URIs like that are suitable for embedded apps like Electron, where normal URLs would fail.
270
271Also this mode is the default used on the Svelte REPL, because is not an iframe, nor a regular webpage... it's a weird thing!
272
273> If you enable `router.hashchange = true` all your regular links will be automatically rewritten to hash-based URIs instead, see how it works in our test suite.
274
275**Why I'm getting `<Component> was created with unknown prop 'router'` in the browser's console?**
276
277If you're not using the `router` prop inside your route-components then just add:
278
279```html
280<script>
281 export const router = null;
282</script>
283```
284
285That will remove the warning and also will make `eslint-plugin-svelte3` in your workflow happy.
286
287**Why `router.subscribe` is called two times when I first open the page?**
288
289Any subscription to stores will fire twice as they have an initial value, once the router resolves (e.g. the initial route) then a second event is fired.
290
291> In this case, and additional property `initial` is added to identify such event.
292
293**Is there any method that allows me to detect route change?**
294
295Yes, you can subscribe to the router store, e.g. `router.subscribe(...)` &mdash; [see above](#route-info).
296
297**Is there a way to reduce the bundle size of yrv?**
298
299Since `v0.0.46` you'll be getting the most reduced version we can ship, however it comes without development warnings.
300
301> Consume it as `import { ... } from 'yrv/debug'` right away and you'll get a more complete version with included DEBUG information.