Recently I met some interviewees. When I asked “how does Vue implement two-way data binding”, I blurted out “data hijacking”. Then what? And then there was no and then ╭. Yes, “data hijacking” is basic, but it’s far from the answer your interviewer wants to hear, so take 10 minutes to read this article and repeat your answer next time

“Bidirectional binding” itself

To answer the question, first understand the problem: data bidirectional binding is a pattern that automatically synchronizes data from dom to JS objects in a Web context. DOM and JS are isolated on two different runtimes and need to communicate with each other through an imperative DOM interface: DOM needs to trigger events correctly to transmit information to JS programs; JS also needs to consciously call the appropriate interface to change the DOM content after the state changes. This approach raises two problems:

  1. The management and presentation of state are two separate sets of logic that need to be deliberately synchronized, which is very expensive to develop
  2. The DOM specification defines a number of interfaces and has compatibility issues, which can be a high learning cost

Bidirectional binding encapsulates the process of synchronizing data from DOM to JS or FROM JS to DOM in the framework itself through a variety of designs. The upper layer of code is independent of the underlying interface and only needs to focus on the state management logic.

Object.defineProperty

The first question we’ll discuss is, how do we detect changes to the properties of our JS objects? The simplest and most crude method is the dirty check, used in older versions of Angular, which starts a dirty check after various events that might cause a state change. This approach is straightforward, but there are a number of implementation considerations:

  1. Dirty checks can trigger new data changes, creating an endless loop
  2. The implementation of dirty checking must keep both old and new copies of data
  3. The dirty check must anticipate all possible triggers of state changes, which means wrapping parts of the native interface (includingsetTimeout,requestNextAnimationFrameEtc.)
  4. .

Vue is implemented using the Object. DefineProperty metaprogramming interface. During component initialization, this interface is called to wrap object properties as get and set functions, and code is “buried” in behavior whose properties are “get” and “modify”. Let’s look at a simple example to get an idea:

const person = {};

// No one can change my name
Object.defineProperty(person, 'name', {
	get() {
		return 'van';
	},
	set(v) {
		console.log('they want change my name'); }});console.log(person.name);
// van
person.name = 'tec';
// they want change my name
console.log(person.name);
// van
Copy the code

Dependency management scheme

Object.defineproperty simply solves the problem of how to trigger notifications when a state change occurs. Who should be notified? Who cares if those properties change? In Vue, Dep is used to decouple the process of determining the relationship between the dependent and the dependent. In a nutshell:

  • The first step is to traverse the state object through the interface provided by the Observer, binding each attribute and subattribute of the object to a dedicated Dep object. The state object here refers primarily to the data property in the component.

  • Step 2: Create three types of Watcher:

    1. callinitComputed 将 computedAttribute conversion towatcherThe instance
    2. callinitWatchMethods,watchConfiguration conversion towatcherThe instance
    3. callmountComponentMethods forrenderThe functionwatcherThe instance
  • Dep.notify () is triggered, which in turn triggers the Watcher update function to perform the Watcher recalculation.

Corresponding to the following figure:

Note that the Render function in the Vue component can be treated simply as a special computed function that, when the Watcher object changes, triggers the execution of render, generates a new virutal-dom structure, and then diff it to Vue. Update the view.

conclusion

This is the end of the article, more content can try to look at the source code, the design pattern in the code is very worth learning.

Vue uses data hijacking as the underlying support and designs a sophisticated dependency management scheme to decouple dependencies. But data hijacking schemes have their own pain points:

  1. Can only be applied to simple objects
  2. Not valid for arrays, so you need to wrap array methods
  3. The starting point for attribute hijacking is variation, so VUE does not have good access to the IMmutable mode