Pro Yearly is on sale from $80 to $50! »

Modern Ember

Modern Ember

An update on the current state and future of Ember. Presented at the Ember Israel Meetup.

Note: Large portions of this deck were borrowed (with permission from Tom Dale) from the EmberConf 2018 keynote.

E01ec1de2f7783812d2235a6a9aaaeea?s=128

Dan Gebhardt

August 27, 2018
Tweet

Transcript

  1. modern ember dan gebhardt @dgeb ember israel meetup 2018.08.27

  2. None
  3. None
  4. WHY EMBER?

  5. WHY EMBER IN 2018?

  6. None
  7. None
  8. OUR VALUES • Convention over Configuration • Stability without Stagnation

    • Climb the Mountain Together
  9. None
  10. None
  11. modern ember • Fewer Concepts • Latest JavaScript • Optimize

    for Success
  12. All features shown today are available in stable, beta, or

    canary releases. Some are behind a feature flag and subject to change. modern ember * *
  13. None
  14. STANDARD FILE LAYOUT

  15. src/ ui/ components/ action-bar/ component.js template.hbs user-profile/ component.js template.hbs

  16. src/ ui/ components/ action-bar/ component.js template.hbs action-bar-item/ component.js template.hbs user-profile/

    component.js template.hbs
  17. JS MODULES

  18. import Component from '@ember/component'; import { computed } from '@ember/object';

    export default class extends Component { fullName: computed(/* … */) }
  19. None
  20. None
  21. UNLOCKED OPTIMIZATIONS • Strip assertions & other debug functions •

    Treeshaking
  22. JS CLASSES

  23. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } sayHello() { console.log(`${this.firstName} ${this.lastName}`); } }
  24. DECORATORS

  25. export default class extends Component { @service currentUser; constructor(...args) {

    super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } didInsertElement() { tab(this.element); } @computed get tabCount() { return this.tabs.length; } }
  26. CLASS FIELDS

  27. export default class extends Component { @service currentUser; constructor(...args) {

    super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } update(title) { this.set('title', title); } didInsertElement() { tab(this.element); } @computed get tabCount() { return this.tabs.length; } }
  28. export default class extends Component { @service currentUser; title =

    'Default Title'; update(title) { this.set('title', title); } didInsertElement() { tab(this.element); } @computed get tabCount() { return this.tabs.length; } }
  29. None
  30. TYPESCRIPT export default class extends Component { @service currentUser: User;

    title = 'Default Title'; tabs: Tab[]; constructor(...args) { super(...args); console.log('constructing!'); } didInsertElement() { tab(this.element); } @computed get tabCount() { return this.tabs.length; } }
  31. None
  32. TESTING

  33. None
  34. TESTS • Unit • Integration • Acceptance

  35. import { module, test } from 'qunit'; import { setupTest

    } from 'ember-qunit'; module('Unit | Service | location', function(hooks) { setupTest(hooks); // Replace this with your real tests. test('it exists', function(assert) { let service = this.owner.lookup('service:location'); assert.ok(service); }); }); EXAMPLE UNIT TEST BLUEPRINT
  36. import { module, test } from 'qunit'; import { setupApplicationTest

    } from 'ember-qunit'; import { visit, fillIn, click } from '@ember/test-helpers'; module('Acceptance | posts', function(hooks) { setupApplicationTest(hooks); test('should add new post', async function(assert) { await visit('/posts/new'); await fillIn('input.title', 'My new post'); await click('button.submit'); const title = this.element.querySelector('ul.posts li:first').textContent; assert.equal(title, 'My new post'); }); }); EXAMPLE ACCEPTANCE TEST
  37. DEPENDENCIES

  38. EMBER ADDONS

  39. None
  40. None
  41. NPM PACKAGES

  42. Step 1. $ npm install --save axios Step 2. import

    axios from 'axios'; Step 3. There is no step 3.
  43. OPTIONAL JQUERY

  44. import Component from '@ember/component'; export default Component.extend({ didInsertElement() { let

    iframe = this.element.querySelector('iframe'); let message = { name: 'didInsertElement' }; iframe.contentWindow.postMessage(message, '*'); } });
  45. None
  46. None
  47. None
  48. None
  49. Preact Glimmer

  50. METRICS • First Meaningful Paint • Rehydration Time

  51. 2.5s 2.9s First Meaningful Paint Glimmer Preact 90th percentile, lower

    numbers are better
  52. 2.5s 4.1s 2.9s 4.3s Rehydration First Meaningful Paint Glimmer Preact

    90th percentile, lower numbers are better
  53. • Binary Bytecode • Server-side Rendering • Repairing Rehydration •

    Async Rendering
  54. HBS HBS HBS 110110 010010 101011 001010 GBX compiles

  55. ASYNC RENDERING

  56. None
  57. SERVER SIDE RENDERING

  58. Glimmer 2.5s 4.1s Glimmer (no SSR) 4.0s 4.0s First Meaningful

    Paint Fully Interactive 90th percentile, lower numbers are better
  59. None
  60. TRACKED FIELDS

  61. export default class extends Component { @service currentUser; title =

    'Default Title'; update(title) { this.set('title', title); } didInsertElement() { tab(this.element); } @computed get tabCount() { return this.tabs.length; } }
  62. export default class extends Component { @service currentUser; @tracked title

    = 'Default Title'; update(title) { this.title = title; } didInsertElement() { tab(this.element); } @computed get tabCount() { return this.tabs.length; } }
  63. export default class extends Component { @service currentUser; @tracked title

    = 'Default Title'; update(title) { this.title = title; } didInsertElement() { tab(this.element); } @computed get tabCount() { return this.tabs.length; } }
  64. export default class extends Component { @service currentUser; @tracked title

    = 'Default Title'; update(title) { this.title = title; } didInsertElement() { tab(this.element); } @tracked get tabCount() { return this.tabs.length; } } // TBD: tracked vs. computed
  65. <UserProfile> {{user-profile}} ANGLE BRACKET COMPONENTS

  66. {{#each options as |option|}} {{radio-button action="select" option=option selected=selected}} {{/each}} {{#each

    options as |option|}} <RadioButton @action="select" @option={{option}} @selected={{selected}} /> {{/each}}
  67. {{#each options as |option|}} {{radio-button action="select" option=option selected=selected}} {{/each}} {{#each

    options as |option|}} <Radio @action="select" @option={{option}} @selected={{selected}} /> {{/each}}
  68. <Dialog @title="Are you sure?" @isOpen={{true}}> You have unsaved changes. Do

    you want to leave? </Dialog> {{#if @isOpen}} <div class="dialog"> <h3>{{@title}}</h3> <div>{{yield}}</div> </div> {{/if}}
  69. <Dialog @title="Are you sure?" @isOpen={{true}}> You have unsaved changes. Do

    you want to leave? </Dialog> {{#if @isOpen}} <div class="dialog"> <h3>{{@title}}</h3> <div>{{yield}}</div> </div> {{/if}}
  70. PERFORMANCE

  71. None
  72. +

  73. Stack Registers Executable Heap Opcodes Constants References Validators Component Managers

    DOM VM
  74. Stack Registers Executable Heap Opcodes Constants References Validators Component Managers

    DOM VM
  75. None
  76. ❤ COMMUNITY

  77. None
  78. None
  79. None
  80. None
  81. None
  82. RESOURCES

  83. https://www.youtube.com/watch?v=NhtpXs0ZtUc

  84. https://glimmerjs.com

  85. https://codingitwrong.com/2018/08/16/modern-ember.html

  86. https://github.com/emberjs/rfcs/pull/364

  87. Thanks! dan gebhardt @dgeb ember israel meetup 2018.08.27