EmberConf 2018 Keynote

9bf3a766e037b9d5a4da0a6f9d0f4f68?s=47 tomdale
March 14, 2018

EmberConf 2018 Keynote

9bf3a766e037b9d5a4da0a6f9d0f4f68?s=128

tomdale

March 14, 2018
Tweet

Transcript

  1. Tom: Welcome to EmberConf 2018! This is our 5th anniversary,

    and we couldn't be more excited to be with you here today. We have a lot to cover, so let's dive right into it.
  2. CORE TEAM Yehuda: I'm very happy to announce… <transition>

  3. CORE TEAM Yehuda: …Melanie Sumner is joining the core team.

    Melanie has done great work modernizing the Ember website to make it accessible, and generally making accessibility a front and center concern across the project. As a JP Morgan Chase employee, she has also served as a tireless representative of the many Ember developers working inside of large enterprises. I'm very excited to have her join the team.
  4. CORE TEAM MELANIE SUMNER Yehuda: …Melanie Sumner is joining the

    core team. Melanie has done great work modernizing the Ember website to make it accessible, and generally making accessibility a front and center concern across the project. As a JP Morgan Chase employee, she has also served as a tireless representative of the many Ember developers working inside of large enterprises. I'm very excited to have her join the team.
  5. YEARS 7 Tom: This is the fifth anniversary of EmberConf,

    but Ember itself will be seven years old in April.
  6. A framework for creating ambitious web applications. Tom: We've always

    described Ember as being a framework for creating ambitious web applications. It's even the first thing you see on the home page. But there's maybe an even better way of saying this.
  7. A framework for web developers. ambitious Tom: Ember is really

    for ambitious developers. Ember is not the code, which has changed radically over the last 7 years. The defining feature of Ember has always been our community.
  8. OUR VALUES • Convention over Configuration • Stability without Stagnation

    • Climb the Mountain Together Yehuda: If you're an Ember user, it's because you value productivity over endless tinkering. You value shipping features over rewrites. You believe that we can solve problems better by solving them together, because your app is not a special snowflake. And you know the value of our community moving together as one. To us, these ideas seem like common sense. But they run counter to a lot of the conventional wisdom of the JavaScript community today.
  9. Yehuda: Over Ember's 7 years, our community has come together

    to build a cohesive set of tools that are designed to last. There's nothing else like it in JavaScript. One of the benefits of being an Ember user is feeling confident that, even as the web changes, your tools are kept up-to-date and tested to work together. Tom: And it goes beyond the framework itself. We have a huge ecosystem of addons that extend Ember while preserving the same belief in stability and productivity. The truth is that we really love Ember. We're proud of Ember. When it came time to think about what our focus should be for the future, it was obvious: we don't want to change the fundamentals. We just want to build a better Ember. (Yehuda clicks)
  10. 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9

    1.10 1.11 1.12 1.13 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 2.12 2.13 2.14 2.15 2.16 2.17 Yehuda: Over Ember's 7 years, our community has come together to build a cohesive set of tools that are designed to last. There's nothing else like it in JavaScript. One of the benefits of being an Ember user is feeling confident that, even as the web changes, your tools are kept up-to-date and tested to work together. Tom: And it goes beyond the framework itself. We have a huge ecosystem of addons that extend Ember while preserving the same belief in stability and productivity. The truth is that we really love Ember. We're proud of Ember. When it came time to think about what our focus should be for the future, it was obvious: we don't want to change the fundamentals. We just want to build a better Ember. (Yehuda clicks)
  11. 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9

    1.10 1.11 1.12 1.13 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 2.12 2.13 2.14 2.15 2.16 2.17 Yehuda: So that's what the focus of the 3.0 series is. (continues next slide)
  12. 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9

    1.10 1.11 1.12 1.13 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 2.12 2.13 2.14 2.15 2.16 2.17 3.0 Yehuda: So that's what the focus of the 3.0 series is. (continues next slide)
  13. TOGETHER, WE'RE GOING TO BUILD A BETTER EMBER. Together, we're

    going to build a better Ember.
  14. BUILDING A BETTER EMBER • Fewer Concepts • Latest JavaScript

    • Optimize for Success Yehuda: Fundamentally, there are three broad themes that we've heard people say they want to focus on: 1. First, to reduce the number of concepts people have to learn to start feeling productive in Ember. 2. Second, to take advantage of new features in JavaScript as they land in browsers. 3. Third, to give people tools to be even more successful. Our APIs should help people fall into a "pit of success." But we can't do it without your help. If we want to make this happen, we need to rally the energy of the community.
  15. 2019 Tom: So I want to share with you our

    vision for what using Ember in 2019 could be like. By knowing where we want to go, together we can figure out the best way to get there.
  16. NEW FILE LAYOUT Yehuda: The first thing we want to

    see is clearer, more conventional file system layout that replaces both the default file system layout and the experimental pods layout. As a community that prides ourselves on conventions and shared solutions, it's time to roll the learnings from the pods experiment into the default Ember experience.
  17. app/ components/ action-bar.js user-profile.js templates/ components/ action-bar.hbs user-profile.hbs Yehuda: In

    the current default file system layout, components and their corresponding templates live in two different directories. The process of switching from one to the other in your editor can be tedious and error-prone.
  18. app/ components/ action-bar.js user-profile.js templates/ components/ action-bar.hbs user-profile.hbs Yehuda: In

    the current default file system layout, components and their corresponding templates live in two different directories. The process of switching from one to the other in your editor can be tedious and error-prone.
  19. Yehuda: As your app gets bigger, this can lead to

    a lot of annoying scrolling just to switch from a component class to its template.
  20. src/ ui/ components/ action-bar/ component.js template.hbs user-profile/ component.js template.hbs Yehuda:

    In the new layout, heavily inspired by pods, we keep related files next to each other. Not only does this make switching back and forth easier, it makes the structure of the entire application clearer.
  21. src/ ui/ components/ action-bar/ component.js template.hbs user-profile/ component.js template.hbs Yehuda:

    In the new layout, heavily inspired by pods, we keep related files next to each other. Not only does this make switching back and forth easier, it makes the structure of the entire application clearer.
  22. Yehuda: Best of all, thanks to Ember's strong conventions, we

    can build tools to painlessly migrate existing apps to the modernized layout. I really love codemods!
  23. Yehuda: Best of all, thanks to Ember's strong conventions, we

    can build tools to painlessly migrate existing apps to the modernized layout. I really love codemods!
  24. OPTIONAL JQUERY Tom: Speaking of modernization, let's talk about jQuery.

    Ember 1.0 supported IE7, which was still a popular web browser at the time. In this era, we needed to work around a large number of cross-browser quirks, and jQuery was the best tool for doing that. However, more and more people are building Ember apps using just the native DOM API.
  25. import Component from '@ember/component'; export default Component.extend({ didInsertElement() { let

    iframe = this.element.querySelector('iframe'); let message = { name: 'didInsertElement' }; iframe.contentWindow.postMessage(message, '*'); } }); Tom: For example, this component doesn't use jQuery and instead uses the native querySelector method to find a child iframe element, and then uses the native postMessage API to send a message to it. In fact, ever since we moved to the Glimmer VM rendering engine, Ember itself does not use jQuery at all in the view layer.
  26. Tom: We're big fans of jQuery and think it's the

    right choice for many apps. At the same time, we want Ember to be as approachable to new developers as possible. We don't want them to think they have to learn jQuery to use Ember.
  27. Tom: It might also surprise you to learn that jQuery

    is one of the largest contributors to Ember’s default file size. jQuery is over 30k, even after minification and gzip. If we can make jQuery optional, the out-of-the-box file size of an Ember app will drop by one fifth.
  28. Tom: It might also surprise you to learn that jQuery

    is one of the largest contributors to Ember’s default file size. jQuery is over 30k, even after minification and gzip. If we can make jQuery optional, the out-of-the-box file size of an Ember app will drop by one fifth.
  29. SIMPLIFIED TESTING TODO: Tone shift for wycats personalization Yehuda: To

    write software that lasts, you need to have tests that give you the confidence to make changes. Every new Ember app comes with integrated, end-to-end testing infrastructure, and our community has a fantastic testing culture. Testing in Ember is awesome. But we can make tests even better by taking advantage of new JavaScript features like async functions. And we can make it even easier for people who want to use testing libraries like Jest or Mocha instead of QUnit.
  30. this.$('.form-submit').click(); Yehuda: The first thing we can do is actually

    also related to jQuery. In my own app, we don't use jQuery that much in our components, but it's still very common in our tests. And this is true about many apps I have seen. It would take more time to migrate tests to stop using jQuery than app code.
  31. await click('.form-submit'); this.$('.form-submit').click(); Yehuda: So step 1 is to transform

    all the places we use jQuery in our tests to use new helpers that use native DOM API.
  32. it('redirects to signin when not authenticated', function () { invalidateSession(application);

    visit('/'); andThen(function () { expect(currentURL()).to.equal('/signin'); }); }); Yehuda: Step 2 is cleaning up our tests with async functions. Often, we want to write tests that rely on asynchronous tasks finishing. This kind of code can be difficult to read and write in JavaScript, so our existing test helpers are asynchronous by default and we introduced the `andThen` helper to wait for asynchronous code to finish before moving on to the next part of the test. However, now that async functions are in JavaScript, we have a built-in way to tell a function to pause until some asynchronous task has finished. If we make all of our helpers return promises, they work seamlessly with async functions.
  33. it('redirects to signin when not authenticated', function () { invalidateSession(application);

    visit('/'); andThen(function () { expect(currentURL()).to.equal('/signin'); }); }); it('redirects to signin when not authenticated', async function() { invalidateSession(application); await visit('/'); expect(currentURL()).to.equal('/signin'); }); Yehuda: In this example, we've turned this test into an async function. Because these test helpers return promises, we can use the await keyword to wait for rendering to finish before continuing with the test. That means that we handle asynchrony the same in our tests and our app.
  34. Yehuda: Best of all, we can help the community move

    to this better way of doing things with a codemod, which can update many tests automatically to use async functions and migrate tests using jQuery to the new testing helpers.
  35. Yehuda: Best of all, we can help the community move

    to this better way of doing things with a codemod, which can update many tests automatically to use async functions and migrate tests using jQuery to the new testing helpers.
  36. Yehuda: Finally, although we're big fans of QUnit, Ember is

    a big tent and we want to work with whatever testing libraries people prefer. By simplifying the API, we can can make the integration with other testing frameworks like Mocha more reliable and first class.
  37. JS MODULES Tom: Ember was an early adopter of JavaScript

    modules. In fact, Yehuda helped drive their design through TC39, and Ember CLI has used the module syntax since before 1.0.
  38. import Ember from 'ember'; const { Component, computed } =

    Ember; export default Component.extend({ fullName: computed(/* … */) }); Tom: This is what a typical file looks like in Ember 2.0. Each file in an Ember app is a module, and Ember itself is exposed as a single module with everything in it. This works, but the beauty of JavaScript modules is that they let you just import the things that you need.
  39. import Component from '@ember/component'; import { computed } from '@ember/object';

    export default Component.extend({ fullName: computed(/* … */) }); Tom: We want Ember users to be able import just the parts of the framework they need for that file. In this small example, it doesn’t seem like much of a difference, but…
  40. import Ember from 'ember'; const { Component, on, warn, makeArray,

    getOwner, inject: { service } } = Ember; export default Component.extend({ store: service(), trackRender: on(function() { warn('tracking render'); let times = makeArray([performance.now()]); let owner = getOwner(this); owner.lookup('analytics:main').track(times); }, 'didInsertElement') }); Tom: But in more complicated files, this monolithic global hides the fact that Ember is made up of separate packages under the hood. This can lead people to feeling like Ember is overwhelming to learn.
  41. Tom: Your API is how people think about your library,

    so this overwhelming API lead to documentation that also felt overwhelming. Even though you only need a handful of concepts day-to-day, this overwhelming list was the first thing people would see when they went to get help.
  42. Tom: Your API is how people think about your library,

    so this overwhelming API lead to documentation that also felt overwhelming. Even though you only need a handful of concepts day-to-day, this overwhelming list was the first thing people would see when they went to get help.
  43. Tom: Your API is how people think about your library,

    so this overwhelming API lead to documentation that also felt overwhelming. Even though you only need a handful of concepts day-to-day, this overwhelming list was the first thing people would see when they went to get help.
  44. Tom: Your API is how people think about your library,

    so this overwhelming API lead to documentation that also felt overwhelming. Even though you only need a handful of concepts day-to-day, this overwhelming list was the first thing people would see when they went to get help.
  45. Tom: One of the best benefits of JavaScript modules, in

    my opinion, is that it allows us to orient our API documentation around these packages, so they're naturally grouped by functionality. This makes finding documentation easier as well as giving people a much better intuition about the high-level features of Ember.
  46. Tom: One of the best benefits of JavaScript modules, in

    my opinion, is that it allows us to orient our API documentation around these packages, so they're naturally grouped by functionality. This makes finding documentation easier as well as giving people a much better intuition about the high-level features of Ember.
  47. Tom: Now, you might imagine that going through every file

    in your app and refactoring it to use the module API would be tedious and time consuming. And if you had to do it by hand, you'd be right. • But the best part about this feature might be that this process could be fully automated. In fact, going forward, any time Ember changes something that requires sweeping changes to your app, we should create a codemod to help you migrate.
  48. Tom: Now, you might imagine that going through every file

    in your app and refactoring it to use the module API would be tedious and time consuming. And if you had to do it by hand, you'd be right. • But the best part about this feature might be that this process could be fully automated. In fact, going forward, any time Ember changes something that requires sweeping changes to your app, we should create a codemod to help you migrate.
  49. UNLOCKED OPTIMIZATIONS • Strip assertions & other debug functions •

    Treeshaking Tom: In addition to the learning benefits, modules unlock the ability to produce better-optimized apps. We can detect when you use debugging utilities and strip them automatically in production builds. We can also use treeshaking to include just the parts of the framework you use in the compiled output.
  50. EDITOR INTEGRATION Yehuda: One thing we really want to see

    is great integration with editors. Getting feedback as you type reinforces good patterns and makes learning faster.
  51. Yehuda: For example, if you've moved your app to the

    JS module API we just talked about, it would be easy to fall back into old habits and use the old API. But, if we offer great editor integration out of the box, we can catch this right away and give you a warning right there in your editor.
  52. Yehuda: For example, if you've moved your app to the

    JS module API we just talked about, it would be easy to fall back into old habits and use the old API. But, if we offer great editor integration out of the box, we can catch this right away and give you a warning right there in your editor.
  53. Yehuda: We can also take advantage of the fact that

    Handlebars is a first-class programming language and build first-class tools on top of it. In this example, our editor is able to automatically discover and suggest the name of the component as I'm typing, because it understands Ember's file system layout. Ember is made up of many individual packages, but pulling all of those pieces into a single, integrated editor experience gives users something even greater than the sum of the parts.
  54. Yehuda: We can also take advantage of the fact that

    Handlebars is a first-class programming language and build first-class tools on top of it. In this example, our editor is able to automatically discover and suggest the name of the component as I'm typing, because it understands Ember's file system layout. Ember is made up of many individual packages, but pulling all of those pieces into a single, integrated editor experience gives users something even greater than the sum of the parts.
  55. OBJECT MODEL Tom: Ember's object model is the heart of

    the framework. Everything else builds on top of this foundation. Improvements that we make to the object model can unlock many downstream improvements in other APIs.
  56. COMPUTED PROPERTIES Tom: First, let's look at computed properties, perhaps

    the most popular feature in the object model.
  57. let person = { firstName: 'Godfrey', lastName: 'Chan', get fullName()

    { return this.firstName + ' ' + this.lastName; } }; person.fullName; // => 'Godfrey Chan' Tom: ES5 added a syntax for computed properties. But when Ember first came out, ES5 was not available in many of our supported browsers.
  58. const Person = EmberObject.extend({ fullName: computed('firstName', 'lastName', function() { return

    `${this.get('firstName')} ${this.get('lastName')}`; }) }); let chancancode = Person.create({ firstName: 'Godfrey', lastName: 'Chan' }); chancancode.get('fullName'); // => 'Godfrey Chan' Tom: If you think about how many computed properties you use in your apps, it’s hard to imagine us not having this feature. So we had to create our own computed properties using the features available in ES3.
  59. const Person = EmberObject.extend({ fullName: computed('firstName', 'lastName', function() { return

    `${this.get('firstName')} ${this.get('lastName')}`; }) }); let chancancode = Person.create({ firstName: 'Godfrey', lastName: 'Chan' }); chancancode.get('fullName'); // => 'Godfrey Chan' Tom: That said, we noticed that forgetting to use `.get` trips people up as they learn Ember. It feels really frustrating until you develop the muscle memory to use it everywhere.
  60. const Person = EmberObject.extend({ fullName: computed('firstName', 'lastName', function() { return

    `${this.firstName} ${this.lastName}`; }) }); let chancancode = Person.create({ firstName: 'Godfrey', lastName: 'Chan' }); chancancode.fullName; // => 'Godfrey Chan' Tom: What if we could stop requiring the use of `get()`? This would make Ember so much easier to learn for people who already know JavaScript.
  61. const Person = EmberObject.extend({ fullName: computed('firstName', 'lastName', function() { return

    `${this.firstName} ${this.lastName}`; }) }); let chancancode = Person.create({ firstName: 'Godfrey', lastName: 'Chan' }); let { firstName, lastName, fullName } = chancancode; Tom: This also means that Ember's computed properties work with new language syntax, like destructuring, because they're just normal JavaScript properties.
  62. JS CLASSES Yehuda: Classes are another feature in JavaScript that

    didn't exist when we started Ember.
  63. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } sayHello() { console.log(`${this.firstName} ${this.lastName}`); } } Yehuda: ES6 added a class syntax to JavaScript. This is an example of a Person class that has a firstName and lastName property and a method called sayHello.
  64. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { } Yehuda: So let's try to take an Ember.Object subclass and rewrite it using native class syntax.
  65. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { constructor(...args) { super(...args); console.log('constructing!'); } } Yehuda: First, we're going to move everything in the init method on the left side into the constructor on the right.
  66. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { constructor(...args) { super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } } Yehuda: Classes don't support initializing properties directly, so we will need to set the title property inside the constructor on the right side.
  67. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { constructor(...args) { super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } didInsertElement() { tab(this.element); } } Yehuda: Next we're going to move the didInsertElement method, which is straightforward.
  68. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { constructor(...args) { super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } didInsertElement() { tab(this.element); } } Yehuda: Next, let's move the tabCount computed property. But… hold on…
  69. Yehuda: The problem is that, at TC39, we intentionally shipped

    a "maximally minimal" class syntax in ES6 to get started, and it is missing important features needed to migrate the richer Ember class model to standard classes. There's no way for us to express things like computed properties inside the class body.
  70. OH NO Yehuda: The problem is that, at TC39, we

    intentionally shipped a "maximally minimal" class syntax in ES6 to get started, and it is missing important features needed to migrate the richer Ember class model to standard classes. There's no way for us to express things like computed properties inside the class body.
  71. @decorators Yehuda: But good news. We didn't stop with max-min

    classes, and TC39 has been busy adding important features based on developer feedback. Decorators are a proposed new feature that allows developers to implement features like computed properties inside of class bodies. Let's take a look at how we could finish the port with decorators.
  72. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { constructor(...args) { super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } didInsertElement() { tab(this.element); } @computed('tabs.[]') get tabCount() { return this.tabs.length; } } Yehuda: Now, with the computed decorator, we can say that this getter is a computed property, and tell Ember what its dependent keys are. While we're at it, let's remove the unnecessary .get() from the tabCount implementation.
  73. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { @service currentUser; constructor(...args) { super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } didInsertElement() { tab(this.element); } @computed('tabs.[]') get tabCount() { return this.tabs.length; } } Yehuda: And finally, we can move our service declaration over as well.
  74. export default class extends Component { @service currentUser; constructor(...args) {

    super(...args); this.set('title', 'Default Title'); console.log('constructing!'); } didInsertElement() { tab(this.element); } @computed('tabs.[]') get tabCount() { return this.tabs.length; } } Yehuda: Isn't this beautiful? I have been waiting a long time for classes to get to the point where we can put this slide on the screen.
  75. CLASS FIELDS Tom: Decorators aren't the only improvement coming to

    classes.
  76. export default Component.extend({ currentUser: service(), title: 'Default Title', init() {

    this._super(...arguments); console.log('constructing!'); }, didInsertElement() { tab(this.element); }, tabCount: computed('tabs.[]', function() { return this.get('tabs.length'); }) }); EMBER CLASS JS CLASS export default class extends Component { @service currentUser; title = 'Default Title'; constructor(...args) { super(...args); console.log('constructing!'); } didInsertElement() { tab(this.element); } @computed('tabs.[]') get tabCount() { return this.tabs.length; } } Tom: In the previous example, we had to move initializing the title property into the constructor, as the first iteration of classes didn't allow declaring fields in the class body. However, the class fields proposal gives us the ability to declare and initialize properties with a convenient syntax that is quite similar to the Ember syntax.
  77. Tom: All of these features will add up to a

    really nice experience for Ember developers who want to use TypeScript.
  78. 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('tabs.[]') get tabCount() { return this.tabs.length; } } Tom: JavaScript classes would make it much easier to adopt TypeScript in your app, too. With class fields, we can tell TypeScript the types of our `currentUser` and `tabs` properties.
  79. Yehuda: We think that people should be able to get

    a great Ember experience using TypeScript, but we will always design Ember JavaScript-first, and we would never make TypeScript mandatory or make any part of the Ember runtime rely on TypeScript support.
  80. Yehuda: One of the things I love about TypeScript is

    the detailed autocomplete it brings to your editor. Especially if I'm writing code that relies on another file, it's so helpful to be reminded what properties and methods are available as I type. <click>So if I'm writing a method in my component and I need to get some data from another service, I don't have to switch files to see what the property is called— TypeScript shows me right there as I type. And JavaScript users benefit when Ember uses TypeScript, because editors can use the TypeScript information to provide autocomplete and inline documentation, even in JavaScript files.
  81. Yehuda: One of the things I love about TypeScript is

    the detailed autocomplete it brings to your editor. Especially if I'm writing code that relies on another file, it's so helpful to be reminded what properties and methods are available as I type. <click>So if I'm writing a method in my component and I need to get some data from another service, I don't have to switch files to see what the property is called— TypeScript shows me right there as I type. And JavaScript users benefit when Ember uses TypeScript, because editors can use the TypeScript information to provide autocomplete and inline documentation, even in JavaScript files.
  82. FRAGMENT COMPONENTS Yehuda: Components are one of the most fundamental

    APIs in Ember. Because of how important they are, improving them yields a huge payoff. Let's continue with the theme of simplification and talk about fragments.
  83. <div id="ember230" class="ember-view"> ... </div> Yehuda: One property of Ember

    components is that they add a wrapping DOM element around the component template. This element can interfere with CSS rules, or just make the DOM more complex for little benefit.
  84. import Ember from 'ember'; export default Ember.Component.extend({ }); Hello, <span>world</span>!

    COMPONENT DOM OUTPUT <div id="ember230" class="ember-view"> </div> Hello, <span>world</span>! TEMPLATE Yehuda: One thing we should mention is that power users were always able to disable this behavior. <click> People "in the know" learn the trick that you can set the `tagName` property to an empty string, <click>which removes the wrapping element, but it really sucks having to create an empty component class just to do this. And it really breaks the flow of learning Ember at a particularly early point.
  85. import Ember from 'ember'; export default Ember.Component.extend({ }); Hello, <span>world</span>!

    COMPONENT DOM OUTPUT <div id="ember230" class="ember-view"> </div> Hello, <span>world</span>! TEMPLATE tagName: '' Yehuda: One thing we should mention is that power users were always able to disable this behavior. <click> People "in the know" learn the trick that you can set the `tagName` property to an empty string, <click>which removes the wrapping element, but it really sucks having to create an empty component class just to do this. And it really breaks the flow of learning Ember at a particularly early point.
  86. import Ember from 'ember'; export default Ember.Component.extend({ }); Hello, <span>world</span>!

    COMPONENT DOM OUTPUT Hello, <span>world</span>! TEMPLATE tagName: '' Yehuda: One thing we should mention is that power users were always able to disable this behavior. <click> People "in the know" learn the trick that you can set the `tagName` property to an empty string, <click>which removes the wrapping element, but it really sucks having to create an empty component class just to do this. And it really breaks the flow of learning Ember at a particularly early point.
  87. import Ember from 'ember'; export default Ember.Component.extend({ tagName: '' });

    Hello, <span>world</span>! COMPONENT DOM OUTPUT <div id="ember230" class="ember-view"> </div> Hello, <span>world</span>! TEMPLATE Yehuda: To make this more discoverable, we want components without a class implementation <click> to not get their template wrapped in an extra DOM element. <click> This change makes creating reusable snippets of HTML feel incredibly lightweight, and is more intuitive because templates become "What You See Is What You Get" in the DOM.
  88. Hello, <span>world</span>! DOM OUTPUT <div id="ember230" class="ember-view"> </div> Hello, <span>world</span>!

    TEMPLATE Yehuda: To make this more discoverable, we want components without a class implementation <click> to not get their template wrapped in an extra DOM element. <click> This change makes creating reusable snippets of HTML feel incredibly lightweight, and is more intuitive because templates become "What You See Is What You Get" in the DOM.
  89. Hello, <span>world</span>! DOM OUTPUT Hello, <span>world</span>! TEMPLATE Yehuda: To make

    this more discoverable, we want components without a class implementation <click> to not get their template wrapped in an extra DOM element. <click> This change makes creating reusable snippets of HTML feel incredibly lightweight, and is more intuitive because templates become "What You See Is What You Get" in the DOM.
  90. export default Component.extend({ tagName: 'fieldset', classNames: ['note', 'test-addon-note'], }); <legend>Note</legend>

    {{markdown-to-sanitized-html markdown=content tables=true ghCodeBlocks=true tasklists=true simplifiedAutoLink=true }} COMPONENT TEMPLATE Yehuda: It's also annoying that you have to create a component class to customize the wrapping element. If I want to replace the default `div` with a `fieldset` element, for example, Ember developers have to create a new component class and learn the JavaScript API for changing the tag name or binding an attribute. This isn't ideal because those are both things people intuitively already know how to do in a template. So with fragment components, <click> we can delete this component class…
  91. <legend>Note</legend> {{markdown-to-sanitized-html markdown=content tables=true ghCodeBlocks=true tasklists=true simplifiedAutoLink=true }} TEMPLATE Yehuda:

    It's also annoying that you have to create a component class to customize the wrapping element. If I want to replace the default `div` with a `fieldset` element, for example, Ember developers have to create a new component class and learn the JavaScript API for changing the tag name or binding an attribute. This isn't ideal because those are both things people intuitively already know how to do in a template. So with fragment components, <click> we can delete this component class…
  92. <fieldset class="note test-addon-note"> <legend>Note</legend> {{markdown-to-sanitized-html markdown=content tables=true ghCodeBlocks=true tasklists=true simplifiedAutoLink=true

    }} </fieldset> TEMPLATE Yehuda: …and just put the fieldset element in the template where it belongs.
  93. export default class extends Component { @service currentUser; update(title) {

    this.set('title', title); } @computed('title') get slug() { return dasherize(this.title); } } SETTERS Tom: So we talked about no longer requiring people to type `this.get` everywhere. But what about `this.set`?
  94. export default class extends Component { @service currentUser; update(title) {

    this.set('title', title); } @computed('title') get slug() { return dasherize(this.title); } } EXPLICIT SET TRACKED PROPERTY export default class extends Component { @service currentUser; @tracked title; update(title) { this.title = title; } @computed('title') get slug() { return dasherize(this.title); } } Tom: For Ember to keep the DOM up-to-date for you, it has to be able to see when your component changes. Currently, we do that by asking you to call `this.set`. But with class fields and the tracked decorator, you can tell Ember about the property once, and then set the property like normal everywhere else. In the class on the right, I tell Ember that the title property is tracked. Then when I mutate it in the update method, I don't have to worry about calling this.set.
  95. export default class extends Component { @service currentUser; update(title) {

    this.set('title', title); } @computed('title') get slug() { return dasherize(this.title); } } COMPUTED TRACKED export default class extends Component { @service currentUser; @tracked title; update(title) { this.title = title; } @tracked('title') get slug() { return dasherize(this.title); } } Tom: So how do tracked properties relate to computed properties? You might be thinking we can just rename `computed` to `tracked` and it will behave the same as before.
  96. export default class extends Component { @service currentUser; update(title) {

    this.set('title', title); } @computed('title') get slug() { return dasherize(this.title); } } COMPUTED TRACKED export default class extends Component { @service currentUser; @tracked title; update(title) { this.title = title; } @tracked('title') get slug() { return dasherize(this.title); } } Tom: So how do tracked properties relate to computed properties? You might be thinking we can just rename `computed` to `tracked` and it will behave the same as before. If you've used Glimmer.js, you know this works. But what if we could do better than that?
  97. export default class extends Component { @service currentUser; update(title) {

    this.set('title', title); } @computed('title') get slug() { return dasherize(this.title); } } COMPUTED TRACKED export default class extends Component { @service currentUser; @tracked title; update(title) { this.title = title; } @tracked get slug() { return dasherize(this.title); } } ('title') Tom: With tracked properties, we could automatically infer dependencies on other tracked properties just by accessing them.
  98. export default class extends Component { @service currentUser; update(title) {

    this.set('title', title); } @computed('title') get slug() { return dasherize(this.title); } } COMPUTED TRACKED export default class extends Component { @service currentUser; @tracked title; update(title) { this.title = title; } @tracked get slug() { return dasherize(this.title); } } Tom: With tracked properties, we could automatically infer dependencies on other tracked properties just by accessing them.
  99. export default class extends Component { @service currentUser; update(title) {

    this.set('title', title); } @computed('title') get slug() { return dasherize(this.title); } } COMPUTED TRACKED export default class extends Component { @service currentUser; @tracked title; update(title) { this.title = title; } @tracked get slug() { return dasherize(this.title); } } Tom: Imagine never having to think about writing dependent keys or keeping them in sync with the implementation ever again. This would be a massive productivity boon and eliminate a common source of bugs. Tracked properties would make autotracking dependencies possible.
  100. export default Component.extend({ post: null, showComments: false, byline: computed('post.{author,date}', function()

    { let { author, date } = this.post; return `By ${author} at ${date}`; }) // ... }); <h1>{{byline}}</h1> <div>{{post.body}}</div> {{#if showComments}} <div>{{post.comments}}</div> {{/if}} COMPONENT TEMPLATE Yehuda: One issue with the component API is that it's hard to tell whether properties are coming from the component itself or passed in as arguments. We've all written a lot of components like this. Can you tell by looking at this code whether `showComments` is passed in as an argument to the component? Neither can we! The only way to know for sure is to search the code for all of the places where the component is used to see if anyone passed a `showComments` argument.
  101. export default class extends Component { showComments = false; @tracked

    get byline() { let { author, date } = this.args.post; return `By ${author} at ${date}`; } // ... }); <h1>{{this.byline}}</h1> <div>{{@post.body}}</div> {{#if this.showComments}} <div>{{@post.comments}}</div> {{/if}} COMPONENT TEMPLATE Yehuda: To make things clear, components should get their arguments off of `this.args` in JavaScript, and by using the `@name` syntax in their templates. Here's the same component updated to be more explicit. It is much easier to see that `post` is an argument, while `byline` and `showComments` come from the component. This also bakes the Data Down, Actions Up pattern right into the API by making passed data immutable.
  102. import Component from '@ember/component'; export default Component.extend({ classNames: ['list-filter'], value:

    '', init() { this._super(...arguments); this.get('filter')('').then((allResults) => { this.set('results', allResults.results); }); }, actions: { handleFilterEntry() { let filterInputValue = this.get('value'); let filterAction = this.get('filter'); filterAction(filterInputValue).then((filterResults) => { if (filterResults.query === this.get('value')) { this.set('results', filterResults.results); } }); } } }); import Component, { tracked } from '@glimmer/component'; export default class extends Component { @tracked value = ''; @tracked results = []; constructor() { super(...arguments); this.handleFilterEntry(''); }, async handleFilterEntry(value) { let { query, results } = await this.args.filter(value); // Handle race condition if (query === this.value) { this.results = results; } } }); BEFORE AFTER Yehuda: Putting all of these improvements together, the code for a real world component gets much simpler and cleaner. Here's an example from the app you build in the getting started tutorial, Super Rentals. * Component state is obvious because we marked state as tracked. * Component arguments are obvious because they come from the args property. * Async code is cleaned up with async functions. * We've moved the class name from the JavaScript to the template.
  103. {{input value=value key-up=(action 'handleFilterEntry') class="light" placeholder="Filter By City"}} {{yield results}}

    <div class="list-filter"> {{input value=value key-up=(action 'handleFilterEntry') class="light" placeholder="Filter By City"}} {{yield results}} </div> BEFORE AFTER Yehuda: Now everything DOM related is in the template where it should be. So that was quite a whirlwind tour. Each of these features alone may seem small, but having used many of them in Glimmer, they really add up to something great.
  104. 2019 Tom: So this is our vision for Ember in

    2019. New features that simplify, clarify, and help you be even more productive.
  105. New File Layout Optional jQuery Simplified Testing TypeScript Partial →

    Component JS Classes JS Modules 2019 Editor Integration Native Getters Custom Components Tom: So this is our vision for Ember in 2019. New features that simplify, clarify, and help you be even more productive.
  106. New File Layout Optional jQuery 2019 Editor Integration ? JS

    Modules Native Getters Custom Components TypeScript Partial → Component JS Classes Simplified Testing Tom: …but do we really have to wait until next year's EmberConf? If you look under your chair, you'll see we've printed out a unique download code—nah, just kidding.
  107. New File Layout Optional jQuery Simplified Testing TypeScript JS Classes

    Editor Integration JS Modules RELEASE BETA CANARY Native Getters Custom Components Tracked Properties Partial → Component @names TODO: This is the big reveal, so dwell on a bit Tom: Everything we just talked about in our vision for the future of Ember is already here. All of these features are in a stable, beta, or canary release<click> today. Many of the people here today have made that happen. If you're as excited as we are for the vision we just presented, we need your help to get everything into a stable release. If you haven't contributed to Ember before, now is a great time to start. You've all already done such amazing work getting us to this point, but there's so much more for us to do.
  108. New File Layout Optional jQuery Simplified Testing TypeScript JS Classes

    Editor Integration JS Modules RELEASE BETA CANARY Native Getters Custom Components Tracked Properties Partial → Component @names TODAY TODO: This is the big reveal, so dwell on a bit Tom: Everything we just talked about in our vision for the future of Ember is already here. All of these features are in a stable, beta, or canary release<click> today. Many of the people here today have made that happen. If you're as excited as we are for the vision we just presented, we need your help to get everything into a stable release. If you haven't contributed to Ember before, now is a great time to start. You've all already done such amazing work getting us to this point, but there's so much more for us to do.
  109. Certain of the matters discussed in this presentation about our

    and our subsidiaries’ future performance, including, without limitation, future revenues, earnings, strategies, prospects, consequences and all other statements that are not purely historical constitute “forward-looking statements” within the meaning of the Private Securities Litigation Reform Act of 1995. Such forward-looking statements are subject to risks and uncertainties, which could cause actual results to differ materially from those anticipated. Such statements are based on management’s beliefs as well as assumptions made by and information currently available to management. When used herein, the words “anticipate,” “intend,” “estimate,” “believe,” “expect,” “plan,” “should,” “hypothetical,” “potential,” “forecast,” “project,” variations of such words and similar expressions are intended to identify forward-looking statements. Factors that may cause actual results to differ are often presented with the forward-looking statements themselves. Other factors that could cause actual results to differ materially from those contemplated in any forward-looking statements made by us herein are discussed in filings we make with the United States Securities and Exchange Commission (SEC) including our Annual Report on Form 10-K and subsequent reports on Form 10-Q and Form 8-K. These factors include, but are not limited to: • fluctuations in wholesale power and natural gas markets, including the potential impacts on the economic viability of our generation units; • our ability to obtain adequate fuel supply; • any inability to manage our energy obligations with available supply; • increases in competition in wholesale energy and capacity markets; • changes in technology related to energy generation, distribution and consumption and customer usage patterns; • economic downturns; • third party credit risk relating to our sale of generation output and purchase of fuel; • adverse performance of our decommissioning and defined benefit plan trust fund investments and changes in funding requirements; • changes in state and federal legislation and regulations; • the impact of pending rate case proceedings; • regulatory, financial, environmental, health and safety risks associated with our ownership and operation of nuclear facilities; • adverse changes in energy industry laws, policies and regulations, including market structures and transmission planning; • changes in federal and state environmental regulations and enforcement; • delays in receipt of, or an inability to receive, necessary licenses and permits; • adverse outcomes of any legal, regulatory or other proceeding, settlement, investigation or claim applicable to us and/or the energy industry; • changes in tax laws and regulations; • the impact of our holding company structure on our ability to meet our corporate funding needs, service debt and pay dividends; • lack of growth or slower growth in the number of customers or changes in customer demand; • any inability of Power to meet its commitments under forward sale obligations; • reliance on transmission facilities that we do not own or control and the impact on our ability to maintain adequate transmission capacity; • any inability to successfully develop or construct generation, transmission and distribution projects; • any equipment failures, accidents, severe weather events or other incidents that impact our ability to provide safe and reliable service to our customers; • our inability to exercise control over the operations of generation facilities in which we do not maintain a controlling interest; • any inability to recover the carrying amount of our long-lived assets and leveraged leases; • any inability to maintain sufficient liquidity; • any inability to realize anticipated tax benefits or retain tax credits; • challenges associated with recruitment and/or retention of key executives and a qualified workforce; • the impact of our covenants in our debt instruments on our operations; and • the impact of acts of terrorism, cybersecurity attacks or intrusions. CAUTIONARY NOTE REGARDING FORWARD LOOKING STATEMENTS Tom: Our lawyer, Robert Jackson, insisted that we add this disclaimer. The things we are about to show are not finalized yet, so everything here is subject to change.
  110. <UserProfile> {{user-profile}} ANGLE BRACKET COMPONENTS Yehuda: We've already covered a

    number of features that came from Glimmer.js and have already landed in Ember. One thing Glimmer.js users have told us they really love is so-called "angle bracket syntax". People who used this feature in Glimmer.js said they were surprised at how much clearer it made their templates.
  111. {{#each options as |option|}} {{radio-button action="select" option=option selected=selected}} {{/each}} Yehuda:

    Here's what using a component looks like in a real app. <Click> And here's what the same component looks like using angle-bracket syntax. It really helps to distinguish between control flow structures and components.
  112. {{#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}} Yehuda: Here's what using a component looks like in a real app. <Click> And here's what the same component looks like using angle-bracket syntax. It really helps to distinguish between control flow structures and components.
  113. {{#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}} Yehuda: And the icing on the cake is that you can finally use single word component names!
  114. <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}} Yehuda: Keen observers might have noticed that the `@` syntax in the component invocation is the same as the `@` syntax in templates we showed before. That isn't an accident!
  115. <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}} Yehuda: When using angle bracket syntax, the names on the outside line up with the names on the inside. In other words, the `@` is how you talk about arguments both on the caller side and in the component template.
  116. {{#contextual-accordion items=blogPosts as |item a|}} {{#a.header}} {{item.title}} {{/a.header}} {{#a.details}} <h4>By

    {{item.author}} </h4> <p> {{item.text}} </p> {{/a.details}} {{/contextual-accordion}} NAMED BLOCKS Tom: Since Ember 2.3, you have been able to yield components to a component's block. People have really taken to this feature, implementing all kinds of cool patterns. One that we've seen a lot is to use this feature to allow users to pass more than one block to components. One of the principles of the Ember community is that we make sure that common patterns are available to everyone, not just power users. So we're working on a way to bake this pattern into Ember.
  117. <Accordion @items={{this.blogPosts}}> <@header= as |item|> {{item.title}} </@header> <@details= as |item|>

    <h4>By {{item.author}} </h4> <p> {{item.text}} </p> </@details> </Accordion> Tom: RFC #226 introduced "named blocks", which allow you to pass more than one block to a component. In this Accordion component, in addition to passing in an array of items as an argument, we also pass two named blocks called header and details.
  118. <Accordion @items={{this.blogPosts}}> <@header= as |item|> {{item.title}} </@header> <@details= as |item|>

    <h4>By {{item.author}} </h4> <p> {{item.text}} </p> </@details> </Accordion> <div class="accordion"> <h1>{{@header item}}</h1> <section>{{@details item}}</section> </div> CALLER COMPONENT TEMPLATE Yehuda: The best part of this feature is how easy it is to define components that use named blocks. At the top, we have our component template that invokes the blocks that were passed in. The blocks are just arguments, like @items.
  119. New File Layout Optional jQuery Simplified Testing TypeScript JS Classes

    Editor Integration JS Modules RELEASE BETA CANARY Native Getters Custom Components Tracked Properties Partial → Component @names Yehuda: These two features have not landed in canary yet… <transition>
  120. New File Layout BETA CANARY Native Getters Custom Components Tracked

    Properties Partial → Component @names RFC Yehuda: …but there are already RFCs. <click> The named blocks RFC has already been merged. And the angle bracket component RFC is still active. Please check it out and help us get it merged. There are even more RFCs I wanted to talk about today, but I was told we only had 3 hours.
  121. New File Layout BETA CANARY Native Getters Custom Components Tracked

    Properties Partial → Component @names RFC Named Blocks Angle Bracket Components Yehuda: …but there are already RFCs. <click> The named blocks RFC has already been merged. And the angle bracket component RFC is still active. Please check it out and help us get it merged. There are even more RFCs I wanted to talk about today, but I was told we only had 3 hours.
  122. Yehuda: Overall, it really feels like we've all found a

    rhythm for shipping code while keeping the pipeline of ideas full. I'm so excited about what we're building together.
  123. Tom: Last year at EmberConf, we announced Glimmer.js. We started

    with a small core of functionality extracted from Ember, and built out a streamlined toolchain for building lightweight web applications. We already talked about all of the great ergonomic improvements from Glimmer.js that are making their way into Ember. Of course, Glimmer.js is also about performance.
  124. Tom: Last year at EmberConf, we announced Glimmer.js. We started

    with a small core of functionality extracted from Ember, and built out a streamlined toolchain for building lightweight web applications. We already talked about all of the great ergonomic improvements from Glimmer.js that are making their way into Ember. Of course, Glimmer.js is also about performance.
  125. Tom: I spent some of last year using it on

    an exciting performance-related project at LinkedIn. You may have seen the blog post.
  126. Tom: The feed you see when you log in is

    powered by Ember. For this experiment, we rebuilt this page with initial load time as the only constraint. But instead of rebuilding it once, we did it twice.
  127. Preact Glimmer Tom: A small team of engineers rebuilt the

    feed in both Glimmer.js and Preact, which we called the two different "flavors". We used Preact because, at 3kb, we considered it to be the gold standard for building lightweight pages and also for how easy it is to hand-tune to achieve optimal performance. It also helped that we had one of the maintainers of Preact on the team. Both flavors rendered initial HTML on the server, then rehydrated the application in the browser. We ran the two flavors in production for a week to see how they compared.
  128. METRICS • First Meaningful Paint • Rehydration Time Tom: To

    measure how fast the page loaded, two numbers we looked at were first meaningful paint and rehydration time. First meaningful paint measures when primary content becomes visible and the page is ready to read, scroll and tap links. Rehydration is when the JavaScript app finishes loading in the browser, and reconnects to the DOM provided by the server. In practice, this is when the app is fully interactive, including things like share and like buttons.
  129. 2.5s 2.9s First Meaningful Paint Glimmer Preact 90th percentile, lower

    numbers are better Tom: So, how did we do? Both flavors performed extremely well, delivering a first meaningful paint under 3 seconds.
  130. 2.5s 4.1s 2.9s 4.3s Rehydration First Meaningful Paint Glimmer Preact

    90th percentile, lower numbers are better Tom: Both implementations also finished rehydrating around the same time. Both libraries are a great way to build fast, lightweight pages for the modern web. We were very happy to see Glimmer perform so well that it could keep up with libraries built for speed.
  131. • Binary Bytecode • Server-side Rendering • Repairing Rehydration •

    Async Rendering Yehuda: As Tom worked on the application, I was helping by optimizing the Glimmer VM. Three major new features contributed to Tom's great results. * Compiling templates into binary data reduces time spent parsing JavaScript. * Server-side rendering allows users to see meaningful content before the app has finished loading. * Finally, incremental rehydration allows apps to start up without causing jank.
  132. HBS HBS HBS 110110 010010 101011 001010 GBX compiles Yehuda:

    First, we improved performance by compiling our templates into a binary format called "gbx". Not only does this allow us to do more work ahead of time, it also allows us to reduce the size of Glimmer and speed up the execution of the templates themselves.
  133. ASYNC RENDERING Yehuda: The second contributor to Glimmer's performance is

    async rendering. Async rendering means the rendering work can be split into chunks and spread out over multiple frames, allowing the user to see content more quickly while the device remains responsive. This is especially important on mobile devices, where the CPU can be overwhelmed by the demands of the modern web.
  134. Yehuda: I could describe the details of how it works,

    but all you probably need to see is this comparison of a Glimmer app running on a low-end phone, one with async rendering and one without. <click>
  135. Yehuda: I could describe the details of how it works,

    but all you probably need to see is this comparison of a Glimmer app running on a low-end phone, one with async rendering and one without. <click>
  136. SERVER SIDE RENDERING Tom: To hit our very ambitious performance

    goals, we assumed we would also need to use server-side rendering. Glimmer's VM architecture already decouples the rendering pipeline from the DOM, so all Yehuda needed to do was implement a new renderer that used the existing nodes instead of creating new ones.
  137. Glimmer 2.5s 4.1s Glimmer (no SSR) 4.0s 4.0s First Meaningful

    Paint Fully Interactive 90th percentile, lower numbers are better Tom: There has been some debate in the wider JavaScript community about whether Server Side Rendering is really worth it. As a result, we ran both the Glimmer and Preact flavors using Server Side Rendering and Client Side Rendering. As you can see, we managed to deliver content to the user a full 1.5 seconds earlier when rendering on the server. First Meaningful Paint is measuring when the user was able to read the content, click on links, and start scrolling. Rehydration is great, but if the user has already started scrolling or interacting with the page, we can't afford to lock the browser for a few seconds to do the rehydration. The same rendering engine that powers async rendering also allows Glimmer to perform async rehydration without janking the scroll.
  138. REPAIRING REHYDRATION Yehuda: One problem that people have run into

    with Server Side Rendering is that small differences between the server and the client can lead to surprising performance problems. It is nearly impossible to produce identical renders due to small differences in things like internationalization differences across browsers. In our implementation, rather than assume that these kinds of differences are bugs for app developers to fix, we wanted to repair differences between the server and client automatically.
  139. SCHEDULE.EMBERCONF.COM Yehuda: Not only has this change made it back

    to Glimmer.js, it is already available in Ember canary. In fact, this year's EmberConf schedule app is an Ember app using rehydration. Try it out on your phone and see how fast it feels.
  140. Tom: Those are some of the really great performance advancements

    that we've been working on in Glimmer.js and we're looking forward to bringing them to all Ember developers soon.
  141. Tom: Those are some of the really great performance advancements

    that we've been working on in Glimmer.js and we're looking forward to bringing them to all Ember developers soon.
  142. New File Layout Optional jQuery Simplified Testing TypeScript Partial →

    Component JS Classes JS Modules 2019 Editor Integration Native Getters Custom Components Tom: Today, we've shared with you just some of the awesome new stuff coming to Ember. We've talked about ergonomics improvements and performance improvements. I feel a renew sense of momentum in the community. I have never been more excited to be an Ember developer. ... but there is one more thing... Yehuda: Glimmer is our laboratory for audacious experiments where we can take advantage of the most cutting edge web technologies. The web is always evolving, but every once in a while, a technology comes along that feels like a sea change. There's something I've been working on for the past few months that I'm excited to talk about with all of you today.
  143. Yehuda: If you aren't familiar with it, Web Assembly is

    a way to run code at native speeds in all modern web browsers. While JavaScript can sometimes achieve close-to- native speed with careful attention and hand-tuning (believe me, I know), it is hard for even the best JavaScript programmer to get good performance predictably. We've managed to build a very fast VM using JavaScript, but maintaining good performance in the face of ever-changing JavaScript JIT optimizations across four different browsers has been extremely challenging. Needless to say, the promise of WebAssembly got us very excited. But is it even possible to adopt WebAssembly in a library architected years ago for JavaScript?
  144. + Yehuda: Last year, as WebAssembly began shipping in all

    browsers, we got interested in exploring whether we could take advantage of this new capability. Due to its nature, Glimmer has always been written in a low-level style, and we were excited with how well aligned with WebAssembly Glimmer looked at first glance.
  145. Stack Registers Executable Heap Opcodes Constants References Validators Component Managers

    DOM We looked at Glimmer's architecture, and we noticed that the core virtual machine <click> was a hot path that was written in very hand-tuned JavaScript. As a result, much of it actually didn't rely much on the features of JavaScript that aren't yet available in WebAssembly. So our plan was to migrate some of this internal code.
  146. Stack Registers Executable Heap Opcodes Constants References Validators Component Managers

    DOM VM We looked at Glimmer's architecture, and we noticed that the core virtual machine <click> was a hot path that was written in very hand-tuned JavaScript. As a result, much of it actually didn't rely much on the features of JavaScript that aren't yet available in WebAssembly. So our plan was to migrate some of this internal code.
  147. Stack Registers Executable Heap Opcodes Constants References Validators Component Managers

    DOM VM Yehuda: After a few months of work, we've successfully migrated much of the low level code in Glimmer to WebAssembly, as well as a good number of opcodes. So how did it go? Well, as you recall from earlier in the keynote, having a path to land any experiment in Ember is priority #1.
  148. Yehuda: Which is why I am happy to report that

    the wasm branch of Glimmer is up to date with master and passing the entire test suite. So far, we've been mostly focused on compatibility, so we have been keeping the test suite green as we go. As you know, Glimmer is the rendering engine for Ember.js, so keeping it green also means it works with Ember.
  149. SCHEDULE-WASM.EMBERCONF.COM In fact, we were able to get the Ember

    observer app running just by swapping in our branch. All of the app code is unaltered. And the scheduling app is not only running with rehydration, it's running with WebAssembly turned on. We only didn't make this the real schedule app, because iOS broke WebAssembly temporarily due to a Spectre snafu, which should be fixed in the next few months.
  150. + To achieve its benefits, WebAssembly is very low level.

    We didn't want to sacrifice the productivity of JavaScript and write low-level, unmaintainable assembly. Thankfully, we're not the only ones excited about WebAssembly.
  151. + + Rust is designed to help you to write

    low-level code in a productive, high-level way, and they are excited about making low-level code accessible to JavaScript developers through WebAssembly. The Rust community has built out a lot of the infrastructure needed to build production WebAssembly applications,
  152. Stack Registers Executable Heap Opcodes Constants References Validators Component Managers

    DOM VM and we've been building the WebAssembly branch of Glimmer in Rust.
  153. Yehuda: We don't have time to get into the code,

    but here's a small example to show you that even though it's producing low-level output, the code itself looks high level. And unlike JavaScript, the high level nature of this code doesn't come at the cost of unpredictable performance. What you see is what you get.
  154. So what's the plan going forward? Now that Ember apps

    can run on WebAssembly, the next step is to land the branch behind a feature flag. We won't enable it until there are strong performance benefits and have verified that there are no unknown issues with browser compatibility and debuggability. Because we have been so focused on maintaining compatibility, we are currently doing some things in the code that are preventing us from taking advantage of the performance benefits. For example, to work with our existing test infrastructure, we are currently encoding the WebAssembly binary as a base64 string in the JavaScript payload. This prevents us from getting an accurate assessment of the performance profile, and the next steps in the code are to clean up these known inefficiencies.
  155. Tom: Both of us really value the fact that our

    community is so focused on finding ways to take advantage of cutting edge technology without losing sight of what really matters: building great apps that our users love over the long haul.
  156. Tom: Both of us really value the fact that our

    community is so focused on finding ways to take advantage of cutting edge technology without losing sight of what really matters: building great apps that our users love over the long haul.
  157. WE NEED YOUR HELP Tom: Despite being 7 years old,

    Ember has never been more technically advanced and our community has never been more energized. We've already landed so much cool stuff, only a fraction of which we've had time to cover today. But we need your help to make sure Ember remains the most productive way to build for the web. Real quickly, can anyone here in the audience who has participated in a subteam stand up? Whether that's Ember CLI, Ember Data, the Learning team, or any of the many strike teams over the years? If you are excited about any of the things we've talked about today, find any one of these people during the conference. They would be happy to help you make your first contribution. While we have them out of their seat, can we take a moment to say thank you to these folks, who have poured their heart and soul into building tools for other ambitious developers… Thank you, everyone. (tell them to sit down) Whether this is your fifth EmberConf or your first, you're part of something special. This community is special. We have a great lineup of talks for you this year, but this conference is also about the connections you make with the people in your community. We are very excited just thinking about all of the cool stuff we're going to build together this year.
  158. THANK YOU! Thank you, and enjoy the show!