1 | # Marko vs React: An In-depth Look
|
2 |
|
3 | <a href="https://hackernoon.com/marko-vs-react-an-in-depth-look-767de0a5f9a6">
|
4 | <img src="https://cdn-images-1.medium.com/max/2000/1*4BP6tPQtwImj6_QseeybwQ.png" alt="Marko logo" width="100%" />
|
5 | </a><br />
|
6 |
|
7 | You can find the original ["Marko vs React: An In-depth Look" article here](https://hackernoon.com/marko-vs-react-an-in-depth-look-767de0a5f9a6)!
|
8 |
|
9 | In this article we will take an in-depth look at the differences and
|
10 | similarities between [Marko](http://markojs.com/) and React from the perspective
|
11 | of the maintainers of Marko.
|
12 |
|
13 | On the surface, Marko and React have a lot in common and both are trying to
|
14 | solve very similar problems. Specifically, both Marko and React allow developers
|
15 | to build web applications based on UI components and both free developers from
|
16 | having to write code to manually update the DOM. While many of the features in
|
17 | Marko were inspired by React, Marko and React offer very different usability and
|
18 | performance characteristics. Marko was designed to avoid almost all boilerplate
|
19 | and is more closely aligned with HTML. In almost all cases, a Marko UI component
|
20 | will require less lines of code than its React JSX equivalent while maintaining
|
21 | readability and allowing the same expressiveness as JSX. In addition, Marko is
|
22 | highly optimized for use on the server and in the browser and has a much smaller
|
23 | weight:
|
24 |
|
25 | <img src="https://cdn-images-1.medium.com/max/1600/1*a9hL_pfNrRq1UU3Mxkf3Jg.png" alt="Marko logo" width="100%" /><br />
|
26 |
|
27 | Because the Marko JavaScript library is much smaller than React, it will require
|
28 | less time to load and parse and this will drastically improve page load times on
|
29 | slow connections or on older devices. Based on [our
|
30 | benchmarks](http://markojs.com/#benchmarks), Marko consistently outperforms
|
31 | React by a significant margin on both the server and in the browser.
|
32 |
|
33 | ### Example
|
34 |
|
35 | The following code highlights some of the differences between Marko and React
|
36 | JSX using a somewhat contrived UI component as an example:
|
37 |
|
38 | #### React JSX
|
39 |
|
40 | ```jsx
|
41 | class Counter extends React.Component {
|
42 | constructor(props) {
|
43 | super(props);
|
44 |
|
45 | this.state = { count: 0 };
|
46 | }
|
47 |
|
48 | increment(delta) {
|
49 | this.setState({ count: this.state.count + delta });
|
50 | }
|
51 |
|
52 | render() {
|
53 | const count = this.state.count;
|
54 | let countClassName = "count";
|
55 |
|
56 | if (count > 0) {
|
57 | countClassName += " positive";
|
58 | } else if (count < 0) {
|
59 | countClassName += " negative";
|
60 | }
|
61 |
|
62 | return (
|
63 | <div className="click-count">
|
64 | <div className={countClassName}>{count}</div>
|
65 | <button
|
66 | onClick={() => {
|
67 | this.increment(-1);
|
68 | }}
|
69 | >
|
70 | -1
|
71 | </button>
|
72 | <button
|
73 | onClick={() => {
|
74 | this.increment(1);
|
75 | }}
|
76 | >
|
77 | +1
|
78 | </button>
|
79 | </div>
|
80 | );
|
81 | }
|
82 | }
|
83 | ```
|
84 |
|
85 | <span class="figcaption_hack">[▶ Try Online](http://codepen.io/mlrawlings/pen/wJXOWR?editors=0010)</span>
|
86 |
|
87 | #### Marko
|
88 |
|
89 | ```marko
|
90 | class {
|
91 | onCreate() {
|
92 | this.state = { count: 0 };
|
93 | }
|
94 | increment(delta) {
|
95 | this.state.count += delta;
|
96 | }
|
97 | }
|
98 |
|
99 | $ var count = state.count;
|
100 |
|
101 | <div.click-count>
|
102 | <div.count class={
|
103 | positive: count > 0,
|
104 | negative: count < 0
|
105 | }>
|
106 | ${count}
|
107 | </div>
|
108 | <button on-click('increment', -1)>
|
109 | -1
|
110 | </button>
|
111 | <button on-click('increment', 1)>
|
112 | +1
|
113 | </button>
|
114 | </div>
|
115 | ```
|
116 |
|
117 | <span class="figcaption_hack">[▶ Try
|
118 | Online](http://markojs.com/try-online/?gist=8fe46bc5866605aca0dfeec202604011)</span>
|
119 |
|
120 | ### Similarities
|
121 |
|
122 | Marko and React have the following in common:
|
123 |
|
124 | - UI component-based
|
125 | - JavaScript and HTML markup can be intertwined
|
126 | - No restrictions on JavaScript (use ES5 or ES2015+, your choice)
|
127 | - Virtual DOM rendering in the browser
|
128 | - DOM diffing/patching is used to reconcile views
|
129 | - Both support keyed element matching
|
130 | - UI components can have input properties
|
131 | - UI components can have internal state
|
132 | - Changes to state trigger an asynchronous update to the DOM
|
133 | - Updates to the DOM are batched
|
134 | - Compatible with central application state stores such as Redux and MobX
|
135 | - UI components can be embedded using custom tags
|
136 | - Declarative event binding (no `domEl.addEventListener()` needed)
|
137 | - Support for all DOM events
|
138 | - Event delegation utilized internally for DOM events that bubble
|
139 | - IE9+ support
|
140 | - Similar lifecycle events for UI components
|
141 | - JSX and Marko both compile to JavaScript
|
142 |
|
143 | ### Differences
|
144 |
|
145 | At a high level here are some differences:
|
146 |
|
147 | #### Differences in rendering
|
148 |
|
149 | - **Improved performance:** Marko renders to a virtual DOM in the browser and
|
150 | directly to an HTML stream on the server (Marko supports multiple compilation
|
151 | targets).
|
152 | - **Improved performance:** Marko supports asynchronous rendering with [early
|
153 | flushing of
|
154 | HTML](http://www.ebaytechblog.com/2014/12/08/async-fragments-rediscovering-progressive-html-rendering-with-marko/)
|
155 | for improvements in actual and perceived page load times.
|
156 | - **Improved performance:** React requires an additional client-side re-render if
|
157 | a page is initially rendered on the server while Marko does not.
|
158 | - **Improved ease of use:** Marko automatically serializes UI component state and
|
159 | input down to the browser so that the browser can pick up right where the server
|
160 | left off.
|
161 | - **Improved ease of use:** Marko is suitable for rendering an entire HTML page on
|
162 | the server with support for tags such as `<doctype>` and `<html>`
|
163 |
|
164 | #### Differences in syntax
|
165 |
|
166 | - **Improved ease of use:** Marko uses the
|
167 | [HTML-JS](http://markojs.com/docs/syntax/) syntax and the
|
168 | [JSX](https://facebook.github.io/react/docs/jsx-in-depth.html) syntax is offered
|
169 | for React.
|
170 | - **Improved ease of use:** Marko supports both a concise syntax and a familiar
|
171 | HTML syntax.
|
172 | - **Improved ease of use:** JSX requires strict XML while Marko aligns with less
|
173 | strict HTML that web developers are used to.
|
174 | - **Improved ease of use:** With Marko, _all_ HTML attribute values are parsed as
|
175 | JavaScript expressions.
|
176 | - **Improved ease of use:** Marko supports simple directives for conditionals,
|
177 | looping, etc.
|
178 | - **JSX limitation:** JSX is “just JavaScript” but requires expressions that
|
179 | preclude the usage of JavaScript statements such as in certain places.
|
180 |
|
181 | #### Differences in compilation
|
182 |
|
183 | - **Improved performance:** Marko supports multiple compilation outputs (Marko
|
184 | VDOM and HTML streaming are currently supported).
|
185 | - **Improved ease of use:** Marko compiles UI components to JavaScript modules
|
186 | that export a rendering API.
|
187 | - **Expanded capabilities:** Marko supports a robust API for controlling how
|
188 | custom tags and custom attributes get compiled and it supports compile-time
|
189 | transforms based on a friendly Abstract Syntax Tree (AST).
|
190 | - **Improved performance:** JSX is just syntactic sugar that translates elements
|
191 | to `createElement()` function calls while the Marko compiler has full control over how things are
|
192 | compiled and optimized.
|
193 | - **Improved ease of use:** React requires all UI components to be explicitly
|
194 | imported before they can be used as custom tags while Marko supports both
|
195 | explicit importing and implicit importing.
|
196 | - **Improved performance:** Marko has a modular runtime and the compiler generates
|
197 | code that only imports the parts of the Marko runtime that are needed for much
|
198 | smaller builds.
|
199 | - **Improved ease of use:** Marko supports optional compile-time checks to ensure
|
200 | that only allowed attributes are passed to custom tags. (React `PropTypes` only provide
|
201 | validation at render-time)
|
202 | - **Improved ease of use:** Marko validates _all_ tag names at compile-time.
|
203 | - **Improved ease of use:** Marko provides its own compiler that integrates with
|
204 | Node.js and JavaScript module bundlers while React JSX requires babel and custom
|
205 | babel transforms.
|
206 |
|
207 | #### Differences in UI components
|
208 |
|
209 | - **Reduced boilerplate:** No explicit extending of JavaScript classes in Marko
|
210 | (in contrast to `class Counter extends React.Component` in React).
|
211 | - **Improved ease of use:** Modifications to UI component state are synchronous
|
212 | with Marko while [the rules for React are more
|
213 | complicated](https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous).
|
214 | - **Improved ease of use:** Marko watches UI component state objects to allow
|
215 | state to be modified directly (e.g., `this.state.count++`).
|
216 | - **Improved ease of use:** Marko supports single-file UI components combining
|
217 | JavaScript behavior, CSS styling (with support for CSS preprocessors) and HTML
|
218 | markup. (React requires using one of the many [CSS in JS
|
219 | solutions](https://github.com/MicheleBertoli/css-in-js) if you want styles in
|
220 | the same file as your component and there is no standard in the community)
|
221 | - **Improved maintainability:** Marko supports a seamless transition from a
|
222 | single-file UI component to a multi-file UI component.
|
223 | - **Improved performance:** Marko assumes UI components are pure by default and
|
224 | skips re-rendering when input properties and state are unchanged (React requires
|
225 | extending
|
226 | [React.PureComponent](https://facebook.github.io/react/docs/react-api.html#react.purecomponent)).
|
227 |
|
228 | #### Differences in event systems
|
229 |
|
230 | - **Reduced complexity:** React utilizes [synthetic
|
231 | events](https://facebook.github.io/react/docs/events.html) while Marko utilizes
|
232 | real DOM events.
|
233 | - **Improved ease of use:** Custom events are emitted using the [EventEmitter
|
234 | API](https://nodejs.org/api/events.html) in Marko (e.g., `this.emit('myCustomEvent', arg1, arg2)`).
|
235 | - **Improved ease of use:** Marko has a consistent approach for listening to both
|
236 | native DOM events and custom events.
|
237 | - **Improved ease of use:** React requires passing around `Function` references for custom
|
238 | events while Marko automatically delegates emitted custom events to event
|
239 | handler methods on components.
|
240 | - **Improved ease of use:** Marko provides a simple mechanism for binding
|
241 | additional arguments to event handler methods and `this` will be the component
|
242 | instance.
|
243 |
|
244 | #### Differences in compatibility
|
245 |
|
246 | - **Marko limitation:** Marko has no support for native mobile similar to React
|
247 | Native (although with Marko VDOM rendering, this is possible).
|
248 | - **Marko limitation:** Marko requires a JavaScript module bundler (such as
|
249 | [Lasso](http://markojs.com/docs/lasso/),
|
250 | [Webpack](http://markojs.com/docs/webpack/),
|
251 | [Rollup](http://markojs.com/docs/rollup/) or
|
252 | [Browserify](http://markojs.com/docs/browserify/)) to be used in the browser
|
253 | since Marko UI components compile down to JavaScript modules. (we consider using
|
254 | a JavaScript module bundler a best practice)
|
255 |
|
256 | ---
|
257 |
|
258 | In the sections below we will take a closer look at some of the differences
|
259 | between Marko and React.
|
260 |
|
261 | ### Syntax
|
262 |
|
263 | Both Marko and React JSX allow HTML markup and JavaScript to be combined into a
|
264 | single file and both support building web applications based on UI components.
|
265 | Marko utilizes an [HTML-JS syntax](http://markojs.com/docs/syntax/) while most
|
266 | React apps use the JSX syntax.
|
267 |
|
268 | > React JSX makes JavaScript more like HTML and Marko makes HTML more like
|
269 | > JavaScript.
|
270 |
|
271 | In the end, both Marko and React allow JavaScript and HTML to be intertwined.
|
272 |
|
273 | ### Syntax: attributes
|
274 |
|
275 | #### React JSX
|
276 |
|
277 | In React JSX, all attribute values are parsed as string values unless `{}` is used.
|
278 |
|
279 | ```jsx
|
280 | <MyComponent
|
281 | name="Frank"
|
282 | messageCount={30}
|
283 | visible={true}
|
284 | person={{ firstName: 'John', lastName: 'Doe' }}
|
285 | colors={['red', 'green', 'blue']} />
|
286 |
|
287 | <div id="content" className="foo">Hello</div>
|
288 | ```
|
289 |
|
290 | #### Marko
|
291 |
|
292 | With Marko, _all_ attribute values are parsed as JavaScript expressions. The
|
293 | following Marko code is equivalent to the React JSX code above:
|
294 |
|
295 | ```marko
|
296 | <my-component
|
297 | name="Frank"
|
298 | message-count=30
|
299 | visible=true
|
300 | person={ firstName: 'John', lastName: 'Doe' }
|
301 | colors=['red', 'green', 'blue'] />
|
302 |
|
303 | <div id="content" class="foo">Hello</div>
|
304 | ```
|
305 |
|
306 | ### Syntax: inline JavaScript
|
307 |
|
308 | #### React JSX
|
309 |
|
310 | React JSX starts with JavaScript and allows XML elements to be inlined as shown
|
311 | below:
|
312 |
|
313 | ```jsx
|
314 | import { formatDate } from "./util";
|
315 |
|
316 | function formatName(person) {
|
317 | return person.firstName + " " + person.lastName.charAt(0) + ".";
|
318 | }
|
319 |
|
320 | export default function HelloMessage(props) {
|
321 | var person = props.person;
|
322 |
|
323 | return (
|
324 | <div>
|
325 | Hello {formatName(person)}!
|
326 | <span>You were born on {formatDate(person.birthday)}.</span>
|
327 | </div>
|
328 | );
|
329 | }
|
330 | ```
|
331 |
|
332 | #### Marko
|
333 |
|
334 | Marko starts out in HTML, but it allows JavaScript to be inlined in a clean and
|
335 | maintainable way. Unlike other template languages, Marko aims to allow the full
|
336 | power of JavaScript. The following Marko code is equivalent to the React JSX
|
337 | code above:
|
338 |
|
339 | ```marko
|
340 | import { formatDate } from './util';
|
341 |
|
342 | static function formatName(person) {
|
343 | return person.firstName + ' ' + person.lastName.charAt(0) + '.';
|
344 | }
|
345 |
|
346 | $ var person = input.person;
|
347 |
|
348 | <div>
|
349 | Hello ${formatName(person)}!
|
350 | <span>
|
351 | You were born on ${formatDate(person.birthday)}.
|
352 | </span>
|
353 | </div>
|
354 | ```
|
355 |
|
356 | Lines prefixed with `$` are directly added to the compiled JavaScript output inside
|
357 | the compiled `render()` function (for JavaScript code that should run for every render).
|
358 | Lines prefixed with `static` are directly added to the compiled JavaScript output
|
359 | outside the `render()` function (for code that should only run _once_ when the template is
|
360 | loaded).
|
361 |
|
362 | ### Syntax: HTML support
|
363 |
|
364 | With Marko any valid HTML markup can be used inside a Marko template. This is
|
365 | not the case with React. The following quote is from the [React
|
366 | documentation](https://facebook.github.io/react/docs/introducing-jsx.html#specifying-children-with-jsx):
|
367 |
|
368 | > **Caveat:**
|
369 |
|
370 | > Since JSX is closer to JavaScript than HTML, React DOM uses `camelCase` property naming
|
371 | > convention instead of HTML attribute names.
|
372 |
|
373 | > For example, `class` becomes `className` in JSX, and `tabindex` becomes `tabIndex`.
|
374 |
|
375 | As a result of this caveat for React, [tools for converting HTML to JSX
|
376 | exist](http://magic.reactjs.net/htmltojsx.htm).
|
377 |
|
378 | #### React JSX
|
379 |
|
380 | ```jsx
|
381 | <div id="content" className="my-component">Hello</div>
|
382 |
|
383 | <input type="text" name="firstName" value="John" />
|
384 | ```
|
385 |
|
386 | #### Marko
|
387 |
|
388 | ```marko
|
389 | <div id="content" class="my-component">Hello</div>
|
390 |
|
391 | <input type="text" name="firstName" value="John">
|
392 | ```
|
393 |
|
394 | ### Syntax: conditionals
|
395 |
|
396 | JSX is syntactic sugar on top of JavaScript, but it requires expressions, so
|
397 | simple things like an `if/else/for` statement don’t work on their own within a JSX element. As
|
398 | a result, you must either use a ternary expression, an immediately invoked
|
399 | function expression, function call expression, or the experimental `do {}` expression
|
400 | (stage 0 at the time of writing). This is not an issue for Marko, and tags
|
401 | such as `if()` and `for` can be used anywhere as shown below:
|
402 |
|
403 | #### React JSX
|
404 |
|
405 | ```jsx
|
406 | function counterMessage(count) {
|
407 | return (
|
408 | <div className="counter-message">
|
409 | (function() {
|
410 | if (count < 0) {
|
411 | return <div>Count is negative</div>
|
412 | } else if (count === 0) {
|
413 | return <div>Count is zero</div>
|
414 | } else {
|
415 | return <div>Count is positive</div>
|
416 | }
|
417 | }())
|
418 | </div>
|
419 | )
|
420 | }
|
421 | ```
|
422 |
|
423 | #### Marko
|
424 |
|
425 | ```marko
|
426 | <div.counter-message>
|
427 | <if(count < 0)>
|
428 | <div>Count is negative</div>
|
429 | </if>
|
430 | <else if(count === 0)>
|
431 | <div>Count is zero</div>
|
432 | </else>
|
433 | <else>
|
434 | <div>Count is positive</div>
|
435 | </else>
|
436 | </div>
|
437 | ```
|
438 |
|
439 | Marko also allows directives to be used as attributes for a more condensed
|
440 | template:
|
441 |
|
442 | ```marko
|
443 | <div.counter-message>
|
444 | <div if(count < 0)>Count is negative</div>
|
445 | <div if(count === 0)>Count is zero</div>
|
446 | <div else>Count is positive</div>
|
447 | </div>
|
448 | ```
|
449 |
|
450 | ### Syntax: looping
|
451 |
|
452 | #### React JSX
|
453 |
|
454 | ```jsx
|
455 | function renderColors(colors) {
|
456 | return (
|
457 | <ul>
|
458 | {colors.map(color => (
|
459 | <li
|
460 | className="color"
|
461 | style={{
|
462 | backgroundColor: color
|
463 | }}
|
464 | >
|
465 | {color}
|
466 | </li>
|
467 | ))}
|
468 | </ul>
|
469 | );
|
470 | }
|
471 | ```
|
472 |
|
473 | #### Marko
|
474 |
|
475 | ```marko
|
476 | <ul>
|
477 | <for|color| of=colors>
|
478 | <li.color style={ backgroundColor: color }>
|
479 | ${color}
|
480 | </li>
|
481 | </for>
|
482 | </ul>
|
483 | ```
|
484 |
|
485 | ### Syntax: HTML shorthand
|
486 |
|
487 | ```marko
|
488 | <div id="content"/>
|
489 | <h1 class="subheader"/>
|
490 | <h1 id="pageTitle" class="foo bar"/>
|
491 |
|
492 | <!-- Shorthand equivalent: -->
|
493 | <div#content/>
|
494 | <h1.subheader/>
|
495 | <h1#pageTitle.foo.bar/>
|
496 | ```
|
497 |
|
498 | Marko supports a shorthand based on CSS selectors for less code.
|
499 |
|
500 | React does not support these helpful shorthands.
|
501 |
|
502 | ### Syntax: concise
|
503 |
|
504 | Marko supports a concise syntax that drops angled brackets and ending tags in
|
505 | favor of indentation. Here’s how the Marko syntax options compare:
|
506 |
|
507 | #### Marko HTML syntax
|
508 |
|
509 | ```marko
|
510 | <ul>
|
511 | <for|color| of=colors>
|
512 | <li>${color}</li>
|
513 | </for>
|
514 | </ul>
|
515 | ```
|
516 |
|
517 | #### Marko concise syntax
|
518 |
|
519 | ```marko
|
520 | ul
|
521 | for|color| of=colors
|
522 | li -- ${color}
|
523 | ```
|
524 |
|
525 | #### Marko mixed syntax
|
526 |
|
527 | ```marko
|
528 | ul
|
529 | for|color| of=colors
|
530 | <li>${color}</li>
|
531 | ```
|
532 |
|
533 | The HTML syntax and the concise syntax can be used together:
|
534 |
|
535 | #### React JSX
|
536 |
|
537 | React does not offer a concise syntax.
|
538 |
|
539 | ### Components
|
540 |
|
541 | Marko starts with simple HTML and allows UI component logic to easily be layered
|
542 | on top.
|
543 |
|
544 | #### React JSX
|
545 |
|
546 | A React UI component is typically implemented as a class that extends `ReactComponent`:
|
547 |
|
548 | ```jsx
|
549 | class HelloMessage extends React.Component {
|
550 | render() {
|
551 | return <div>Hello {this.props.name.toUpperCase()}</div>;
|
552 | }
|
553 | }
|
554 | ```
|
555 |
|
556 | React also supports a more concise functional component:
|
557 |
|
558 | ```jsx
|
559 | function HelloMessage(props) {
|
560 | return <div>Hello {props.name.toUpperCase()}</div>;
|
561 | }
|
562 | ```
|
563 |
|
564 | However, if state or lifecycle events are needed then a functional UI component
|
565 | must be converted to a class component:
|
566 |
|
567 | ```jsx
|
568 | class HelloMessage extends React.Component {
|
569 | componentDidMount() {
|
570 | // ...
|
571 | }
|
572 | render() {
|
573 | return <div>Hello {this.props.name.toUpperCase()}</div>;
|
574 | }
|
575 | }
|
576 | ```
|
577 |
|
578 | #### Marko
|
579 |
|
580 | Here is the same component in Marko:
|
581 |
|
582 | ```marko
|
583 | <div>Hello ${input.name.toUpperCase()}</div>
|
584 | ```
|
585 |
|
586 | Behavior can easily be added to any Marko UI component:
|
587 |
|
588 | ```marko
|
589 | class {
|
590 | onMount() {
|
591 | // ...
|
592 | }
|
593 | }
|
594 |
|
595 | <div>Hello ${input.name.toUpperCase()}</div>
|
596 | ```
|
597 |
|
598 | Marko also allows JavaScript behavior, CSS styling and HTML markup to be
|
599 | embedded in the Marko template as a single file UI component:
|
600 |
|
601 | ```marko
|
602 | class {
|
603 | onMount() {
|
604 | // ...
|
605 | }
|
606 | }
|
607 |
|
608 | style.less {
|
609 | .hello {
|
610 | color: red;
|
611 | }
|
612 | }
|
613 |
|
614 | <div.hello>
|
615 | Hello ${input.name.toUpperCase()}
|
616 | </div>
|
617 | ```
|
618 |
|
619 | ### API
|
620 |
|
621 | Marko compiles component to JavaScript modules that export an API for rendering
|
622 | the component as shown below:
|
623 |
|
624 | ```js
|
625 | require("./components/greeting")
|
626 | .renderSync({ name: "Frank" })
|
627 | .appendTo(document.body);
|
628 | ```
|
629 |
|
630 | The same UI component can be rendered to a stream such as a writable HTTP
|
631 | response stream:
|
632 |
|
633 | ```js
|
634 | require("./components/hello").render({ name: "John" }, res);
|
635 | ```
|
636 |
|
637 | > The user’s of a Marko UI component do not need to know that the component was
|
638 | > implemented using Marko.
|
639 |
|
640 | Contrast this with React as an example:
|
641 |
|
642 | ```jsx
|
643 | import ReactDOM from "react-dom";
|
644 |
|
645 | ReactDOM.render(
|
646 | <HelloMessage name="John" />,
|
647 | document.getElementById("container")
|
648 | );
|
649 | ```
|
650 |
|
651 | On top of that, React requires that a different module be imported to render the
|
652 | exact same UI component on the server:
|
653 |
|
654 | ```jsx
|
655 | import ReactDOMServer from "react-dom/server";
|
656 |
|
657 | var html = ReactDOMServer.renderToString(<HelloMessage name="John" />);
|
658 | ```
|
659 |
|
660 | ### Custom tags
|
661 |
|
662 | #### React JSX
|
663 |
|
664 | With React, all custom tags for UI components must be explicitly imported:
|
665 |
|
666 | ```jsx
|
667 | import Hello from "./components/Hello";
|
668 | import GoodBye from "./components/GoodBye";
|
669 |
|
670 | export default function HelloGoodBye(props) {
|
671 | return (
|
672 | <div>
|
673 | <Hello name={props.name} />
|
674 | <GoodBye name={props.name} />
|
675 | </div>
|
676 | );
|
677 | }
|
678 | ```
|
679 |
|
680 | #### Marko
|
681 |
|
682 | Marko supports a mechanism for [automatically discovering custom
|
683 | tags](http://markojs.com/docs/custom-tags/#discovering-tags) for UI components
|
684 | based on the project directory structure. Marko walks up the directory tree to
|
685 | discover all directories and it will also automatically discover custom tags
|
686 | exported by installed packages. This approach negates the need for explicitly
|
687 | importing a custom tag to reduce the amount of code needed in a Marko template.
|
688 | For example given the following directory structure:
|
689 |
|
690 | ```
|
691 | .
|
692 | ├── components/
|
693 | │ ├── hello.marko
|
694 | │ └── good-bye.marko
|
695 | └── index.marko
|
696 | ```
|
697 |
|
698 | The `<hello>` tag and the `<good-bye>` tag nested below the `components/`
|
699 | directory will automatically be made available to the `index.marko` at the root:
|
700 |
|
701 | ```marko
|
702 | <div>
|
703 | <hello name=input.name />
|
704 | <good-bye name=input.name />
|
705 | </div>
|
706 | ```
|
707 |
|
708 | This approach also allows editors and IDEs to offer autocompletion for custom
|
709 | tags.
|
710 |
|
711 | ### Async
|
712 |
|
713 | Even after rendering has started, Marko allows parts of the view to be rendered
|
714 | asynchronously using the [`<await>`](http://markojs.com/docs/core-tags#await)
|
715 | tag as shown in the following Marko template:
|
716 |
|
717 | ```marko
|
718 | import fsp from 'fs-promise';
|
719 |
|
720 | $ var filePath = __dirname + '/hello.txt';
|
721 | $ var readPromise = fsp.readFile(filePath, {encoding: 'utf8'});
|
722 |
|
723 | <await(readPromise)>
|
724 | <@then|helloText|>
|
725 | <p>${helloText}</p>
|
726 | </@then>
|
727 | </await>
|
728 | ```
|
729 |
|
730 | ### Compiler
|
731 |
|
732 | Marko compiles a template differently based on whether or not it will be used on
|
733 | the server or in the browser. For example, given the following template:
|
734 |
|
735 | ```marko
|
736 | <div>Hello ${input.name}!</div>
|
737 | ```
|
738 |
|
739 | #### Compiled for the server:
|
740 |
|
741 | ```marko
|
742 | var marko_template = require("marko/html").t(__filename),
|
743 | marko_helpers = require("marko/runtime/html/helpers"),
|
744 | marko_escapeXml = marko_helpers.x;
|
745 |
|
746 | function render(input, out) {
|
747 | out.w("<div>Hello " +
|
748 | marko_escapeXml(input.name) +
|
749 | "!</div>");
|
750 | }
|
751 | ```
|
752 |
|
753 | #### Compiled for the browser:
|
754 |
|
755 | ```marko
|
756 | var marko_template = require("marko/vdom").t(__filename);
|
757 |
|
758 | function render(input, out) {
|
759 | out.e("DIV", null, 3)
|
760 | .t("Hello ")
|
761 | .t(input.name)
|
762 | .t("!");
|
763 | }
|
764 | ```
|
765 |
|
766 | ### Compile-time code transforms
|
767 |
|
768 | The Marko compiler was built to support compile-time code generators for custom
|
769 | tags and it also provides support for compile-time transforms. While Babel
|
770 | allows code transformations of JavaScript, the Marko compiler provides support
|
771 | for resolving custom tags declaratively and the Marko AST provides for very
|
772 | powerful and simple transformations as shown in the following code for rendering
|
773 | Markdown to HTML at _compile-time_:
|
774 |
|
775 | **components/markdown/code-generator.js:**
|
776 |
|
777 | ```js
|
778 | import marked from "marked";
|
779 | import { removeIndentation } from "./util";
|
780 |
|
781 | export default function generateCode(el, codegen) {
|
782 | var bodyText = removeIndentation(el.bodyText);
|
783 | var html = marked(bodyText);
|
784 | var builder = codegen.builder;
|
785 | return builder.html(builder.literal(html));
|
786 | }
|
787 | ```
|
788 |
|
789 | The `<markdown>` tag can then be used as shown below:
|
790 |
|
791 | ```marko
|
792 | <markdown>
|
793 |
|
794 | > This section demonstrates Markdown in Marko
|
795 |
|
796 | # Marko is awesome!
|
797 |
|
798 | - High performance
|
799 | - Small
|
800 | - Intuitive
|
801 |
|
802 | </markdown>
|
803 | ```
|
804 |
|
805 | In this example, after the template is compiled, the
|
806 | [marked](https://github.com/chjj/marked) library is no longer needed at
|
807 | render-time.
|
808 |
|
809 | ### Tools
|
810 |
|
811 | Marko and React offer a variety of developer tools. The [Marko developer
|
812 | tools](https://github.com/marko-js/marko-devtools) are constantly evolving, but
|
813 | Marko currently provides tools for unit testing UI components, precompiling `.marko`
|
814 | files and generating configuration-less apps (similar to
|
815 | [create-react-app](https://github.com/facebookincubator/create-react-app)).
|
816 | Currently, there are no Marko developer tools that integrate with the browser,
|
817 | but this is something we would like to see in the future. We will go into more
|
818 | detail on the Marko developer tools in a future post.
|
819 |
|
820 | #### IDE and editor support
|
821 |
|
822 | Marko offers syntax highlighting across all major IDEs and editors, as well as
|
823 | on GitHub. Marko provides first-class support for the Atom editor with syntax
|
824 | highlighting,
|
825 | [Autocomplete](https://github.com/marko-js/atom-language-marko#autocomplete) for
|
826 | both HTML and custom tags,
|
827 | [Hyperclick](https://github.com/marko-js/atom-language-marko#hyperclick) to
|
828 | quickly jump to referenced files and methods, and [Pretty
|
829 | printing](https://github.com/marko-js/atom-language-marko#prettyprint) to keep
|
830 | your code readable.
|
831 |
|
832 | ---
|
833 |
|
834 | ### Why Marko?
|
835 |
|
836 | Here are just a few reasons you should consider using
|
837 | [Marko](http://markojs.com/) over React:
|
838 |
|
839 | - Marko requires much less boilerplate.
|
840 | - Marko has much better performance based on our benchmarks.
|
841 | - Marko offers a clean and powerful syntax that aligns with HTML while also
|
842 | allowing the full power of JavaScript.
|
843 | - Marko has much less complexity and a very small runtime.
|
844 | - Marko has a much lower page weight for faster page loads.
|
845 | - Marko has strong integrations with Node.js.
|
846 | - Marko allows for extremely powerful IDE and editor plugins (see the [Marko
|
847 | plugin for Atom](https://github.com/marko-js/atom-language-marko) as an
|
848 | example).
|
849 | - Marko has a powerful compiler that allows new features to be added without
|
850 | introducing bloat.
|
851 | - eBay relies heavily on Marko and it is being used to build ebay.com (including
|
852 | the mobile web).
|
853 | - Marko has a strong and growing community on
|
854 | [GitHub](https://github.com/marko-js/marko) and in
|
855 | [Gitter](https://gitter.im/marko-js/marko).
|
856 |
|
857 | Interested in learning more about Marko? If so, you can get additional
|
858 | information on the [Marko website](http://markojs.com/). Join the conversation
|
859 | and contribute on [GitHub](https://github.com/marko-js/marko) and follow us on
|
860 | [Twitter](https://twitter.com/MarkoDevTeam).
|