Slide 1

Slide 1 text

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.

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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.

Slide 4

Slide 4 text

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.

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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.

Slide 7

Slide 7 text

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.

Slide 8

Slide 8 text

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.

Slide 9

Slide 9 text

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)

Slide 10

Slide 10 text

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)

Slide 11

Slide 11 text

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)

Slide 12

Slide 12 text

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)

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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.

Slide 15

Slide 15 text

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.

Slide 16

Slide 16 text

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.

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

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.

Slide 19

Slide 19 text

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.

Slide 20

Slide 20 text

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.

Slide 21

Slide 21 text

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.

Slide 22

Slide 22 text

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!

Slide 23

Slide 23 text

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!

Slide 24

Slide 24 text

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.

Slide 25

Slide 25 text

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.

Slide 26

Slide 26 text

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.

Slide 27

Slide 27 text

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.

Slide 28

Slide 28 text

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.

Slide 29

Slide 29 text

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.

Slide 30

Slide 30 text

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.

Slide 31

Slide 31 text

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.

Slide 32

Slide 32 text

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.

Slide 33

Slide 33 text

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.

Slide 34

Slide 34 text

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.

Slide 35

Slide 35 text

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.

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

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.

Slide 38

Slide 38 text

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.

Slide 39

Slide 39 text

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…

Slide 40

Slide 40 text

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.

Slide 41

Slide 41 text

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.

Slide 42

Slide 42 text

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.

Slide 43

Slide 43 text

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.

Slide 44

Slide 44 text

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.

Slide 45

Slide 45 text

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.

Slide 46

Slide 46 text

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.

Slide 47

Slide 47 text

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.

Slide 48

Slide 48 text

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.

Slide 49

Slide 49 text

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.

Slide 50

Slide 50 text

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.

Slide 51

Slide 51 text

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.

Slide 52

Slide 52 text

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.

Slide 53

Slide 53 text

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.

Slide 54

Slide 54 text

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.

Slide 55

Slide 55 text

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.

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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.

Slide 58

Slide 58 text

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.

Slide 59

Slide 59 text

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.

Slide 60

Slide 60 text

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.

Slide 61

Slide 61 text

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.

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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.

Slide 64

Slide 64 text

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.

Slide 65

Slide 65 text

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.

Slide 66

Slide 66 text

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.

Slide 67

Slide 67 text

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.

Slide 68

Slide 68 text

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…

Slide 69

Slide 69 text

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.

Slide 70

Slide 70 text

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.

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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.

Slide 73

Slide 73 text

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.

Slide 74

Slide 74 text

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.

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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.

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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.

Slide 79

Slide 79 text

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.

Slide 80

Slide 80 text

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.

Slide 81

Slide 81 text

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.

Slide 82

Slide 82 text

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.

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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.

Slide 85

Slide 85 text

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.

Slide 86

Slide 86 text

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.

Slide 87

Slide 87 text

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.

Slide 88

Slide 88 text

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.

Slide 89

Slide 89 text

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.

Slide 90

Slide 90 text

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…

Slide 91

Slide 91 text

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…

Slide 92

Slide 92 text

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.

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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.

Slide 95

Slide 95 text

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.

Slide 96

Slide 96 text

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?

Slide 97

Slide 97 text

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.

Slide 98

Slide 98 text

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.

Slide 99

Slide 99 text

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.

Slide 100

Slide 100 text

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.

Slide 101

Slide 101 text

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.

Slide 102

Slide 102 text

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.

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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.

Slide 106

Slide 106 text

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.

Slide 107

Slide 107 text

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.

Slide 108

Slide 108 text

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.

Slide 109

Slide 109 text

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.

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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!

Slide 115

Slide 115 text

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.

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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…

Slide 120

Slide 120 text

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.

Slide 121

Slide 121 text

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.

Slide 122

Slide 122 text

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.

Slide 123

Slide 123 text

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.

Slide 124

Slide 124 text

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.

Slide 125

Slide 125 text

Tom: I spent some of last year using it on an exciting performance-related project at LinkedIn. You may have seen the blog post.

Slide 126

Slide 126 text

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.

Slide 127

Slide 127 text

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.

Slide 128

Slide 128 text

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.

Slide 129

Slide 129 text

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.

Slide 130

Slide 130 text

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.

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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.

Slide 133

Slide 133 text

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.

Slide 134

Slide 134 text

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.

Slide 135

Slide 135 text

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.

Slide 136

Slide 136 text

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.

Slide 137

Slide 137 text

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.

Slide 138

Slide 138 text

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.

Slide 139

Slide 139 text

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.

Slide 140

Slide 140 text

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.

Slide 141

Slide 141 text

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.

Slide 142

Slide 142 text

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.

Slide 143

Slide 143 text

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?

Slide 144

Slide 144 text

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

Slide 145

Slide 145 text

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.

Slide 146

Slide 146 text

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.

Slide 147

Slide 147 text

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.

Slide 148

Slide 148 text

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.

Slide 149

Slide 149 text

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.

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

+ + 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,

Slide 152

Slide 152 text

Stack Registers Executable Heap Opcodes Constants References Validators Component Managers DOM VM and we've been building the WebAssembly branch of Glimmer in Rust.

Slide 153

Slide 153 text

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.

Slide 154

Slide 154 text

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.

Slide 155

Slide 155 text

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.

Slide 156

Slide 156 text

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.

Slide 157

Slide 157 text

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.

Slide 158

Slide 158 text

THANK YOU! Thank you, and enjoy the show!