Vue 2 Internal Features from the Ground Up

Challenge 1: Solution

Evan You

Evan You

Creator of Vue.js
Vue 2 Internal Features from the Ground Up

Check out a free preview of the full Vue 2 Internal Features from the Ground Up course

The "Challenge 1: Solution" Lesson is part of the full, Vue 2 Internal Features from the Ground Up course featured in this preview video. Here's what you'd learn in this lesson:

Evan provides a starting point to the Challenge 1 by showing dependency tracking, when an inner update function executes, the dependency class has access to the currently active update.

Preview
Close

Transcript from the "Challenge 1: Solution" Lesson

[00:00:00]
>> Evan You: If you want to check out the reference implementation, what you can do is first save your current work, commit it, and then do git checkout solutions.
>> Evan You: And this will put you in a branch where everything is completely implemented for you. So you can reference what it is doing.

[00:00:24]
So yeah this actually is pretty straightforward. So we first use object.keys to get a list of the current existing properties, and then we iterate through that keys array. And for each key, we get a closure here, right? So that gives us some opportunity to just store this, oops!

[00:00:45]
To store this variable here inside this closure. This is the internal value. And remember to assign it to the initial value of that original property, otherwise, you get undefined on the first access. And then we use definedProperty to override it. So I'm omitting all the extra innumerable staff here but remember to think about them for your use cases in real applications.

[00:01:11]
And then inside the get, we simply log the internal value, view the get access, and return the internalValue. This will retain the original access behavior. And inside the set, we'll just set the internal value into the new value and lock the set operation. Switch back to the master branch, so that you don't see the solutions for the next exercise.

[00:01:37]
>> Evan You: And then open 1.2.md. So now we're getting into something called dependency tracking, okay? So dependencies. So how do we abstract the concept of a dependency? Let's just create a class, okay? Let's create a class called dep. And this class will have two methods, depend and notify. Depend means the current code that's executing depends on this dependency, all right?

[00:02:14]
And notify means this dependency has changed. So any previous expressions, computations, functions that have previously depended on the step should be re-executed, they should be notified. What this means is we need to find a way to associate a piece, a function or a piece of expression or computation, I would call it associated computation to dependency.

[00:02:45]
And this computation should probably be considered something like a subscriber of this dependency, right? So that's how the dependency class works, and then we will have another function called autorun. And what this autorun function facilitates is that it'll take an update function or an updater function or a computation, as I would call it.

[00:03:11]
So this update function, when you enter this updater function, everything becomes special. We're in the reactive zone. So when you're inside this reactive zone, you're able to register dependencies. So let's take a look at how that would actually work. So the expect usage looks like this, right? So we'll have the dep class, we'll create an instance of the dep class called dep.

[00:03:37]
So this represents a dependency. And then we'll autorun a computation, and all it does is, the side effect we're producing is just a console.log, okay? But the important thing here is, inside this computation, we're calling dep.depend, which associates this function, this computation to this specific dependency. So we're essentially adding this function into the subscriber list of this dep, okay?

[00:04:08]
And later on, when you call dep.notify anywhere else, it doesn't need to be inside autorun or anything. When you call dep.notify, this function will be called again.
>> Evan You: Does that make sense?
>> Evan You: So the API is actually pretty simple. All you need to do is create the dep class and the autorun function that makes this code possible.

[00:04:37]
So similarly, you can run tests with this. And if you look at the file, the boiler plate is pretty simple, we just ask you to implement this. It takes a little bit of a trick. So let's working on this in ten minutes, I think. I'll try to see how everyone is doing and provide some hints as we go half through the time, okay?

[00:05:09]
So now I'm going to provide some hints on how this might work. So the idea here is we want to associate an instance of a dependency to a computation. So the catch here is this update function now we're taking in here, in fact, can be stored somewhere as well.

[00:05:37]
It's a variable, right? Functions are variables. So what we can do is let's have something like a global variable called activeUpdate, right? Because in JavaScript, JavaScript is single threaded. At any given time, only one function can be currently executing, right? So if we create a function that marks itself as the function that is currently being executed, then we can know any time whether this function is currently running.

[00:06:15]
So now we know if we're inside this function, does that make sense? So let's say we have this function called wrappedUpdate,
>> Evan You: And inside wrappedUpdate, we call the original update, okay? So this is equivalent. What we want to do is we want to know whether we are inside this inner update function.

[00:06:47]
And the way we can do that is do, activeUpdate = wrappedUpdate.
>> Evan You: So let's think about what we have achieved here. Whenever we call wrappedUpdate and the code inside this update function executes, any functions or any code being called to execute inside this update function, they're guaranteed to be able to access this outer wrappedUpdate function viewed as activeUpdate global variable.

[00:07:35]
So if we do this, and let's say autorun a function,
>> Evan You: So now we can check activeUpdate here, right? This activeUpdate will always be pointing to the wrappedUpdate that's created for this one. Does that make sense?
>> Evan You: So this variable will always point to something that references this.

[00:08:25]
But what's more important is when we call dep.depend here, right? Even inside the dep class, we can check the activeUpdate as well. So inside the dep's implementation,
>> Evan You: Remember, we're calling dep.depend inside the autorun, and we're essentially calling it inside here. And because we're calling here, this activeUpdate would have been set to this, so,

[00:09:07]
>> Evan You: if (activeUpdate), we need to register the current active update as a subscriber.
>> Evan You: And what the notify function does is run all subscriber functions.
>> Evan You: What this means is this dep class probably needs an instant property to store its subscribers, right? Let's create a constructor,
>> Evan You: So we can use an array but array requires some.

[00:09:56]
So we'll use the ES Set, which is easier,
>> Evan You: Okay? So, essentially,
>> Evan You: What you need to do is fill in the rest.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now