UNPKG

26.4 kBMarkdownView Raw
1# Upgrading to Marko 4
2
3The following guide will help you get through the upgrade process quickly and smoothly. After any given step you should have a working application.
4
5This means you should complete a step and get it merged back into master fairly quickly. You shouldn't need to have a `marko-4-upgrade` branch for your project that lives in limbo for a couple of weeks falling behind the other changes that are being merged into master.
6
7If you do decide to pause and later jump in where you left off, be sure to repeat Step 0 first 😉.
8
9## Step 0 - Ensure you're in a working state
10
11Run your application and tests to ensure your project is in a working state. There's little worse than finding an issue after you've started the upgrade process only to figure out the issue existed beforehand.
12
13## Step 1 - Upgrade to latest 3.x
14
15Before we start, you'll want to make sure that you are already on the latest `3.x` release of `marko` and the latest `6.x` release of `marko-widgets`. Later versions of `marko@3` and `marko-widgets@6` ship with deprecation warnings should be handled (the next step) before upgrading to Marko 4. This will make your life _so_ much easier.
16
17```
18npm install marko@^3 marko-widgets@^6
19```
20
21or
22
23```
24yarn upgrade marko@^3 marko-widgets@^6
25```
26
27> Note: Do NOT run `npm install marko` (without the `@^3`). This will put you on Marko 4 and we're not quite there yet.
28
29## Step 2 - Deal with deprecations
30
31Run your application and tests and ensure that there are no deprecation warnings logged to the console. If there are, you should follow the instructions in the deprecation messages to avoid the deprecated pattern and migrate to the recommended pattern. In particular:
32
33- Using `w-extend` is not supported by the compatibility layer in Marko 4, compose components instead (you'll need an extra wrapper node in Marko 3, but it can be removed once you upgrade).
34
35 ```marko
36 // UNSUPPORTED
37 <fancy-button w-extend/>
38
39 // SUPPORTED
40 <span w-bind>
41 <fancy-button/>
42 </span>
43 ```
44
45- Using `<widget-types/>` to conditionally bind to two or more different widgets is not supported by the compatibility layer in Marko 4 (using `<widget-types>` to disable binding is still supported).
46
47 ```marko
48 // UNSUPPORTED
49 <widget-types default="./widget" mobile="./widget-mobile"/>
50 <div w-bind=(data.isMobile ? 'default' : 'mobile')>
51 ...
52 </div>
53
54 // SUPPORTED
55 <widget-types default="./"/>
56 <div w-bind=(data.includeWidget ? 'default' : null)>
57 ...
58 </div>
59 ```
60
61- Using `data-widget` and `data-w-*` attributes in your application code and tests. These attributes existed so Marko could keep track of DOM nodes and they don't exist in Marko 4.
62- If you're accessing the DOM to get an Element, prefer `next/prevElementSibling`, `first/lastElementChild` and `children` instead of ~`next/prevSibling`~, ~`first/lastChild`~ and ~`childNodes`~. There are differences in the DOM structure generated by Marko 3 vs 4 and you might not get the same node after upgrading with the non-element version of these properties.
63- In Marko 3, the rendering API was different between templates (which returned strings) and widgets (which returned a RenderResult). In Marko 4, all render methods return RenderResults, so if you need a string, use `renderToString` which will still return a string after upgrading.
64 - `template.render(data)` without a callback → `template.renderToString(data)`
65 - `template.render(data, callback)` with a callback → `template.renderToString(data, callback)`
66 - `template.renderSync(data)` → `template.renderToString(data)`
67 - `widget.render(data)` without a callback → `widget.renderSync`
68
69Please note that you will need to deal with deprecations in any dependent modules as well before continuing with the upgrade process (usually this means updating dependencies).
70
71## Step 3 - Upgrade dependencies
72
73Before upgrading to Marko 4, it is recommended to make sure that your Marko-related dependencies are up-to-date. Many packages have versions that support both Marko 3 and Marko 4. If one of your dependencies doesn't have a version that supports both, you'll need to wait to upgrade it until you're upgrading Marko.
74
75After upgrading, run your application and tests to ensure that everything is still working as intended. If there are any issues, please refer to the changelogs of the modules you just upgraded to see if you need to make any changes within your app to accommodate the new versions.
76
77### Step 3.1 - Dependencies with widgets
78
79If you have any Marko components installed from npm, chances are at least one of them has a direct dependency on `marko@^3` or `marko-widgets@^6`. **This is bad.**
80
81Marko 4 for has legacy support for Marko 3 widgets, but if a dependency directly depends on an old version of `marko` or `marko-widgets`, it will try to use that old version and after your app is on Marko 4 this will cause all sorts of errors.
82
83You can run `npm ls marko marko-widgets` (or `yarn list marko marko-widgets`) to view any dependencies that have a direct dependency on either of these. Any packages that provide components will need to move these into `peerDependencies`.
84
85> NOTE: Some modules that have direct dependencies on Marko do not need to be updated, but as a general rule, they do.
86
87Let's take a look at what a `package.json` for a dependency _should_ look like (minus the comments, because that's not valid JSON 😉).
88
89```js
90{
91 // marko and marko-widgets are NOT here
92 "dependencies":{}
93
94 // use marko@3 and marko-widgets@6 for testing
95 "devDependencies":{
96 "marko": "^3",
97 "marko-widgets": "^6"
98 },
99
100 // use the app's version of marko/marko-widgets, but
101 // give a warning if it doesn't match the versions this
102 // package is compatible with (both Marko 3 and 4)
103 "peerDependencies": {
104 "marko": "^3 || ^4",
105 "marko-widgets": "^6 || ^7"
106 }
107}
108```
109
110#### `yarn` resolutions
111
112If you don't maintain the package that needs to move `marko` and/or `marko-widgets` to `peerDependencies`, you can make use of [yarn's selective dependency resolutions](https://yarnpkg.com/lang/en/docs/selective-version-resolutions/). Add the following to your `package.json` and it will force your dependencies to use the latest version of Marko:
113
114```js{5-8}
115{
116 "name": "appname",
117 "version": "0.0.0",
118 /* ... */
119 "resolutions": {
120 "**/marko": "^4",
121 "**/marko-widgets": "^7"
122 }
123}
124```
125
126## Step 4 - Upgrade marko
127
128Phew! With all the prep out of the way we're finally ready to upgrade `marko`!
129
130```
131npm install marko@^4 marko-widgets@^7
132```
133
134or
135
136```
137yarn upgrade marko@^4 marko-widgets@^7
138```
139
140If at this point you're thinking, "Wait... I thought Marko 4 didn't need `marko-widgets` any more...", you'd be correct. `marko-widgets@7` is just there to help with the migration. We'll remove it soon, but for now, there's still a bunch of calls to `require('marko-widgets').defineComponent` all over your app's code and we don't want that to throw saying it can't find the module.
141
142Now run your application and its tests. Marko 4 contains a legacy compatibility layer, so everything should still work! Congratulations, you've upgraded to Marko 4!
143
144You will however have noticed a swarm of deprecations in your console. We'll get to those.
145
146> NOTE: `marko-widgets@7` isn't tagged as latest, therefore `npm install marko-widgets` and `npm install marko-widgets@latest` will NOT get you to `7.x`.
147
148## Step 5 - Deal with Marko 4 deprecations
149
150Despite having a lot of deprecation warnings, the beauty is that you can deal with them on a template by template, component by component basis and keep a working app in between migrating each template/component.
151
152Additionally, any deprecation warnings that start with `MIGRATION` are automatically migratable by [`marko migrate`](https://github.com/marko-js/cli/blob/master/packages/migrate/README.md). Most migrations are 100% safe and will run automatically. However, there are a few migrations which are considered unsafe: they may only get you 90% of the way there. These migrations will prompt and ask if you want to run the migration. It is highly recommended to run these only on a single component at a time and then finish the migration manually using the guide below so that your app is always in a working state
153
154### 6.1 - Layouts
155
156#### `<layout-use>` → Layout components with `<@tags>` (or `import`)
157
158The layout taglib is no longer necessary in Marko 4 because components have the ability to easily recieve multiple blocks of content and can render those blocks whereever they like.
159
160You can also directly `import` a template by it's path much like `<layout-use>` and render it using the `<${dynamic}/>` syntax, but the recommended way to reference it is by creating components. You should move the layout into your `components/` directory and use it as any other component.
161
162**Old:**
163
164```
165src/
166 components/
167 layouts/
168 site-layout.marko
169 pages/
170 home/
171 template.marko
172```
173
174```marko
175<layout-use('../../layouts/site-layout.marko')>
176 <layout-put into="body">
177 Hello World
178 </layout-put>
179</layout-use>
180```
181
182**New (_automatically migratable_):**
183
184```marko
185import SiteLayout from '../../layouts/site-layout.marko';
186
187<${SiteLayout}>
188 <@body>
189 Hello World
190 </@body>
191</>
192```
193
194> NOTE: If you're using a layout from an npm package that requires you to reference it by its path, you can `import` it. However we recommend checking to see if there is a newer version of the package that exposes the layout as a component or updating the package to expose the layout as a component.
195
196**New (Recommended):**
197
198```
199src/
200 components/
201 site-layout.marko
202 pages/
203 home/
204 template.marko
205```
206
207```marko
208<site-layout>
209 <@body>
210 Hello World
211 </@body>
212</site-layout>
213```
214
215Related Docs: [Custom Tags](http://markojs.com/docs/custom-tags/)
216
217#### `<layout-placeholder>` → `<${dynamic}>`
218
219**Old:**
220
221```marko
222<!doctype>
223<html>
224<body>
225 <layout-placeholder name="body">
226 Default body content
227 </layout-placeholder>
228</body>
229</html>
230```
231
232**New (_automatically migratable_):**
233
234```marko
235<!doctype>
236<html>
237<body>
238 <if(input.body)>
239 <${input.body}/>
240 </if>
241 <else>
242 Default body content
243 </else>
244</body>
245</html>
246```
247
248##### Without fallback content
249
250**Old:**
251
252```marko
253<!doctype>
254<html>
255<body>
256 <layout-placeholder name="body"/>
257</body>
258</html>
259```
260
261**New (_automatically migratable_):**
262
263```marko
264<!doctype>
265<html>
266<body>
267 <${input.body}/>
268</body>
269</html>
270```
271
272Related Docs: [Body content](http://markojs.com/docs/body-content/)
273
274### 6.2 - Variables & Scripts
275
276#### `<script marko-init>` → `import`/`static`
277
278The `<script marko-init>` attribute is deprecated, but in its place you get ES Module `import` syntax and the `static` keyword. Anything after the `static` keyword is executed as JavaScript _when the template is loaded_.
279
280**Old:**
281
282```marko
283<script marko-init>
284 var capitalize = require('./util/caps');
285 var NAME = capitalize('Frank');
286</script>
287
288<div>${NAME}</div>
289```
290
291**New (_automatically migratable_):**
292
293```marko
294import capitalize from './util/caps';
295static var NAME = capitalize('Frank');
296
297<div>${NAME}</div>
298```
299
300Related Docs:
301
302- [Static JavaScript](http://markojs.com/docs/syntax/#static-javascript)
303- [Importing external files](http://markojs.com/docs/syntax/#importing-external-files)
304
305#### `<var>`/`<assign>`/`<invoke>` → `$`
306
307The `<var>` tag is deprecated, but in its place you get `$`. Similar to `static`, a line that begins with `$` will execute the JavaScript that follows _as a part of each render_.
308
309**Old:**
310
311```marko
312<var name="Frank"/>
313<assign name="John"/>
314<invoke console.log(name)/>
315<div>${name}</div>
316```
317
318**New (_automatically migratable_):**
319
320```marko
321$ var name = "Frank";
322$ name = "John";
323$ console.log(name);
324<div>${name}</div>
325```
326
327Related Docs: [Inline JavaScript](http://markojs.com/docs/syntax/#inline-javascript)
328
329### 6.3 - `w-*` Atrributes
330
331#### `w-id` → `key`
332
333**Old:**
334
335```marko
336<div w-id="foo"/>
337```
338
339**New:**
340
341```marko
342<div key="foo"/>
343```
344
345Related Docs: [The `key` attribute](http://markojs.com/docs/class-components/#key)
346
347#### `w-for` → `for:scoped`
348
349**Old:**
350
351```marko
352<label w-for="name">Name</label>
353<input type="text" w-id="name"/>
354```
355
356**New (_automatically migratable_):**
357
358```marko
359<label for:scoped="name">Name</label>
360<input type="text" id:scoped="name"/>
361```
362
363Related Docs: [The `:scoped` attribute modifier](http://markojs.com/docs/class-components/#scoped)
364
365#### `widget.elId` → `:scoped`
366
367You can use `:scoped` on any attribute to reference a scoped value. This value will be unique to this component instance and is useful for other attributes that take an `id` to reference, so you can use a scoped `id` instead.
368
369**Old:**
370
371```marko
372<button aria-describedby=widget.elId("tooltip")>...</button>
373<div w-id="tooltip" role="tooltip">...</div>
374```
375
376**New (_automatically migratable_):**
377
378```marko
379<button aria-describedby:scoped="tooltip">...</button>
380<div id:scoped="tooltip" role="tooltip">...</div>
381```
382
383Related Docs: [The `:scoped` attribute modifier](http://markojs.com/docs/class-components/#scoped)
384
385#### `w-preserve` → `no-update`
386
387**Old:**
388
389```marko
390<div w-preserve>
391 ...
392</div>
393```
394
395**New (_automatically migratable_):**
396
397```marko
398<div no-update>
399 ...
400</div>
401```
402
403#### `w-preserve-attrs` → `:no-update`
404
405**Old:**
406
407```marko
408<div class="foo" w-preserve-attrs="class">
409 ...
410</div>
411```
412
413**New (_automatically migratable_):**
414
415```marko
416<div class:no-update="foo">
417 ...
418</div>
419```
420
421#### `w-on-*` → `on-*()`
422
423**Old:**
424
425```marko
426<button w-on-click="handleClick">click me</button>
427```
428
429or
430
431```marko
432<button w-onClick="handleClick">click me</button>
433```
434
435**New (_automatically migratable_):**
436
437```marko
438<button on-click('handleClick')>click me</button>
439```
440
441The new syntax support binding additional arguments.
442
443Related Docs: [Listening to events](http://markojs.com/docs/events/#listening-to-events)
444
445### 6.4 Widgets → Components
446
447It's time to migrate your first legacy (Marko 3 style) widget to a Marko 4 component. Before you continue, please note that you'll need to go through all these steps for a given component. Partially migrated components will break your app. This is the section for which there are unsafe migrations provided by `marko migrate`. Again, these migrations should be run on a single component, and then follow the steps below to ensure the component is fully migrated.
448
449#### Remove `w-bind`
450
451`w-bind` is the indicator used by Marko to determine whether a component should operate in legacy mode. Marko 4 automatically binds the top level elements in a component, so `w-bind` is not necessary. Let's remove it. There's no turning back now...
452
453#### Rename widget methods
454
455- `this.getWidget``this.getComponent`
456- `this.getWidgets``this.getComponents`
457
458Related Docs:
459
460- [`getComponent()`](http://markojs.com/docs/class-components/#getcomponentkey-index)
461- [`getComponents()`](http://markojs.com/docs/class-components/#getcomponentskey-index)
462
463#### Component filename structure
464
465##### Traditional widget
466
467**Old:**
468
469Marko 3 widgets were traditionally structured as follows:
470
471```
472 components/
473 my-cool-component/
474 index.js → Widget Definition
475 template.marko → Widget Marko Template
476```
477
478Your `index.js` acts as the entry point for the component, contains a call to `require('marko-widgets').defineComponent` and requires `template.marko`.
479
480**New:**
481
482Marko 4 changes the filename structure and makes the template the entry point for the component:
483
484- `template.marko``index.marko` (the template)
485- `index.js``component.js` (component behavior for server/client)
486
487Thus a full split file based component in Marko 4 would be structured as follows:
488
489```
490 components/
491 my-cool-component/
492 component.js → Component Definition
493 index.marko → Component Marko Template
494```
495
496Marko 4 also introduces [single file components](http://markojs.com/docs/class-components/#single-file-components) within `index.marko`.
497
498##### Split renderer/widget
499
500**Old:**
501
502Marko 3 Split renderer/widgets were structured as follows:
503
504```
505 components/
506 my-cool-component/
507 renderer.js → Renderer Definition
508 template.marko → Widget Marko Template
509 widget.js → Widget Definition
510```
511
512Your `renderer.js` acts as the entry point for the component, contains a call to `require('marko-widgets').defineRenderer` and requires `template.marko`.
513
514Your `widget.js` should contain a call to `require('marko-widgets').defineWidget`.
515
516**New:**
517
518Marko 4 changes the filename structure and makes the template the entry point for the component:
519
520- `template.marko``index.marko` (the template)
521- `renderer.js``component.js` (component behavior for server/client)
522- `widget.js``component-browser.js` (component behavior for client only & causes `component.js` to be server only).
523
524Thus a full component in Marko 4 would be structured as follows:
525
526```
527 components/
528 my-cool-component/
529 component.js → Component Definition (Server)
530 component-browser.js → Component Definition (Browser)
531 index.marko → Component Marko Template
532```
533
534#### Remove all references to `marko-widgets`
535
536**Old:**
537
538As noted in file structure section above, Marko 3 used `marko-widgets` to define a component within each `index.js`. This was the entry point for the component and it required `template.marko` so it knew how to render itself.
539
540_index.js_
541
542```js
543module.exports = require("marko-widgets").defineComponent({
544 template: require("./template.marko")
545 // ...
546});
547```
548
549**New:**
550
551In Marko 4, `marko-widgets` is no longer necessary and `index.marko` becomes the component entry point so referencing the template from the `component.js` file is not necessary (and might cause circular dependency issues).
552
553_component.js_
554
555```js
556module.exports = {
557 // ...
558};
559```
560
561> NOTE: Once this step has been completed for all components in a project, you can remove `marko-widgets` as a dependency!
562
563#### `data` → `input`/`state`
564
565**Old:**
566
567In Marko 3, a template received a single `data` variable that contained the input data. In the case of a widget, the `getTemplateData` method could be used to combine the widget state with the widget input data into a single `data` object to be passed to the template.
568
569_index.js_
570
571```js
572// ...
573getTemplateData(state, input) {
574 return {
575 foo: state.foo,
576 bar: input.bar
577 }
578}
579// ...
580```
581
582_template.marko_
583
584```marko
585<ul>
586 <li>Foo: ${data.foo}</li>
587 <li>Bar: ${data.bar}</li>
588</ul>
589```
590
591**New:**
592
593In Marko 4, components are passed the input data as `input` and a separate `state` variable contains component state. This removes the need for `getTemplateData`! (And it is no longer called)
594
595_index.marko_
596
597```marko
598<ul>
599 <li>Foo: ${state.foo}</li>
600 <li>Bar: ${input.bar}</li>
601</ul>
602```
603
604_component.js_
605
606```js
607// getTemplateData is removed
608```
609
610> NOTE: `input` is aliased as `data`, so accessing `data` will still work, but it is recommended to use `input`. Accessing `data` will be officially deprecated at a later date.
611
612##### Data massaging
613
614If your `getTemplateData` has a lot of logic in it to transform the `state` or `input`, you'll probably want to retain that logic, but still remove the `getTemplateData` method.
615
616**Old:**
617
618_index.js_
619
620```
621// ...
622getTemplateData: function(state, input) {
623 var value = state.value;
624 var sign;
625
626 if (value < 0) {
627 sign = 'negative';
628 } else if (value > 0) {
629 sign = 'positive';
630 }
631
632 return {
633 value: value,
634 sign: sign
635 };
636 }
637// ...
638```
639
640_template.marko_
641
642```marko
643<div class=data.sign>
644 ${data.value}
645</div>
646```
647
648**New:**
649
650Instead of manipulating `input`/`state` before it makes it to the template, move the manipulation logic from `getTemplateData` into a helper function that can be imported into your template. This has the added benefit that it is now easy to write unit tests for any helper functions you might have.
651
652_helpers.js_
653
654```
655exports.getSign = function(value) {
656 var sign;
657
658 if (value < 0) {
659 sign = 'negative';
660 } else if (value > 0) {
661 sign = 'positive';
662 }
663
664 return sign;
665}
666```
667
668_index.marko_
669
670```marko
671import { getSign } from './helpers';
672
673<div class=getSign(state.value)>
674 ${state.value}
675</div>
676```
677
678#### Initializing state
679
680**Old:**
681
682In Marko 3, `input` was transient: it was only there for the first render (or when _new_ input was passed into a component). This meant that when your component re-rendered, if there was data that was in the `input` that was necessary for a re-render, you had to put it in `state` to make sure it got kept around.
683
684_index.js_
685
686```js
687// ...
688getInitialState(input) {
689 return {
690 count: input.initialCount || 0,
691 color: input.color
692 }
693}
694// ...
695```
696
697_template.marko_
698
699```marko
700<div style={ color:data.color }>
701 ${data.count}
702</div>
703```
704
705**New:**
706
707In Marko 4, `getInitialState` is no longer called. You can set initial state in `onCreate`. If you have some state that is derived from `input` and should be reset when the `input` changes, you can set it in `onInput`, but this should be a rare occurrence.
708
709Marko 4 keeps the original `input` around for subsequent renders, so you don't need to add `input` properties into the `state`. Only values that are controlled by the component should be put in `state`.
710
711_component.js_
712
713```js
714// ...
715onCreate(input) {
716 this.state = {
717 count: input.initialCount || 0
718 };
719}
720// ...
721```
722
723_index.marko_
724
725```marko
726<div style={ color:input.color }>
727 ${state.count}
728</div>
729```
730
731> NOTE: From within the component you can access `this.state` as well as `this.input`
732
733Related Docs:
734
735- [`onCreate`](http://markojs.com/docs/class-components/#oncreateinput-out)
736- [`onInput`](http://markojs.com/docs/class-components/#oninputinput-out)
737- [`this.state`](http://markojs.com/docs/class-components/#thisstate)
738- [`this.input`](http://markojs.com/docs/class-components/#thisinput)
739
740#### Browser initialization
741
742**Old:**
743
744In Marko 3, the `init` method was used to set things up in the browser and was the first time the DOM for the component was ready since `init` was called immediately after _mounting_ the component to the DOM.
745
746The `getWidgetConfig` method was used to create a `config` object that would be serialized and sent to the browser to be used in the `init` method. This was necessary because `input` was not available in `init`.
747
748_index.js_
749
750```js
751// ...
752init(config) {
753 $(this.el).dataTable(config);
754}
755getWidgetConfig(input) {
756 return {
757 paginate: input.paginate,
758 scrollY: input.scrollY
759 };
760}
761// ...
762```
763
764**New:**
765
766`init` has been renamed to the more appropriate `onMount` which better describes where in a component's lifecycle it is called. `getWidgetConfig` is no longer necessary (or called) because we can access `this.input`.
767
768_component.js_
769
770```js
771// ...
772onMount() {
773 $(this.el).dataTable({
774 paginate: this.input.paginate,
775 scrollY: this.input.scrollY
776 });
777}
778// ...
779```
780
781> NOTE: If you need values from `out`, you can grab them in `onCreate`, attach them to the component instance and access them in `onMount`:
782>
783> ```js
784> //...
785> onCreate(input, out) {
786> this.value = out.global.value;
787> }
788> onMount() {
789> console.log(this.value)
790> }
791> // ...
792> ```
793
794Related Docs: [`onMount`](http://markojs.com/docs/class-components/#onmount)
795
796#### Widget body
797
798**Old:**
799
800```js
801// ...
802getInitialBody(input, out) {
803 return input.renderBody || input.label
804}
805//
806```
807
808```marko
809<button>
810 <w-body/>
811</button>
812```
813
814**New:**
815
816```marko
817<button>
818 <if(input.renderBody)>
819 <${input.renderBody}/>
820 </if>
821 <else>
822 ${input.label}
823 </else>
824</button>
825```
826
827#### `getInitialProps(input, out)`
828
829**Old:**
830
831This method existed because `input` was passed to `getInitialState`, `getInitialBody`, `getWidgetConfig` and `getTemplateData`. If the input needed to be transformed, `getInitialProps` allowed you to do it in a single place.
832
833**New:**
834
835`getInitialProps` is no longer called. If you need to transform your input, move that logic into helper methods or another appropriate location.
836
837#### Lifecycle methods
838
839A few of these have already been covered:
840
841- `init``onMount`
842- `getWidgetConfig``onMount`/`onCreate`
843- `getInitialState``onCreate`
844- `getTemplateData` ➔ (no longer needed)
845- `getInitialProps` ➔ (no longer needed)
846- `getInitialBody` ➔ (no longer needed)
847
848##### `onRender`
849
850The legacy `onRender` method was called with `firstRender === true` [immediately after mounting the widget in the DOM](https://github.com/marko-js/marko-widgets/blob/5b226b9ede8227bb68e16fa8316ef3058daf3d06/lib/init-widgets-browser.js#L205-L209).
851
852Subsequent calls `onRender` occurred [immediately after calls to `onUpdate`](https://github.com/marko-js/marko-widgets/blob/5b226b9ede8227bb68e16fa8316ef3058daf3d06/lib/init-widgets-browser.js#L195-L196).
853
854This behavior did not align with where the actual render takes place (it actually occurs before mounting and before updating the DOM). So we've changed its behavior in Marko 4. If you were using `onRender` in Marko 3, use `onMount` or `onUpdate` instead.
855
856- `onRender` (first render) ➔ `onMount`
857- `onRender` (subsequent renders) ➔ `onUpdate`
858
859##### `onBeforeUpdate` and `onUpdate`
860
861- `onBeforeUpdate``onUpdate`/`onRender`
862- `onUpdate``onUpdate`
863
864The `onUpdate` is called after DOM updates have been made. The `onRender` method is now called before rendering, so it can replace some use-cases of `onBeforeUpdate`.
865
866##### `onBeforeDestroy` and `onDestroy`
867
868- `onBeforeDestroy``onDestroy`
869- `onDestroy``onDestroy`
870
871The `onDestroy` is now called immediately before destroying the DOM associated with a component.
872
873See how the [legacy adaptor remaps these methods](https://github.com/marko-js/marko/blob/373a7ceb52b60454a9661fc94a3c9935e5a7dbfa/src/components/legacy/defineWidget-legacy-browser.js#L72-L102).
874
875Related Docs: [Lifecycle events](http://markojs.com/docs/class-components/#lifecycle-events)
876
877#### Fin.
878
879👍🎉 You've fully migrated your first component! 🎉👍
880
881Repeat this process for each component in your app. As you get familiar with "Thinking in Marko 4" each one will be easier. And remember, you should have a working application after converting each individual component, so you don't have to do it all at once.