Favorite this article, stop talking about "data hijacking"

I recently contacted some interviewers. When I asked "how does Vue implement two-way data binding", I would blurt out "data hijacking", and then what? Then there is no ╮(╯_╰)╭. It is true that "data hijacking" is the foundation, but it is far from the answer that the interviewer wants to hear. It is better to spend ten minutes reading this article and answer it next time.

"Two-way binding" itself

To answer the question, we must first understand the problem: two-way data binding is a pattern, which generally refers to the automatic synchronization of data from dom to JS objects in the web context. DOM and JS are isolated on two different runtimes, and they need to communicate with each other through an imperative DOM interface : DOM needs to trigger events correctly and transmit information to JS programs; JS also needs to consciously change the state after Call the appropriate interface to change the DOM content. This approach causes two problems:

  1. Status management and presentation are two separate sets of logic that need to be kept in sync, which is a high development cost
  2. The DOM specification defines a lot of interfaces, and there are compatibility issues, which is a high cost of learning

Two-way binding encapsulates the synchronization process of data from DOM to JS or from JS to DOM through various designs, and encapsulates it in the framework itself. The upper-level code is separated from the dependence on the underlying interface, and only needs to focus on the state management logic.

Object.defineProperty

The first question we want to discuss is, how to detect changes to JS object properties? The simplest and crudest method is "dirty checking", which is used in older versions of Angular to initiate a dirty check after various events that may cause state changes. This method is intuitive, but there are many issues to consider in implementation:

  1. During the dirty check process, new data changes may be triggered, and an infinite loop is entered.
  2. The implementation of dirty checking must keep a copy of the old and new data
  3. Dirty checks must predict all possible timings that trigger state changes, which means that some native interfaces need to be wrapped (including setTimeout, requestNextAnimationFrameetc.)
  4. ...

Vue is Object.definePropertyimplemented . When the component is initialized, the interface will be called, the object properties will be wrapped as a getfunction set, and the code will be "buried" in the "get" and "modify" behaviors of the properties. Take a look at a simple example and feel it intuitively:

const person = {};

// 嘿嘿,谁都改不了我的名字
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

Dependency Management Solution

Object.definePropertyIt just solves the problem of how to trigger the notification after the state changes. Who should be notified? Who cares that those properties have changed? In Vue, Dep is used to decouple the process of determining the relationship between the dependant and the dependant. simply put:

  • The first step is to traverse the state object through the interface provided by Observer , and bind a dedicated object to each property and sub-property of the Depobject . The state object here mainly refers to the dataproperties in the component.

  • The second step is to create three types of watchers:

    1. Call initComputed to convert computedproperties to watcherinstances
    2. Call the initWatch method to convert the watchconfiguration into an watcherinstance
    3. Call the mountComponent method renderto bind the watcherinstance to the function
  • The third step, after the state changes, trigger the dep.notify()function , which further triggers the Watcher object updatefunction to perform the recalculation of the watcher.

Corresponding to the following figure:

data.png

Note that the renderfunction can simply be regarded as a special computedfunction. When the corresponding Watcherobject changes, it will trigger the execution of render, generate a new virutal-dom structure, and then hand it over to Vue for diff. Update the view.

Epilogue

This article ends here. For more content, you can try to look at the source code. The design patterns in the code are very worth learning.

Vue uses data hijacking as the underlying support, and designs a sophisticated dependency management solution to decouple dependencies. However, the data hijacking scheme also has its own pain points that are difficult to solve:

  1. Can only be applied to simple objects
  2. Not valid for arrays, so you need to wrap the array method
  3. The starting point of attribute hijacking is "change", so vue cannot easily access the "immutable" mode

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325966908&siteId=291194637