UNPKG

24.5 kBMarkdownView Raw
1# What's New in Marko v4
2
3A lot of exciting improvements were made as part of the Mark v4 release.
4
5## Notable Changes and Improvements
6
7### Single file components ([#399](https://github.com/marko-js/marko/issues/399))
8
9Marko now supports combining HTML, rendering logic, client-side behavior and styling into a single file component.
10
11_src/components/click-count/index.marko_
12
13```marko
14class {
15 onCreate() {
16 this.state = {
17 count: 0
18 }
19 }
20
21 onInput(input) {
22 this.state.count = input.value || 0;
23 }
24
25 increment() {
26 this.state.count++;
27 }
28}
29
30style.less {
31 .count {
32 color: #09c;
33 }
34 .button {
35 background: #fff;
36 }
37}
38
39<div>
40 <span class="count">${state.count}</span>
41 <button class="button" on-click('increment')>
42 increment count
43 </button>
44</div>
45```
46
47You can easily `require`/`import` a single file component and interact with it using the exported JavaScript API:
48
49```js
50var clickCount = require("./src/components/click-count");
51
52var component = clickCount
53 .renderSync({
54 value: 10
55 })
56 .appendTo(document.body)
57 .getComponent();
58
59component.increment();
60```
61
62Of course, a single file component can also be embedded in another template as a custom tag:
63
64```marko
65<div>
66 <click-count value=10 />
67</div>
68```
69
70### Virtual DOM support ([#366](https://github.com/marko-js/marko/issues/366))
71
72Because Marko renders raw HTML strings to a stream on the server, Marko has always been faster than other libraries by an [order of magnitude](https://github.com/patrick-steele-idem/marko-vs-react) when rendering on the server. However although Marko has been _pretty_ fast in the browser, it was a little behind some of our competitors. This was mainly because the output HTML string needed to be parsed into a DOM in order to do DOM diffing/patching.
73
74That's changed. Marko now supports multiple compilation outputs. Templates compiled for the server will continue to render to an HTML stream/string and templates compiled for the browser will now render to a fast and lightweight virtual DOM tree. The code samples below show how the two different compilation outputs compare:
75
76_Compiled for HTML output (server-side):_
77
78```javascript
79function render(data, out) {
80 var colors = data.colors;
81
82 if (colors && colors.length) {
83 out.w("<ul>");
84
85 marko_forEach(colors, function(color) {
86 out.w('<li class="color">' + marko_escapeXml(color) + "</li>");
87 });
88
89 out.w("</ul>");
90 } else {
91 out.w("<div>No colors!</div>");
92 }
93}
94```
95
96_Compiled for VDOM output (browser-side):_
97
98```javascript
99var marko_attrs0 = {
100 class: "color"
101 },
102 marko_node0 = marko_createElement("DIV", null, 1, marko_const_nextId()).t(
103 "No colors!"
104 );
105
106function render(data, out) {
107 var colors = data.colors;
108
109 if (colors && colors.length) {
110 out.be("UL");
111
112 marko_forEach(colors, function(color) {
113 out.e("LI", marko_attrs0, 1).t(marko_str(color));
114 });
115
116 out.ee();
117 } else {
118 out.n(marko_node0);
119 }
120}
121```
122
123The VDOM output allows optimizations that were previously not possible:
124
125- Static subtrees are pulled into variables that are only initialized once and reused for every render
126- Static attributes that are on dynamic elements are pulled out to static variables
127- Diffing is skipped when comparing static subtrees
128- Diffing is skipped when comparing static attributes
129
130Our benchmarks show a significant improvement in rendering time and we are consistently outperforming React/Preact/Inferno, Vue and other UI libraries.
131
132### Merge in Marko Widgets ([#390](https://github.com/marko-js/marko/issues/390))
133
134A big part of this release is a shift in focus from Marko being merely a templating language to a complete UI library. As such, we are providing first-class support for components.
135
136You will no longer need to install `marko-widgets` as an external library, and there is more cohesion between the templates and components/components.
137
138### Improved component lifecycle methods ([#396](https://github.com/marko-js/marko/issues/396))
139
140- `getInitialState()``onInput(input)`
141- `getComponentConfig()``onInput(input)`
142- `init(config)``onMount()`
143- `getTemplateData(input, state)` ➔ (no longer needed)
144- `getInitialProps(input)` ➔ (no longer needed)
145
146```js
147class {
148 onCreate(input) {
149 this.state = {
150 count: 0
151 }
152 this.initialCount = 0;
153 }
154
155 onInput(input) {
156 if (input.count) {
157 // if the parent component passes a value
158 // for count, we'll reset our state to that
159 // value
160 this.state.count = input.count;
161 this.initialCount = input.count;
162 }
163 }
164
165 onRender(out) {
166 // Called for every render. This component
167 // may or may not be mounted.
168 // During render we have access to the `out`.
169 console.log('The template is about to be rendered!');
170 }
171
172 onMount() {
173 console.log('The component has mounted!');
174
175 console.log('Count: ' + this.state.count);
176 console.log('Initial count: ' + this.initialCount);
177 }
178
179 onUpdate() {
180 console.log('The DOM has been updated!');
181 }
182
183 onDestroy() {
184 console.log('The component is about to be destroyed :(')
185 }
186
187 // Helper methods:
188
189 reset() {
190 this.state.count = this.initialCount;
191 }
192
193 increment() {
194 this.state.count++;
195 }
196
197 decrement() {
198 this.state.count--;
199 }
200}
201```
202
203### Automatically watch component state object for changes ([#406](https://github.com/marko-js/marko/issues/406))
204
205**Old:**
206
207```js
208{
209 getInitialState(input) {
210 return {
211 count: input.count || 0
212 };
213 }
214 increment() {
215 this.setState('count', this.state.count+1);
216 }
217}
218```
219
220**New:**
221
222```js
223{
224 onInput(input) {
225 this.state = {
226 count: input.count || 0
227 };
228 }
229 increment() {
230 this.state.count++;
231 }
232}
233```
234
235In addition, the default state can now be declared:
236
237```js
238{
239 onCreate() {
240 this.state = {
241 count: 0
242 };
243 }
244
245 onInput(input) {
246 this.state = {
247 count: input.count
248 };
249 }
250
251 increment() {
252 this.state.count++;
253 }
254}
255```
256
257### Consistent rendering API ([#415](https://github.com/marko-js/marko/issues/415))
258
259In Marko v3, UI components exported an API that included DOM insertion methods while simple templates only exported an API for rendering to a string/stream. With Marko v4, simple templates and UI components now export the exact same rendering API.
260
261```js
262// Append to an existing DOM node:
263require("./template.marko")
264 .renderSync({ name: "Frank " })
265 .appendTo(document.body);
266
267// Replace an existing DOM node:
268require("./template.marko")
269 .renderSync({ name: "Frank " })
270 .replace(document.getElementById("foo"));
271```
272
273### Less Boilerplate
274
275- Removed: `w-bind`
276- Removed: `w-extend`
277- Removed: `require('marko-widgets').defineComponent(...)`
278- Removed: `require('marko-widgets').defineWidget(...)`
279- Removed: `require('marko-widgets').defineRenderer(...)`
280- Removed: `w-body` (use `<include()>` instead)
281- `w-on*="handleSomeEvent"` --> `on*('handleSomeEvent')`
282- `w-id` --> `key`
283- `w-for` --> `for-key`
284- `w-preserve` --> `no-update`
285- `class="foo" w-preserve-attrs="class"` --> `class:no-update="foo"`
286
287Some of these things are described in more detail later in this document.
288
289### Embedded JavaScript blocks
290
291**Old:**
292
293```marko
294<var name="Frank"/>
295<assign name="John"/>
296<invoke console.log(name)/>
297```
298
299**New:**
300
301```marko
302$ var name='Frank';
303$ name='John';
304$ console.log(name);
305```
306
307Alternatively:
308
309```marko
310$ {
311 var name='Frank';
312 name='John';
313 console.log(name);
314}
315```
316
317JavaScript blocks can be embedded anywhere by putting `$` at the start of the line (ignoring whitespace) and it works the same for both the concise syntax and the non-concise syntax:
318
319```marko
320<div.hello>
321 <for(name in names)>
322 $ name = name.toUpperCase();
323 Hello ${name}!
324 </for>
325</div>
326```
327
328Static JavaScript blocks (for JavaScript that should only be executed once when the template is first loaded) are also supported:
329
330**Old:**
331
332```marko
333<script marko-init>
334 function formatName(person) {
335 return person.firstName + ' ' + person.lastName;
336 }
337 function isTeenager(person) {
338 return person.age > 12 && person.age < 20;
339 }
340</script>
341<div>
342 Hello ${formatName(input.person)}!
343 Teenager?: ${isTeenager(input.person) ? 'Yes' : 'No'}
344</div>
345```
346
347**New:**
348
349```marko
350static function formatName(person) {
351 return person.firstName + ' ' + person.lastName;
352}
353
354static function isTeenager(person) {
355 return person.age > 12 && person.age < 20;
356}
357<div>
358 Hello ${formatName(input.person)}!
359 Teenager?: ${isTeenager(input.person) ? 'Yes' : 'No'}
360</div>
361```
362
363Alternatively:
364
365```marko
366static {
367 function formatName(person) {
368 return person.firstName + ' ' + person.lastName;
369 }
370
371 function isTeenager(person) {
372 return person.age > 12 && person.age < 20;
373 }
374}
375<div>
376 Hello ${formatName(input.person)}!
377 Teenager?: ${isTeenager(input.person) ? 'Yes' : 'No'}
378</div>
379```
380
381### Template variables
382
383- `data` --> `input` - References the input object (should be treated as immutable)
384- Introduced `state` - References the components raw state object (for components only)
385- Introduced `component` - References the component instance (for components only)
386
387## Other Improvements
388
389### Only diff attributes that are rendered by Marko ([#417](https://github.com/marko-js/marko/issues/417))
390
391Previously, when diffing the DOM, all of the attributes on a real HTML element node were diffed with all of the attributes on a newly rendered HTML element node. This posed a problem when using Marko with third party libraries, such as animation libraries, that added HTML attributes that should have been left alone. The proposed workaround was to add the `w-preserve-attrs` attribute wherever needed.
392
393In Marko v4, only the attributes rendered by Marko are ever modified by Marko. Any attributes added by third-party libraries are simply ignored.
394
395### Allow multiple top-level DOM elements to be bound ([#393](https://github.com/marko-js/marko/issues/393))
396
397**Old:**
398
399```marko
400<div w-bind>
401 <h1>The current count is ${data.count}</h1>
402 <button onClick('incrementCount')>Increment Count</button>
403</div>
404```
405
406**New:**
407
408```marko
409<h1>The current count is ${input.count}</h1>
410<button onClick('incrementCount')>Increment Count</button>
411```
412
413### Template as entry point for UI components ([#416](https://github.com/marko-js/marko/issues/416))
414
415**Old:**
416
417`index.js`
418
419```js
420module.exports = require('marko-widgets').defineComponent({
421 template: require('./template.marko'),
422 ...
423});
424```
425
426`template.marko`
427
428```marko
429<div w-bind>
430 ...
431</div>
432```
433
434**New:**
435
436`component.js`
437
438```js
439module.exports = {
440 ...
441};
442```
443
444`index.marko`
445
446```marko
447<div>
448 ...
449</div>
450```
451
452> The compiled template now exports the component
453
454### Allow event handler attribute to bind additional arguments ([#401](https://github.com/marko-js/marko/issues/401))
455
456**Old:**
457
458```marko
459<ul for(color in colors)>
460 <li w-onClick="handleColorClick" data-color=color>${color}</li>
461</ul>
462```
463
464```js
465handleColorClick(event, el) {
466 console.log(el.getAttribute('data-color'), 'was clicked');
467}
468```
469
470**New:**
471
472```marko
473class {
474 handleColorClick(color, event, el) {
475 console.log(color, 'was clicked');
476 }
477}
478
479<ul for(color in colors)>
480 <li onClick('handleColorClick', color)>${color}</li>
481</ul>
482```
483
484NOTE: `w-on*` has been deprecated. See: [Deprecate `w-on*` in favor of `on*()`](#deprecate-w-on)
485
486### Introduce the `<import>` tag ([#404](https://github.com/marko-js/marko/issues/404))
487
488Marko v4 introduces ES6 style imports for importing other JavaScript modules:
489
490**Old:**
491
492```marko
493<script marko-init>
494 var helpers = require('./helpers');
495</script>
496<div>Total: ${helpers.formatCurrency(data.total)}</div>
497```
498
499**New:**
500
501```marko
502import helpers from "./helpers"
503<div>Total: ${helpers.formatCurrency(data.total)}</div>
504```
505
506The full ES6 import syntax is supported:
507
508```marko
509import { formatCurrency } from "./helpers"
510<div>Total: ${formatCurrency(data.total)}</div>
511```
512
513### Allow dynamic custom tags/components to be used with `<include>` ([#139](https://github.com/marko-js/marko/issues/139))
514
515**Old:**
516
517```marko
518<invoke data.myComponent.renderer({name: 'Frank'}, out)/>
519```
520
521**New:**
522
523```marko
524<include(input.myComponent) name='Frank' />
525```
526
527or
528
529```marko
530<include(input.myComponent, {name: 'Frank'}) />
531```
532
533### Single file components outside of a directory are not automatically discovered
534
535The following are equivalent:
536
537Component directory:
538
539```
540components/hello/index.marko
541```
542
543Component file:
544
545```
546components/hello.marko
547```
548
549### Supporting files for UI components
550
551The following are equivalent:
552
553Component directory:
554
555```
556components/hello/
557 compnent.js
558 index.marko
559 style.less
560```
561
562Component file:
563
564```bash
565components/
566 hello.component.js
567 hello.marko
568 hello.style.less
569```
570
571### Split component
572
573Marko v4 improves how split components are supported. Split component allow only the client-side logic to be sent to the browser if the rendering logic (and view template) are not needed in the browser:
574
575```
576components/hello/
577 component.js # Required to render
578 component-browser.js # Required to handle events, etc.
579 index.marko # Required to render
580```
581
582### Introduce `state` as a local variable ([#400](https://github.com/marko-js/marko/issues/400))
583
584**Old:**
585
586`component.js`
587
588```js
589{
590 getInitialState(input) {
591 return {
592 name: input.name,
593 birthday: input.birthday
594 }
595 },
596 getTemplateData(state, input) {
597 return {
598 name: state.name,
599 age: calculateAge(state.birthday)
600 }
601 },
602 ...
603}
604```
605
606`template.marko`
607
608```marko
609<div>
610 Hello ${data.name}! You are ${data.age} year(s) old.
611</div>
612```
613
614**New:**
615
616`component.js`
617
618```js
619{
620 onInput(input) {
621 // `this.state` will be available as the `state` variable
622 // in the template.
623 this.state = {
624 name: input.name,
625 birthday: input.birthday
626 };
627 }
628 ...
629}
630```
631
632`template.marko`
633
634```marko
635$ var age = calculateAge(state.birthday);
636<div>
637 Hello ${state.name}! You are ${age} year(s) old.
638</div>
639```
640
641### Make output of render `Promise`-compatible ([#251](https://github.com/marko-js/marko/issues/251))
642
643**Old:**
644
645```js
646template.render({}, function(err, html, out) {});
647```
648
649**New:**
650
651```js
652template
653 .render({})
654 .then(function(result) {})
655 .catch(function(err) {});
656
657// render() can now be used with async/await
658var out = await template.render({});
659out.appendTo(document.body);
660```
661
662NOTE: callback/events still work as well
663
664### Make `<await-reorderer/>` optional ([#410](https://github.com/marko-js/marko/issues/410))
665
666**Old:**
667
668```marko
669<html>
670 ...
671 <body>
672 ...
673 <await-reorderer/>
674 </body>
675</html>
676```
677
678**New:**
679
680```marko
681<html>
682 ...
683 <body>
684 ...
685 </body>
686</html>
687```
688
689_Automatically inserted before `</body>`_
690
691### Allow multiple extensions when installing the Node.js require hook ([#407](https://github.com/marko-js/marko/issues/407))
692
693**Old:**
694
695```js
696require("marko/node-require").install({
697 extension: ".marko"
698});
699```
700
701**New:**
702
703```js
704require("marko/node-require").install({
705 extensions: [".marko", ".marko.xml", ".html"]
706});
707```
708
709### Auto hot reload for any extensions provided when installing the Node.js require hook ([#363](https://github.com/marko-js/marko/issues/363))
710
711Hot reload any extensions that were registered via `require('marko/node-require').install()`.
712
713### Allow spaces around attributes ([#403](https://github.com/marko-js/marko/issues/403))
714
715**Old:**
716
717```marko
718var className="foo"
719<div class=className/>
720```
721
722**New:**
723
724```marko
725$ var className = "foo"
726<div class = className/>
727```
728
729> NOTE: spaces are **allowed**, not required
730
731### Allow compile-time transformers to be registered at the template level ([#408](https://github.com/marko-js/marko/issues/408))
732
733`marko.json`
734
735```json
736{
737 "transformer": "./my-transformer.js"
738}
739```
740
741`my-transformer.js`
742
743```js
744module.exports = function transform(rootNode, context) {
745 // ...
746};
747```
748
749[see commit](https://github.com/marko-js/marko/commit/a35e6bdbc3fe6e7f4e92fb377c435e29ab3d6e33)
750
751### Allow regular expression for an HTML attribute value ([#386](https://github.com/marko-js/marko/issues/386))
752
753**Old:**
754
755```marko
756<!-- escaped backslash (\) since strings are parsed as JS values -->
757<input type="text" pattern="\\w{2,20}" />
758```
759
760**New:**
761
762```marko
763<!-- just use a regex -->
764<input type="text" pattern=/\w{2,20}/ />
765```
766
767## Deprecations
768
769A huge effort is being made to make this release as painless as possible and keep backwards compatibility wherever possible. It should be possible to continue to use custom tags that were developed against v3 with the v4 release as long as there are no dependencies on features deprecated in Marko v3 that have now been removed in Marko v4 (see [Breaking Changes](#breaking-changes) below).
770
771Additionally, [`marko-migrate`](https://github.com/marko-js/marko-migrate) will be updated to handle many of the deprecations described below.
772
773### Deprecate `<script marko-init>` and replace with `static` section ([#397](https://github.com/marko-js/marko/issues/397))
774
775**Old:**
776
777```marko
778<script marko-init>
779 var format = require('format');
780</script>
781<var name="World"/>
782<div>Hello ${format(name)}</div>
783```
784
785**New:**
786
787```marko
788static var format=require('format')
789$ var name='World'
790<div>Hello ${format(name)}</div>
791```
792
793### Deprecate `<var>`, `<assign>` and `<invoke>`
794
795Use embedded JavaScript blocks instead
796
797### Deprecate `w-bind` ([#394](https://github.com/marko-js/marko/issues/394), [#395](https://github.com/marko-js/marko/issues/395))
798
799**Old:**
800
801```marko
802<div w-bind>
803 ...
804</div>
805```
806
807**New:**
808
809```marko
810<div>
811 ...
812</div>
813```
814
815(Automatic binding to `./component.js`)
816
817### Deprecate `widget-types` ([#514](https://github.com/marko-js/marko/issues/514))
818
819**Old:**
820
821```marko
822<widget-types default="./component" mobile="./component-mobile"/>
823
824<div w-bind=(data.isMobile ? 'default' : 'mobile')>
825 ...
826</div>
827```
828
829`widget-types` should no longer be used. Instead, the JavaScript module bundler should handle the remapping if needed.
830
831### Deprecate `w-id` and `w-for` in favor of `key` and `for-key` ([#394](https://github.com/marko-js/marko/issues/394))
832
833The `w-id` attribute was used to obtain references using `this.getEl(refId)`. `w-id` has been replaced with the `key` attribute:
834
835**Old:**
836
837```marko
838<input type="text" w-id="nameInput" />
839```
840
841**New:**
842
843```marko
844<input type="text" key="nameInput" />
845```
846
847Similarly, `w-for` has been been replaced with `for-key`:
848
849**Old:**
850
851```marko
852<label w-for="nameInput">Name</label>
853<input type="text" w-id="nameInput" />
854```
855
856**New:**
857
858```marko
859<label for-key="nameInput">Name</label>
860<input type="text" key="nameInput" />
861```
862
863<a name="deprecate-w-on"></a>
864
865### Deprecate `w-on*` in favor of `on*()` ([#420](https://github.com/marko-js/marko/issues/420))
866
867**Old:**
868
869```marko
870<button w-on-click="handleClick">click me</button>
871```
872
873or
874
875```marko
876<button w-onClick="handleClick">click me</button>
877```
878
879**New:**
880
881```marko
882<button on-click('handleClick')>click me</button>
883```
884
885or
886
887```marko
888<button onClick('handleClick')>click me</button>
889```
890
891### Deprecate `<init-widgets/>` ([#409](https://github.com/marko-js/marko/issues/409))
892
893**Old:**
894
895```marko
896<html>
897 ...
898 <body>
899 ...
900 <init-widgets/>
901 </body>
902</html>
903```
904
905**New:**
906
907**Automatic component initialization!**
908
909### Deprecate `w-body` and replace with `include` ([#418](https://github.com/marko-js/marko/issues/418))
910
911**Old:**
912
913```marko
914<div>
915 <h1>My Awesome Component</h1>
916 <div class="body" w-body/>
917</div>
918```
919
920**New:**
921
922```marko
923<div>
924 <h1>My Awesome Component</h1>
925 <div class="body" include()/>
926</div>
927```
928
929Or, as a tag:
930
931```marko
932<div>
933 <h1>My Awesome Component</h1>
934 <div class="body">
935 <include()/>
936 </div>
937</div>
938```
939
940NOTE: The parens (i.e., `()`) are optional for both the include attribute and the include tag
941
942Or, with an argument value:
943
944```marko
945<div>
946 <h1>My Awesome Component</h1>
947 <div class="body">
948 <include(data.renderBody || data.label)/>
949 </div>
950</div>
951```
952
953### Deprecate `w-preserve*` and replace with `no-update*` ([#419](https://github.com/marko-js/marko/issues/419))
954
955**Old:**
956
957```marko
958<div w-preserve>
959 ...
960</div>
961```
962
963**New:**
964
965```marko
966<div no-update>
967 ...
968</div>
969```
970
971### Deprecate `w-preserve-attrs` and replace with `:no-update` ([#422](https://github.com/marko-js/marko/issues/422))
972
973**Old:**
974
975```marko
976<div style="color:#09c" w-preserve-attrs="style">
977 ...
978</div>
979```
980
981**New:**
982
983```marko
984<div style:no-update="color:#09c">
985 ...
986</div>
987```
988
989### Deprecate `w-extend` and allow multiple components to be bound to the same HTML element ([#392](https://github.com/marko-js/marko/issues/392))
990
991> `w-extend` is now deprecated
992
993**Old:**
994
995```marko
996<div w-bind>
997 <some-component w-onEvent="handleEvent"/>
998</div>
999```
1000
1001or
1002
1003```marko
1004<some-component w-extend w-onEvent="handleEvent"/>
1005```
1006
1007**New:**
1008
1009```marko
1010<some-component onEvent('handleEvent')/>
1011```
1012
1013NOTE: The outer most component is what is returned when calling `getComponent()`/`getComponentForEl()`.
1014
1015<a name="breaking-changes"></a>
1016
1017## Breaking Changes
1018
1019In order to move forward it was necessary to introduce a few (minor) breaking changes. We are also removing support for some features that were already logging deprecation messages in v3.
1020
1021### Consistent rendering API ([#389](https://github.com/marko-js/marko/issues/389))
1022
1023**Old:**
1024
1025```js
1026var template = require("./template.marko");
1027var component = require("./my-component");
1028var data = {};
1029
1030template.render(data); // returns `out`
1031template.render(data, (err, html, out) => {});
1032template.renderSync(data); // returns a String representing the HTML output
1033
1034component.render(data); // returns a `RenderResult`
1035component.render(data, (err, renderResult) => {});
1036component.renderSync(data); // throws an error, not a method.
1037```
1038
1039**New:**
1040
1041```js
1042var template = require("./template.marko");
1043var component = require("./my-component");
1044var data = {};
1045
1046template.render(data); // returns `out`
1047template.render(data, (err, out) => {});
1048template.renderSync(data); // returns `out`
1049
1050component.render(data); // returns `out`
1051component.render(data, (err, out) => {});
1052component.renderSync(data); // returns `out`
1053```
1054
1055Also, `out` has been updated to implement DOM manipulation methods like `appendTo` that were previously only available from the `RenderResult` returned from component renders.
1056
1057NOTE: We will implement `out.toString()` and `out.toJSON()` so in many cases the `out` can be used as a string.
1058
1059### Remove support for deprecated `empty`/`notEmpty` helpers ([#357](https://github.com/marko-js/marko/issues/357))
1060
1061> Already deprecated in v3
1062
1063The `empty`/`notEmpty` helpers were automatically being added to every compiled
1064template. While they can be helpful, we feel it is better if the developer
1065explicitly imports only the exact helpers that your code depends on for
1066improved modularity.
1067
1068### Remove hyphenated properties from input model ([#356](https://github.com/marko-js/marko/issues/356))
1069
1070> Already deprecated in v3
1071
1072Given a template like this:
1073
1074```marko
1075<include("./include-target.marko") first-name='Frank'/>
1076```
1077
1078`include-target.marko` looks like:
1079
1080**Old:**
1081
1082```marko
1083-- Hello ${data['first-name']}
1084```
1085
1086**New:**
1087
1088```marko
1089-- Hello ${input.firstName}
1090```
1091
1092### Remove support for deprecated `<async-fragment>` and related tags ([#312](https://github.com/marko-js/marko/pull/312))
1093
1094> Already deprecated in v3
1095
1096**Old:**
1097
1098```marko
1099<async-fragment var="foo" data-provider=data.provider>
1100 ${foo}
1101</async-fragment>
1102```
1103
1104**New:**
1105
1106```marko
1107<await(data.provider)>
1108 <@then|foo|>
1109 ${foo}
1110 </@then>
1111</await>
1112```
1113
1114### Remove support for emitting deprecated `async-fragment` events ([#426](https://github.com/marko-js/marko/issues/426))
1115
1116> Already deprecated in v3
1117
1118| Old | New |
1119| ---------------------------- | --------------------- |
1120| `asyncFragmentFinish` | `await:finish` |
1121| `asyncFragmentBegin` | `await:begin` |
1122| `asyncFragmentBeforeRender` | `await:beforeRender` |
1123| `asyncFragmentClientReorder` | `await:clientReorder` |