Upgrade to Pro — share decks privately, control downloads, hide ads and more …

EmberConf 2018 Keynote

tomdale
March 14, 2018

EmberConf 2018 Keynote

tomdale

March 14, 2018
Tweet

More Decks by tomdale

Other Decks in Programming

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.

    View Slide

  2. CORE TEAM
    Yehuda: I'm very happy to announce…

    View Slide

  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.

    View Slide

  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.

    View Slide

  5. YEARS
    7
    Tom: This is the fifth anniversary of EmberConf, but Ember itself will be seven years old in April.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View 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)

    View Slide

  13. TOGETHER, WE'RE GOING
    TO BUILD A BETTER EMBER.
    Together, we're going to build a better Ember.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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!

    View Slide

  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!

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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…

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  56. COMPUTED PROPERTIES
    Tom: First, let's look at computed properties, perhaps the most popular feature in the object model.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  62. JS CLASSES
    Yehuda: Classes are another feature in JavaScript that didn't exist when we started Ember.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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…

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  75. CLASS FIELDS
    Tom: Decorators aren't the only improvement coming to classes.

    View Slide

  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.

    View Slide

  77. Tom: All of these features will add up to a really nice experience for Ember developers who want to use TypeScript.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    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.

    View Slide

  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.

    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.

    View Slide

  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.

    View Slide


  83. ...

    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.

    View Slide

  84. import Ember from 'ember';
    export default Ember.Component.extend({
    });
    Hello, world!
    COMPONENT
    DOM OUTPUT


    Hello, world!
    TEMPLATE
    Yehuda: One thing we should mention is that power users were always able to disable this behavior.

    People "in the know" learn the trick that you can set the `tagName` property to an empty string, 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.

    View Slide

  85. import Ember from 'ember';
    export default Ember.Component.extend({
    });
    Hello, world!
    COMPONENT
    DOM OUTPUT


    Hello, world!
    TEMPLATE
    tagName: ''
    Yehuda: One thing we should mention is that power users were always able to disable this behavior.

    People "in the know" learn the trick that you can set the `tagName` property to an empty string, 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.

    View Slide

  86. import Ember from 'ember';
    export default Ember.Component.extend({
    });
    Hello, world!
    COMPONENT
    DOM OUTPUT
    Hello, world!
    TEMPLATE
    tagName: ''
    Yehuda: One thing we should mention is that power users were always able to disable this behavior.

    People "in the know" learn the trick that you can set the `tagName` property to an empty string, 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.

    View Slide

  87. import Ember from 'ember';
    export default Ember.Component.extend({
    tagName: ''
    });
    Hello, world!
    COMPONENT
    DOM OUTPUT


    Hello, world!
    TEMPLATE
    Yehuda: To make this more discoverable, we want components without a class implementation to not get their template wrapped in an extra DOM element.


    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.

    View Slide

  88. Hello, world!
    DOM OUTPUT


    Hello, world!
    TEMPLATE
    Yehuda: To make this more discoverable, we want components without a class implementation to not get their template wrapped in an extra DOM element.


    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.

    View Slide

  89. Hello, world!
    DOM OUTPUT
    Hello, world!
    TEMPLATE
    Yehuda: To make this more discoverable, we want components without a class implementation to not get their template wrapped in an extra DOM element.


    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.

    View Slide

  90. export default Component.extend({
    tagName: 'fieldset',
    classNames: ['note', 'test-addon-note'],
    });
    Note
    {{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, we can delete this
    component class…

    View Slide

  91. Note
    {{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, we can delete this
    component class…

    View Slide


  92. Note
    {{markdown-to-sanitized-html
    markdown=content
    tables=true
    ghCodeBlocks=true
    tasklists=true
    simplifiedAutoLink=true
    }}

    TEMPLATE
    Yehuda: …and just put the fieldset element in the template where it belongs.

    View Slide

  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`?

    View Slide

  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.

    View Slide

  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.

    View Slide

  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?

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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}`;
    })
    // ...
    });
    {{byline}}
    {{post.body}}
    {{#if showComments}}
    {{post.comments}}
    {{/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.

    View Slide

  101. export default class extends Component {
    showComments = false;
    @tracked get byline() {
    let { author, date } = this.args.post;
    return `By ${author} at ${date}`;
    }
    // ...
    });
    {{this.byline}}
    {{@post.body}}
    {{#if this.showComments}}
    {{@post.comments}}
    {{/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.

    View Slide

  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.

    View Slide

  103. {{input
    value=value
    key-up=(action 'handleFilterEntry')
    class="light"
    placeholder="Filter By City"}}
    {{yield results}}

    {{input
    value=value
    key-up=(action 'handleFilterEntry')
    class="light"
    placeholder="Filter By City"}}
    {{yield results}}

    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.

    View Slide

  104. 2019
    Tom: So this is our vision for Ember in 2019. New features that simplify, clarify, and help you be even more productive.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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 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.

    View Slide

  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 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.

    View Slide

  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.

    View Slide


  110. {{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.

    View Slide

  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.



    And here's what the same component looks like using angle-bracket syntax. It really helps to distinguish between control flow structures and components.

    View Slide

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

    {{/each}}
    Yehuda: Here's what using a component looks like in a real app.



    And here's what the same component looks like using angle-bracket syntax. It really helps to distinguish between control flow structures and components.

    View Slide

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

    {{/each}}
    Yehuda: And the icing on the cake is that you can finally use single word component names!

    View Slide


  114. You have unsaved changes. Do you want to leave?

    {{#if @isOpen}}

    {{@title}}
    {{yield}}

    {{/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!

    View Slide


  115. You have unsaved changes. Do you want to leave?

    {{#if @isOpen}}

    {{@title}}
    {{yield}}

    {{/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.

    View Slide

  116. {{#contextual-accordion items=blogPosts as |item a|}}
    {{#a.header}}
    {{item.title}}
    {{/a.header}}
    {{#a.details}}
    By {{item.author}}

    {{item.text}}

    {{/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.

    View Slide


  117. <@header= as |item|>
    {{item.title}}
    @header>
    <@details= as |item|>
    By {{item.author}}

    {{item.text}}

    @details>

    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.

    View Slide


  118. <@header= as |item|>
    {{item.title}}
    @header>
    <@details= as |item|>
    By {{item.author}}

    {{item.text}}

    @details>


    {{@header item}}
    {{@details item}}

    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.

    View Slide

  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…

    View Slide

  120. New File Layout
    BETA CANARY
    Native Getters
    Custom Components
    Tracked Properties
    Partial → Component
    @names
    RFC
    Yehuda: …but there are already RFCs.

    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.

    View Slide

  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.

    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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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?

    View Slide

  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.

    View Slide

  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 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.

    View Slide

  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 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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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,

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  158. THANK YOU!
    Thank you, and enjoy the show!

    View Slide