UNPKG

6.03 kBMarkdownView Raw
1# Server-side rendering
2
3Marko 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
6var template = require("./template"); // Import ./template.marko
7
8module.exports = function(req, res) {
9 res.setHeader("Content-Type", "text/html; charset=utf-8");
10 template.render({ name: "Frank" }, res);
11};
12```
13
14Marko can also provide you with a `Readable` stream.
15
16```js
17var template = require("./template"); // Import ./template.marko
18
19module.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> - [hapi](./hapi.md)
29> - [koa](./koa.md)
30> - [huncwot](./huncwot.md)
31
32## UI Bootstrapping
33
34When 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.
35
36Marko 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.
37
38## Bootstrapping Components
39
40When 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.
41
42### Bootstrapping: Lasso
43
44If 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:
45
46_routes/index/template.marko_
47
48```marko
49<!DOCTYPE html>
50<html lang="en">
51 <head>
52 <meta charset="UTF-8">
53 <title>Marko + Lasso</title>
54
55 <!-- CSS includes -->
56 <lasso-head/>
57 </head>
58 <body>
59 <!-- Top-level UI component: -->
60 <app/>
61
62 <!-- JS includes -->
63 <lasso-body/>
64 </body>
65</html>
66```
67
68> **ProTip:** We have provided some sample apps to help you get started with Marko + Lasso
69>
70> - [marko-lasso](https://github.com/marko-js-samples/marko-lasso)
71> - [ui-components-playground](https://github.com/marko-js-samples/ui-components-playground)
72
73### Bootstrapping: Non-Lasso
74
75If 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:
76
771. Load/import/require all of the UI components that were rendered on the server (loading the top-level UI component is typically sufficient)
782. Call `require('marko/components').init()`
79
80For example, if `client.js` is the entry point for your client-side application:
81
82_routes/index/client.js_
83
84```js
85// Load the top-level UI component:
86require("./components/app/index");
87
88// Now that all of the JavaScript modules for the UI component have been
89// loaded and registered we can tell marko to bootstrap/initialize the app
90
91// Initialize and mount all of the server-rendered UI components:
92require("marko/components").init();
93```
94
95> **ProTip:** We have provided some sample apps to help you get started:
96>
97> - [marko-webpack](https://github.com/marko-js-samples/marko-webpack)
98> - [marko-browserify](https://github.com/marko-js-samples/marko-browserify)
99> - [marko-rollup](https://github.com/marko-js-samples/marko-rollup)
100
101# Serialization
102
103For 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:
104
105```javascript
106class {
107 onInput() {
108 // Do not serialize any input:
109 this.input = null;
110
111 // Serialize a new object instead of the provided input:
112 this.input = {
113 foo: 'bar'
114 };
115 }
116}
117```
118
119> NOTE: Marko does allow cycles in serialized objects and Duplicate objects will only be serialized once
120
121# Caveats
122
123There are some caveats associated with rendering a page on the server:
124
125- The UI component data for top-level UI components must be serializable:
126 - Only simple objects, numbers, strings, booleans, arrays and `Date` objects are serializable
127 - Functions are not serializable
128- Care should be taken to avoid having Marko serialize too much data
129- None of the data in `out.global` is serialized by default, but this can be changed as shown below
130
131## Serializing globals
132
133If 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:
134
135```js
136template.render(
137 {
138 $global: {
139 serializedGlobals: {
140 apiKey: true,
141 locale: true
142 }
143 }
144 },
145 res
146);
147```