You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Mar 20, 2024. It is now read-only.
Currently Preboot suffers from various race conditions:
1. It waits for the `document.body` to be available and then applies its logic.
However, the application root may not be available at this point - see the issue
#72.
2. Even if the application root exists when Preboot starts listening for events
the server node may not be available in full yet. Since Preboot searches for
nodes matching desired selectors inside of the existing `serverNode`, it may
skip some of them if the server node hasn't loaded fully. This is especially
common if the internet connection is slow.
3. Preboot waits for `body` in a tight loop, checking every 10 milliseconds.
This starves the browser but may also be clamped by it to a larger delay
(around 1 second); that's what happens in modern browsers if the tab where the
page loads is inactive which is what happens if you open a link in a new tab in
Chrome or Firefox. This means Preboot may start listening for events after
Angular reports the app is stable and the `PrebootComplete` event fires. This
then leaves the server node active, never transferring to the client one which
makes for a broken site.
To solve it, we're doing the following:
1. Since we want to attach event listeners as soon as possible when the server
node starts being available (so that it can be shallow-cloned) we cannot wait
for it to be available in full. Therefore, we switched to delegated event
handlers on each of the app roots instead of directly on specific nodes.
2. As we support multiple app roots, to not duplicate large inline preboot code,
we've split `getInlinePrebootCode` into two parts: function definitions to be
put in `<head>` and the invocation separate for each app root put just after
the app root opening tag.
3. To maintain children offset numbers (otherwise selectors won't match between
the client & the server) we've removed the in-app-root script immediately when
it starts executing; this won't stop the execution. `document.currentScript` is
a good way to find the currently running script but it doesn't work in IE. A
fallback behavior is applied to IE that leverages the fact that start scripts
are invoked synchronously so the current script is the last one currently in
the DOM.
4. We've removed `waitUntilReady` as there's no reliable way to wait; we'll
invoke the init code immediately instead.
5. We've switched the overlay used for freeing the UI to attach to
`document.documentElement` instead of `document.body` so that we don't have to
wait for `body`.
6. The mentioned overlay is now created for each app root separately to avoid
race conditions.
Fixes#82Fixes#72
BREAKING CHANGES:
1. When used in a non-Angular app, the code needs to be updated to use
`getInlineDefinition` and `getInlineInvocation`; the previously defined
`getInlinePrebootCode` has been removed. The `getInlineDefinition` output
needs to be placed before any content user might interactive with, preferably
in <head>. The `getInlineInvocation` output needs to be put just after the
opening tag of each app root. Only `getInlineDefinition` needs to have options
passed. An example expected (simplified) layout in EJS format:
```html
<html>
<head>
<script><%= getInlineDefinition(options) %></script>
</head>
<body>
<app1-root>
<script><%= getInlineInvocation() %></script>
<h2>App1 header</h2>
<div>content</div>
</app1-root>
<app2-root>
<script><%= getInlineInvocation() %></script>
<h2>App2 header</h2>
<span>content</span>
</app2-root>
</body>
</html>
```
2. The refactor breaks working with frameworks that replace elements
like AngularJS with UI Router as it no longer uses serverSelector and
clientSelector but expects its clientNode to remain in the DOM.
Copy file name to clipboardExpand all lines: README.md
+29-8Lines changed: 29 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -46,24 +46,45 @@ import { AppComponent } from './app.component';
46
46
exportclassAppModule { }
47
47
```
48
48
49
-
The key part here for preboot is to include `PrebootModule.withConfig({ appRoot: 'app-root' })` where the `appRoot`
50
-
is the selector(s) to find the root of your application. The options you can pass into `withConfig()` are in the [PrebootOptions](#PrebootOptions) section below. In most cases, however, you will only need to specify the `appRoot`.
49
+
The key part here for preboot is to include `PrebootModule.withConfig({ appRoot: 'app-root' })` where the `appRoot` is the selector(s) to find the root of your application. The options you can pass into `withConfig()` are in the [PrebootOptions](#PrebootOptions) section below. In most cases, however, you will only need to specify the `appRoot`.
0 commit comments