marko 5.21.2 → 5.21.5
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +6 -6
- package/bin/markoc.js +13 -1
- package/dist/core-tags/core/await/reorderer-renderer.js +26 -6
- package/dist/runtime/RenderResult.js +7 -7
- package/dist/runtime/components/Component.js +129 -129
- package/dist/runtime/components/ComponentDef.js +25 -25
- package/dist/runtime/components/ComponentsContext.js +8 -8
- package/dist/runtime/components/GlobalComponentsContext.js +4 -4
- package/dist/runtime/components/KeySequence.js +3 -3
- package/dist/runtime/components/ServerComponent.js +13 -13
- package/dist/runtime/components/State.js +25 -25
- package/dist/runtime/components/attach-detach.js +8 -8
- package/dist/runtime/components/beginComponent/index-browser.js +2 -2
- package/dist/runtime/components/beginComponent/index.js +7 -7
- package/dist/runtime/components/defineComponent.js +5 -5
- package/dist/runtime/components/dom-data.js +3 -3
- package/dist/runtime/components/endComponent/index.js +2 -2
- package/dist/runtime/components/entry/index-browser.js +4 -4
- package/dist/runtime/components/entry/index.js +15 -15
- package/dist/runtime/components/event-delegation.js +10 -10
- package/dist/runtime/components/init-components/index-browser.js +34 -34
- package/dist/runtime/components/registry/index-browser.js +7 -7
- package/dist/runtime/components/registry/index.js +2 -2
- package/dist/runtime/components/renderer.js +26 -26
- package/dist/runtime/components/update-manager.js +10 -10
- package/dist/runtime/components/util/index-browser.js +17 -17
- package/dist/runtime/components/util/index.js +9 -9
- package/dist/runtime/createOut.js +1 -1
- package/dist/runtime/dom-insert.js +5 -5
- package/dist/runtime/helpers/_change-case.js +2 -2
- package/dist/runtime/helpers/dynamic-tag.js +7 -7
- package/dist/runtime/helpers/style-value.js +1 -1
- package/dist/runtime/html/AsyncStream.js +20 -20
- package/dist/runtime/html/BufferedWriter.js +2 -2
- package/dist/runtime/html/StringWriter.js +1 -1
- package/dist/runtime/html/helpers/attr.js +1 -1
- package/dist/runtime/html/helpers/data-marko.js +4 -4
- package/dist/runtime/html/helpers/escape-quotes.js +1 -1
- package/dist/runtime/html/helpers/escape-xml.js +1 -1
- package/dist/runtime/html/helpers/props-script.js +1 -1
- package/dist/runtime/html/index.js +1 -1
- package/dist/runtime/renderable.js +4 -4
- package/dist/runtime/setImmediate/index-browser.js +1 -1
- package/dist/runtime/setImmediate/index-worker.js +2 -2
- package/dist/runtime/setImmediate/index.js +2 -2
- package/dist/runtime/vdom/AsyncVDOMBuilder.js +28 -28
- package/dist/runtime/vdom/VDocumentFragment.js +1 -1
- package/dist/runtime/vdom/VElement.js +11 -11
- package/dist/runtime/vdom/VFragment.js +3 -3
- package/dist/runtime/vdom/VNode.js +4 -4
- package/dist/runtime/vdom/hot-reload.js +15 -15
- package/dist/runtime/vdom/index.js +1 -1
- package/dist/runtime/vdom/morphdom/fragment.js +2 -2
- package/dist/runtime/vdom/morphdom/helpers.js +4 -4
- package/dist/runtime/vdom/morphdom/index.js +28 -28
- package/dist/runtime/vdom/morphdom/specialElHandlers.js +1 -1
- package/docs/10-awesome-marko-features.md +3 -4
- package/docs/class-components.md +5 -5
- package/docs/cloudflare-workers.md +4 -6
- package/docs/compiler.md +13 -2
- package/docs/core-tags.md +9 -1
- package/docs/express.md +2 -2
- package/docs/http.md +2 -2
- package/docs/koa.md +2 -2
- package/docs/marko-5-upgrade.md +3 -4
- package/docs/marko-vs-react.md +7 -7
- package/docs/redux.md +6 -6
- package/docs/rendering.md +48 -41
- package/docs/troubleshooting-streaming.md +5 -5
- package/docs/webpack.md +9 -9
- package/package.json +3 -3
- package/src/core-tags/core/await/reorderer-renderer.js +27 -7
- package/docs/server-side-rendering.md +0 -144
package/docs/rendering.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Rendering
|
2
2
|
|
3
|
-
To render a Marko view, you need to `
|
3
|
+
To render a Marko view, you need to `import` it.
|
4
4
|
|
5
5
|
_example.js_
|
6
6
|
|
7
7
|
```js
|
8
|
-
|
8
|
+
import FancyButton from "./components/fancy-button.marko";
|
9
9
|
```
|
10
10
|
|
11
11
|
> **Note:** If you are targeting node.js, you will need to enable the [require extension](./installing.md#require-marko-views) in order to require `.marko` files or you will need to precompile all of your templates using [Marko CLI](https://github.com/marko-js/cli). If you are targeting the browser, you will need to use a bundler like [`lasso`](./lasso.md), [`webpack`](./webpack.md) or [`rollup`](./rollup.md).
|
@@ -15,13 +15,13 @@ Once you have a view, you can pass input data and render it:
|
|
15
15
|
_example.js_
|
16
16
|
|
17
17
|
```js
|
18
|
-
|
19
|
-
|
18
|
+
import FancyButton from "./components/fancy-button.marko";
|
19
|
+
const html = FancyButton.renderToString({ label: "Click me!" });
|
20
20
|
|
21
21
|
console.log(html);
|
22
22
|
```
|
23
23
|
|
24
|
-
The
|
24
|
+
The data passed to `renderToString` becomes available as `input` in the component, so if `fancy-button.marko` looked like this:
|
25
25
|
|
26
26
|
_./components/fancy-button.marko_
|
27
27
|
|
@@ -51,8 +51,8 @@ Many of these methods return a [`RenderResult`](#renderresult) which is an objec
|
|
51
51
|
Using `renderSync` forces the render to complete synchronously. If a tag attempts to run asynchronously, an error will be thrown.
|
52
52
|
|
53
53
|
```js
|
54
|
-
|
55
|
-
var result =
|
54
|
+
import View from "./view.marko";
|
55
|
+
var result = View.renderSync({});
|
56
56
|
|
57
57
|
result.appendTo(document.body);
|
58
58
|
```
|
@@ -67,8 +67,8 @@ result.appendTo(document.body);
|
|
67
67
|
The `render` method returns an async `out` which is used to generate HTML on the server or a virtual DOM in the browser. In either case, the async `out` has a `then` method that follows the Promises/A+ spec, so it can be used as if it were a Promise. This promise resolves to a [`RenderResult`](#renderresult).
|
68
68
|
|
69
69
|
```js
|
70
|
-
|
71
|
-
var resultPromise =
|
70
|
+
import View from "./view.marko";
|
71
|
+
var resultPromise = View.render({});
|
72
72
|
|
73
73
|
resultPromise.then(result => {
|
74
74
|
result.appendTo(document.body);
|
@@ -85,9 +85,9 @@ resultPromise.then(result => {
|
|
85
85
|
| return value | `AsyncStream`/`AsyncVDOMBuilder` | the async `out` render target |
|
86
86
|
|
87
87
|
```js
|
88
|
-
|
88
|
+
import View from "./view.marko";
|
89
89
|
|
90
|
-
|
90
|
+
View.render({}, (err, result) => {
|
91
91
|
result.appendTo(document.body);
|
92
92
|
});
|
93
93
|
```
|
@@ -103,12 +103,12 @@ view.render({}, (err, result) => {
|
|
103
103
|
The HTML output is written to the passed `stream`.
|
104
104
|
|
105
105
|
```js
|
106
|
-
|
107
|
-
|
106
|
+
import http from "http";
|
107
|
+
import View from "./view.marko";
|
108
108
|
|
109
109
|
http.createServer((req, res) => {
|
110
110
|
res.setHeader("content-type", "text/html");
|
111
|
-
|
111
|
+
View.render({}, res);
|
112
112
|
});
|
113
113
|
```
|
114
114
|
|
@@ -123,10 +123,10 @@ http.createServer((req, res) => {
|
|
123
123
|
The `render` method also allows passing an existing async `out`. If you do this, `render` will not automatically end the async `out` (this allows rendering a view in the middle of another view). If the async `out` won't be ended by other means, you are responsible for ending it.
|
124
124
|
|
125
125
|
```js
|
126
|
-
|
127
|
-
var out =
|
126
|
+
import View from "./view.marko";
|
127
|
+
var out = View.createOut();
|
128
128
|
|
129
|
-
|
129
|
+
View.render({}, out);
|
130
130
|
|
131
131
|
out.on("finish", () => {
|
132
132
|
console.log(out.getOutput());
|
@@ -145,8 +145,8 @@ out.end();
|
|
145
145
|
Returns an HTML string and forces the render to complete synchronously. If a tag attempts to run asynchronously, an error will be thrown.
|
146
146
|
|
147
147
|
```js
|
148
|
-
|
149
|
-
var html =
|
148
|
+
import View from "./view.marko";
|
149
|
+
var html = View.renderToString({});
|
150
150
|
|
151
151
|
document.body.innerHTML = html;
|
152
152
|
```
|
@@ -162,25 +162,27 @@ document.body.innerHTML = html;
|
|
162
162
|
An HTML string is passed to the callback.
|
163
163
|
|
164
164
|
```js
|
165
|
-
|
165
|
+
import View from "./view.marko";
|
166
166
|
|
167
|
-
|
167
|
+
View.renderToString({}, (err, html) => {
|
168
168
|
document.body.innerHTML = html;
|
169
169
|
});
|
170
170
|
```
|
171
171
|
|
172
172
|
### `stream(input)`
|
173
173
|
|
174
|
-
The `stream` method returns a
|
174
|
+
The `stream` method returns a Node.js-style stream of the output HTML.
|
175
175
|
|
176
176
|
```js
|
177
|
-
|
178
|
-
|
179
|
-
|
177
|
+
import fs from "fs";
|
178
|
+
import View from "./view.marko";
|
179
|
+
const writeStream = fs.createWriteStream("output.html");
|
180
180
|
|
181
|
-
|
181
|
+
View.stream({}).pipe(writeStream);
|
182
182
|
```
|
183
183
|
|
184
|
+
This method is available on the server, but not available by default in the browser. If you need to use streams in the browser, you may `import 'marko/stream'` as part of your client-side bundle.
|
185
|
+
|
184
186
|
## RenderResult
|
185
187
|
|
186
188
|
### `getComponent()`
|
@@ -207,35 +209,42 @@ view.stream({}).pipe(writeStream);
|
|
207
209
|
|
208
210
|
## Global data
|
209
211
|
|
210
|
-
If you need to make data available
|
212
|
+
If you need to make data available to all rendered views, use the `$global` property on the input data object. This property will be removed from `input` and merged into the `out.global` property.
|
213
|
+
|
214
|
+
Global values persist across renders.
|
211
215
|
|
212
216
|
```js
|
213
|
-
|
217
|
+
View.render({
|
214
218
|
$global: {
|
215
219
|
flags: ["mobile"]
|
216
220
|
}
|
217
221
|
});
|
218
222
|
```
|
219
223
|
|
220
|
-
|
221
|
-
|
224
|
+
> **Warning:** Use `$global` with caution; it is visible in any component.
|
225
|
+
|
226
|
+
### Sending global data to browsers
|
227
|
+
|
228
|
+
⚠️ To prevent accidentally exposing sensitive data, by default **no keys** in `out.global` are sent to browsers. To serialize data to the frontend, name the desired properties in `$global.serializedGlobals`.
|
229
|
+
|
230
|
+
Values must be serializable by [the `warp10` module](https://www.npmjs.com/package/warp10).
|
222
231
|
|
223
232
|
```js
|
233
|
+
import Page from "./index.marko";
|
234
|
+
|
224
235
|
app.get("/", (req, res) => {
|
225
236
|
const ua = req.get("User-Agent");
|
226
|
-
const isIos = !!ua.match(/iPad|iPhone/);
|
227
|
-
const isAndroid = !!ua.match(/Android/);
|
228
237
|
|
229
|
-
|
238
|
+
Page.render(
|
230
239
|
{
|
231
240
|
$global: {
|
232
|
-
isIos, //
|
233
|
-
isAndroid, //
|
234
|
-
req, //
|
241
|
+
isIos: /iPad|iPhone/.test(ua), // Serialized and available on the server and browser as `out.global.isIos`
|
242
|
+
isAndroid: /Android/.test(ua), // Serialized and available on the server and browser as `out.global.isAndroid`
|
243
|
+
req, // Only available server-side and not serialized, because it’s not in `serializedGlobals`
|
235
244
|
|
236
245
|
serializedGlobals: {
|
237
|
-
isIos: true, // Tell
|
238
|
-
isAndroid: true // Tell
|
246
|
+
isIos: true, // Tell Marko to serialize `isIos`
|
247
|
+
isAndroid: true // Tell Marko to serialize `isAndroid`
|
239
248
|
}
|
240
249
|
}
|
241
250
|
},
|
@@ -244,6 +253,4 @@ app.get("/", (req, res) => {
|
|
244
253
|
});
|
245
254
|
```
|
246
255
|
|
247
|
-
|
248
|
-
|
249
|
-
Check [this PR](https://github.com/marko-js/marko/pull/672) for more details.
|
256
|
+
For details, check [#672: “Serialize only input and state on top-level server-rendered UI components”](https://github.com/marko-js/marko/pull/672).
|
@@ -35,7 +35,7 @@ Content Delivery Networks (CDNs) consider efficient streaming one of their best
|
|
35
35
|
|
36
36
|
- Some [Akamai features designed to mitigate slow backends can ironically slow down fast chunked responses](https://community.akamai.com/customers/s/question/0D50f00006n975d/enabling-chunked-transfer-encoding-responses). Try toggling off Adaptive Acceleration, Ion, mPulse, Prefetch, and/or similar performance features. Also check for the following in the configuration:
|
37
37
|
|
38
|
-
```
|
38
|
+
```xml
|
39
39
|
<network:http.buffer-response-v2>off</network:http.buffer-response-v2>
|
40
40
|
```
|
41
41
|
|
@@ -44,15 +44,15 @@ Content Delivery Networks (CDNs) consider efficient streaming one of their best
|
|
44
44
|
For extreme cases where [Node streams very small HTML chunks with its built-in compression modules](https://github.com/marko-js/marko/pull/1641), you may need to tweak the compressor stream settings. Here’s an example with `createGzip` and its `Z_PARTIAL_FLUSH` flag:
|
45
45
|
|
46
46
|
```js
|
47
|
-
|
48
|
-
|
47
|
+
import http from "http";
|
48
|
+
import zlib from "zlib";
|
49
49
|
|
50
|
-
|
50
|
+
import MarkoTemplate from "./something.marko";
|
51
51
|
|
52
52
|
http
|
53
53
|
.createServer(function (request, response) {
|
54
54
|
response.writeHead(200, { "content-type": "text/html;charset=utf-8" });
|
55
|
-
const templateStream =
|
55
|
+
const templateStream = MarkoTemplate.stream({});
|
56
56
|
const gzipStream = zlib.createGzip({
|
57
57
|
flush: zlib.constants.Z_PARTIAL_FLUSH
|
58
58
|
});
|
package/docs/webpack.md
CHANGED
@@ -116,21 +116,22 @@ export default {
|
|
116
116
|
},
|
117
117
|
```
|
118
118
|
|
119
|
-
## Multiple client
|
119
|
+
## Multiple client-side compilers
|
120
120
|
|
121
|
-
Sometimes you need
|
121
|
+
Sometimes you need multiple compilers for your client-side bundles. For example, with [`i18n`](https://github.com/webpack/webpack/tree/master/examples/i18n) or [even shipping dynamic runtime bundles to the browser](https://github.com/eBay/arc/tree/master/packages/arc-webpack).
|
122
122
|
|
123
|
-
The
|
123
|
+
The `@marko/webpack` plugin’s `.browser` property can be passed to multiple Webpack compilers. While rendering at runtime, you can provide a `$global.buildName` property to choose which assets from the Webpack compiler are included in the page.
|
124
124
|
|
125
|
-
For example with the
|
125
|
+
For example, with the Webpack internationalization plugin, you might have a config like the following:
|
126
126
|
|
127
127
|
```js
|
128
128
|
import MarkoPlugin from "@marko/webpack/plugin";
|
129
129
|
import I18nPlugin from "i18n-webpack-plugin";
|
130
|
+
import germanTranslations from "./de.json";
|
130
131
|
|
131
132
|
const languages = {
|
132
133
|
en: null,
|
133
|
-
de:
|
134
|
+
de: germanTranslations
|
134
135
|
};
|
135
136
|
|
136
137
|
const markoPlugin = new MarkoPlugin();
|
@@ -167,20 +168,19 @@ export default [
|
|
167
168
|
];
|
168
169
|
```
|
169
170
|
|
170
|
-
With the above config you can render your top
|
171
|
+
With the above config, you can render your top-level Marko template server-side with a `$global.buildName` like so:
|
171
172
|
|
172
173
|
```javascript
|
173
174
|
template.render({ $global: { buildName: "Browser-de" } });
|
174
175
|
```
|
175
176
|
|
176
|
-
|
177
|
-
Of course in this case you'll want to conditionally send the appropriate assets given a users locale. This can be some simply, like so:
|
177
|
+
That will automatically send German assets. However, what you _probably_ want instead of always serving German is conditionally sending appropriate assets for a user’s locale. This can be done like so:
|
178
178
|
|
179
179
|
```javascript
|
180
180
|
template.render({ $global: { buildName: `Browser-${req.language}` } });
|
181
181
|
```
|
182
182
|
|
183
|
-
Note
|
183
|
+
**Note:** If a bundle with the provided `buildName` does not exist, an error is thrown.
|
184
184
|
|
185
185
|
## Multiple copies of Marko
|
186
186
|
|
package/package.json
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "marko",
|
3
|
-
"version": "5.21.
|
3
|
+
"version": "5.21.5",
|
4
4
|
"license": "MIT",
|
5
5
|
"description": "UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.",
|
6
6
|
"dependencies": {
|
7
|
-
"@marko/compiler": "^5.
|
8
|
-
"@marko/translator-default": "^5.21.
|
7
|
+
"@marko/compiler": "^5.22.2",
|
8
|
+
"@marko/translator-default": "^5.21.3",
|
9
9
|
"app-module-path": "^2.2.0",
|
10
10
|
"argly": "^1.2.0",
|
11
11
|
"browser-refresh-client": "1.1.4",
|
@@ -1,5 +1,8 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
|
+
var escapeDoubleQuotes =
|
4
|
+
require("../../../runtime/html/helpers/escape-quotes").___escapeDoubleQuotes;
|
5
|
+
|
3
6
|
module.exports = function (input, out) {
|
4
7
|
// We cannot call beginSync() when using renderSync(). In this case we will
|
5
8
|
// ignore the await-reorderer tag.
|
@@ -54,13 +57,30 @@ module.exports = function (input, out) {
|
|
54
57
|
global._afRuntime = true;
|
55
58
|
}
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
if (global.cspNonce) {
|
61
|
+
asyncOut.write(
|
62
|
+
'<style nonce="' +
|
63
|
+
escapeDoubleQuotes(global.cspNonce) +
|
64
|
+
'">' +
|
65
|
+
"#af" +
|
66
|
+
awaitInfo.id +
|
67
|
+
"{display:none;}" +
|
68
|
+
"</style>" +
|
69
|
+
'<div id="af' +
|
70
|
+
awaitInfo.id +
|
71
|
+
'">' +
|
72
|
+
result.toString() +
|
73
|
+
"</div>"
|
74
|
+
);
|
75
|
+
} else {
|
76
|
+
asyncOut.write(
|
77
|
+
'<div id="af' +
|
78
|
+
awaitInfo.id +
|
79
|
+
'" style="display:none">' +
|
80
|
+
result.toString() +
|
81
|
+
"</div>"
|
82
|
+
);
|
83
|
+
}
|
64
84
|
|
65
85
|
asyncOut.script(
|
66
86
|
"$af(" +
|
@@ -1,144 +0,0 @@
|
|
1
|
-
# Server-side rendering
|
2
|
-
|
3
|
-
Marko allows any Marko template/UI component to be rendered on the server or in the browser. A page can be rendered to a `Writable` stream such as an HTTP response stream as shown below:
|
4
|
-
|
5
|
-
```js
|
6
|
-
var template = require("./template"); // Import ./template.marko
|
7
|
-
|
8
|
-
module.exports = function (req, res) {
|
9
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
10
|
-
template.render({ name: "Frank" }, res);
|
11
|
-
};
|
12
|
-
```
|
13
|
-
|
14
|
-
Marko can also provide you with a `Readable` stream.
|
15
|
-
|
16
|
-
```js
|
17
|
-
var template = require("./template"); // Import ./template.marko
|
18
|
-
|
19
|
-
module.exports = function (req) {
|
20
|
-
// Return a Readable stream for someone to do something with:
|
21
|
-
return template.stream({ name: "Frank" });
|
22
|
-
};
|
23
|
-
```
|
24
|
-
|
25
|
-
> **ProTip:** Marko also provides server-side framework integrations:
|
26
|
-
>
|
27
|
-
> - [express](./express.md)
|
28
|
-
> - [koa](./koa.md)
|
29
|
-
> - [fastify](./fastify.md)
|
30
|
-
|
31
|
-
## UI Bootstrapping
|
32
|
-
|
33
|
-
When a page is rendered on the server, additional code is added to the output HTML to allow the UI to instantly boot in the browser. This additional code allows UI components rendered on the server to be mounted in the browser automatically. For each _top-level_ UI component, Marko will serialize the component's data (including `input` and `state` and any properties added to the UI component instance) so that each top-level UI component can be re-rendered and mounted when the page loads in the browser. Only a "partial" re-render is done for each top-level UI component. That is, when doing the partial re-render in the browser, the DOM is not updated and no virtual DOM is actually produced.
|
34
|
-
|
35
|
-
Marko encodes required information into attributes of rendered HTML elements and it also generates `<script>` tags that will cause UI components to be mounted. The code inside the `<script>` simply registers UI components and when the Marko runtime finally loads, all of the registered UI components will then be mounted. This allows the Marko runtime to be loaded at anytime without causing JavaScript errors.
|
36
|
-
|
37
|
-
## Bootstrapping Components
|
38
|
-
|
39
|
-
When a server-rendered page loads in the browser it's possible for marko to automatically detect UI components rendered on the server and create and mount them with the correct `state` and `input` in the browser.
|
40
|
-
|
41
|
-
### Bootstrapping: Lasso
|
42
|
-
|
43
|
-
If you are using [Lasso.js](https://github.com/lasso-js/lasso) then the bootstrapping will happen automatically as long as the JavaScript bundles for your page are included via the `<lasso-body>` tag. A typical HTML page structure will be the following:
|
44
|
-
|
45
|
-
_routes/index/template.marko_
|
46
|
-
|
47
|
-
```marko
|
48
|
-
<!DOCTYPE html>
|
49
|
-
<html lang="en">
|
50
|
-
<head>
|
51
|
-
<meta charset="UTF-8">
|
52
|
-
<title>Marko + Lasso</title>
|
53
|
-
|
54
|
-
<!-- CSS includes -->
|
55
|
-
<lasso-head/>
|
56
|
-
</head>
|
57
|
-
<body>
|
58
|
-
<!-- Top-level UI component: -->
|
59
|
-
<app/>
|
60
|
-
|
61
|
-
<!-- JS includes -->
|
62
|
-
<lasso-body/>
|
63
|
-
</body>
|
64
|
-
</html>
|
65
|
-
```
|
66
|
-
|
67
|
-
> **ProTip:** We have provided some sample apps to help you get started with Marko + Lasso
|
68
|
-
>
|
69
|
-
> - [marko-lasso](https://github.com/marko-js/examples/tree/master/examples/lasso-express)
|
70
|
-
> - [ui-components-playground](https://github.com/marko-js/examples/tree/master/examples/ui-components-playground)
|
71
|
-
|
72
|
-
### Bootstrapping: Non-Lasso
|
73
|
-
|
74
|
-
If a JavaScript module bundler other than Lasso is being used then you will need to add some client-side code to bootstrap your application in the browser by doing the following:
|
75
|
-
|
76
|
-
1. Load/import/require all of the UI components that were rendered on the server (loading the top-level UI component is typically sufficient)
|
77
|
-
2. Call `require('marko/components').init()`
|
78
|
-
|
79
|
-
For example, if `client.js` is the entry point for your client-side application:
|
80
|
-
|
81
|
-
_routes/index/client.js_
|
82
|
-
|
83
|
-
```js
|
84
|
-
// Load the top-level UI component:
|
85
|
-
require("./components/app/index");
|
86
|
-
|
87
|
-
// Now that all of the JavaScript modules for the UI component have been
|
88
|
-
// loaded and registered we can tell marko to bootstrap/initialize the app
|
89
|
-
|
90
|
-
// Initialize and mount all of the server-rendered UI components:
|
91
|
-
require("marko/components").init();
|
92
|
-
```
|
93
|
-
|
94
|
-
> **ProTip:** We have provided some sample apps to help you get started:
|
95
|
-
>
|
96
|
-
> - [marko-webpack](https://github.com/marko-js/examples/tree/master/examples/webpack-express)
|
97
|
-
|
98
|
-
# Serialization
|
99
|
-
|
100
|
-
For each _top-level_ UI component, Marko will serialize the component's data (including `input` and `state` and any properties added to the UI component instance) down to the browser. You can control which data gets serialized by implementing [`toJSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) or by reassigning `this.input` in the UI component's `onInput(input, out)` lifecycle method as shown below:
|
101
|
-
|
102
|
-
```javascript
|
103
|
-
class {
|
104
|
-
onInput() {
|
105
|
-
// Do not serialize any input:
|
106
|
-
this.input = null;
|
107
|
-
|
108
|
-
// Serialize a new object instead of the provided input:
|
109
|
-
this.input = {
|
110
|
-
foo: 'bar'
|
111
|
-
};
|
112
|
-
}
|
113
|
-
}
|
114
|
-
```
|
115
|
-
|
116
|
-
> NOTE: Marko does allow cycles in serialized objects and Duplicate objects will only be serialized once
|
117
|
-
|
118
|
-
# Caveats
|
119
|
-
|
120
|
-
There are some caveats associated with rendering a page on the server:
|
121
|
-
|
122
|
-
- The UI component data for top-level UI components must be serializable:
|
123
|
-
- Only simple objects, numbers, strings, booleans, arrays and `Date` objects are serializable
|
124
|
-
- Functions are not serializable
|
125
|
-
- Care should be taken to avoid having Marko serialize too much data
|
126
|
-
- None of the data in `out.global` is serialized by default, but this can be changed as shown below
|
127
|
-
|
128
|
-
## Serializing globals
|
129
|
-
|
130
|
-
If there are specific properties on the `out.global` object that need to be serialized then they must be whitelisted when the top-level page is rendered on the server. For example, to have the `out.global.apiKey` and the `out.global.locale` properties serialized you would do the following:
|
131
|
-
|
132
|
-
```js
|
133
|
-
template.render(
|
134
|
-
{
|
135
|
-
$global: {
|
136
|
-
serializedGlobals: {
|
137
|
-
apiKey: true,
|
138
|
-
locale: true
|
139
|
-
}
|
140
|
-
}
|
141
|
-
},
|
142
|
-
res
|
143
|
-
);
|
144
|
-
```
|