The preface
Company developed a drag and drop form project recently, with the help of the Vue, boss began to study the Vue source code, and teach us, the boss said, read the source code can not only read the source code, still have to read his design idea, why is he so design, like a designer to read, so you can understand, this article, I will talk about the responsive principle of Vue and its design ideas according to the guiding direction of the eldest brother and my own understanding
What is a Vue response
Official explanation: One of the most unique features of Vue is its non-invasive and responsive system. Data models are just plain JavaScript objects. And when you change them, the view updates, which is basically the data changes and the view updates accordingly, the view changes.
For example, input input, the data will be changed accordingly.
If we look at an example graph of the response, which is an addition expression for an Excel table, we will see that when the addend changes, its sum changes automatically, without the need for human manipulation to re-evaluate the result. The relationship between data and views in Vue is similar to the relationship between addends and sums here.So how is Vue responsive implemented? To know, we need to understand his design pattern.
Responsive design patterns
The design pattern used by Vue responsiveness is the observer pattern, which is colloquially referred to as.
For example: Guo teacher to work every day with an orange, I want to rub a bite when he eats oranges, but I don’t want to keep staring at Guo teacher to see when he eats, so I and Guo teacher agreed, you eat oranges notice me. Then guo teacher saw, also want to eat, is also, to make an appointment with miss guo guo teacher also notify her eat oranges, and when miss guo eat oranges, think of me and he agreed and guo teacher, then send notification, guo teacher messages are received, and I do what I want to do things as soon as (gather together in the past, and he eat together).
In programming, this saves the resource cost of repeated retrieval and allows for faster feedback.
- Observation Object (Subject) : A method that has the two necessary identifiers to notify the observer owned by the current instance. A method to add an observer to the current instance so you know who to notify.
- Observer: A method that has a necessary identifier to notify an instance of status updates.
Contact information:
- Through its own internal notification function, the Subject calls the callback function corresponding to all observers in the list of observers to notify observers.
- An Observer passes itself to its list of observers by calling the add method on the Subject.
A fusion of the responsive and observer modes
Let’s first take a look at what files are needed to implement a basic Vue, let’s start with a large oneminiVueAs a demo of the discussion.
Let’s first introduce what these files do one by one;
- Vue is an event bus file, which will take the data from the object description text, and then hijack all the data through the _proxyData function to facilitate access to the subsequent properties.
- The observer file, the walk method here, recurses through each property in the data. Then create a new Dep for each attribute in defineReactive to store its own dependencies (observers), the root of the object.defineProperty response, and add its dep. target to the Dep list if it triggers a GET. So this step is collecting dependencies, so if you take my value, you’re interested in me, so I’m going to add you to my observer table, and if I hit set, I’m going to hit notify in DEP, and I’m going to tell all observers that the data has been updated, so go ahead.
- The DEp file is a Subject in the observer mode, which has a container to store the observations, methods to add observers, and methods to notify observers.
- The compiler file is a processing file designed to handle collections of parsing instructions, difference expressions, and so on.
- The watcher file is the observer in the observer mode, which has a method to update the view and calls the deP method to add the observer.
We see that there are only three files left in addition to the event bus file and the processing file, and these three files are the root of the Vue response. Let’s look at the diagram
summary
We can see that the DEp file and the watcher file, which is an implementation of the observer mode, and the observe file is the bridge between them, by hijacking the GET and set operations, telling the DEP when to add an observer, and informing the observer, which forms automation. When data is being read, Create a connection between the observer and the observed, and notify the observed to send a notification message when the data changes, thus achieving responsiveness.
The Vue instance is initialized
Let’s take a look at the source code for each of these steps
- The first is the entry file vue.js. through
defineProperty
Complete the proxy for all Data in Data.
class Vue {
constructor (options) {
this.$options = options || {} // save options
this.$el = typeof options.el === 'string' ? document.querySelector(options.el)
:options.el // get dom
this.$data = options.data // get data
this.$methods = options.methods
// 1. Data All data hijacked agent
this._proxyData(this.$data)
// 2. Call the observe object to listen for data changes
new Observer(this.$data)
// 3. Call the Compiler object to parse the instructions and difference expressions
new Compiler(this)
}
_proxyData (data) {
// Iterate over all data
Object.keys(data).forEach(key= > {
// Hijack each data via defineProperty
Object.defineProperty(this, key, {
enumerable: true.configurable: true,
get () {
return data[key]
},
set (newValue) {
if (data[key] === newValue) {
return
}
data[key] = newValue
}
})
})
}
}
Copy the code
- Then go to observe-js and iterate over all the data, collecting dependencies if it’s GET and sending notifications if it’s set
class Observer {
constructor(data) {
this.walk(data)
}
walk (data) { // Loop through data
if(! data ||typeofdata ! = ='object') {
return
}
Object.keys(data).forEach(key= > {
this.defineReactive(data, key, data[key])
})
}
defineReactive (obj, key, val) {
let that = this
this.walk(val) // If val is an object, give it a method that fires when it is bound to get and set
let dep = new Dep() // Responsible for collecting dependencies and sending notifications
Object.defineProperty(obj, key, {
configurable: true.enumerable: true.get() {
Dep.target && dep.addSub(Dep.target) // Collect dependencies
return val // If obj[key] is used, it becomes an infinite loop
},
set(newValue) {
if (newValue === val) {
return
}
val = newValue
that.walk(newValue) // This may be an object, which is called inside the set function
dep.notify() // Send a notification}}}})Copy the code
Summary: This file is a reactive automation implementation that uses a walk method to recursively iterate through each property in the data, then creates a new Dep for each object in defineReactive to store its own dependencies (observers), and then opens the object’s enumerable and writable properties. And define a method when a set and get are triggered.
If a GET is triggered, add its dep. target to the Dep list. This step is collecting dependencies. You took my note that you were interested in me, so I added you to my observer list. If set is triggered, it indicates that the data has changed, and the notify in DEP is triggered, notifying all observers that the data has been updated, and acting quickly, so as to achieve a response.
- Then go to watcher.js. In this case, a static property of Dep class target is used to record the current watcher object. By using the property of Dep class, trigger GET, and make its own dependency added to the list of observers to form the observer pattern. Then, Dep. Upon receiving notification from the observed object, the view updates by calling its own update method.
class Watcher {
constructor (vm, key, cb) {
this.vm = vm
// Attribute name in data
this.key = key
// The callback function updates the view
this.cb = cb
// Record the watcher object to the static property target of the Dep class
Dep.target = this
// Triggers the GET method, which calls addSub
this.oldValue = vm[key]
Dep.target = null
}
// Notify the view to update when data changes
update () {
let newValue = this.vm[this.key]
if (this.oldValue === newValue) {
return
}
this.cb(newValue)
}
}
Copy the code
Let’s take a final look at the initialization flowchart
According to the arrow, the first initialization phase is done in three steps:
- Call _proxyData in vue.js for data hijacking.
- Go to observe.js and create a Dep (object to Observe), which has a container to store a list of observers, and a method to add observers and notify observers.
- Create a Watcher. He is an observer in the observer pattern, so he has a response method to notify updates.
Summary: These three steps are the core of Vue responsiveness and the implementation of the observer pattern, where the Watcher adds itself to the list of observers to the object of observation by triggering get. Dep realizes the response by notifying all observers by traversing its own observer list.
View rendering and view updating
Let’s take a look at the main content of the compiler.js file
- Compile: Edit the template
- CompileElement: compileElement nodes
- CompileText: Compilestext nodes to handle differential expressions
/ / parsing v - model
modelUpdater (node, value, key) {
node.value = value
new Watcher(this.vm, key, (newValue) = > { // Create a watcher object to update the view when data changes
node.value = newValue
})
// Bidirectional binding
node.addEventListener('input'.() = > {
this.vm[key] = node.value
})
}
// Compile the template
compile (el) {
let childNodes = el.childNodes
Array.from(childNodes).forEach(node= > {
if (this.isTextNode(node)) { // Process text nodes
this.compileText(node)
} else if(this.isElementNode(node)) { // Process element nodes
this.compileElement(node)
}
// If there are children, call recursively
if (node.childNodes && node.childNodes.length > 0) {
this.compile(node)
}
})
}
// Compile element nodes to process instructions
compileElement (node) {
// console.log(node.attributes)
if (node.attributes.length) {
Array.from(node.attributes).forEach(attr= > { // Iterate over all element nodes
let attrName = attr.name
if (this.isDirective(attrName)) { // Check whether it is an instruction
attrName = attrName.indexOf(':') > -1 ? attrName.substr(5) : attrName.substr(2) // Get the value after v-
let key = attr.value // Get the data name
this.update(node, key, attrName)
}
})
}
}
// Compile the text node to handle differential expressions
compileText (node) {
// Get the value in {{}}
// console.dir(node) // console.dir => Convert to object
let reg = / \ {\ {(. +?) \} \} /
let value = node.textContent
if (reg.test(value)) {
let key = RegExp.$1.trim() // Return the first matched string, excluding Spaces
node.textContent = value.replace(reg, this.vm[key])
new Watcher(this.vm, key, (newValue) = > { // Create a watcher object to update the view when data changes
node.textContent = newValue
})
}
}
Copy the code
Let’s look at another flow chart
Summary: Compile converts elements into data models, which are ordinary JavaScript objects called vNode objects, and then traverses vNode objects, which are classified into element nodes, text nodes and data according to the identifier, respectively into different processing functions. And create a Watcher object, and then trigger get in the Watcher object to achieve the response, the synchronization will updata update data, converted to the real DOM, complete the page rendering, the update is so repeated.
Overall response flow chart
Finally, let’s do a knowledge review according to the flow chart. The first step is to initialize the three steps:
- Using _proxyData to hijack Data
- Create a Dep (Observe target) object,
- Then create the Watcher object,
Then start the render phase
- Template compilation occurs after vm.render () is triggered
- Complie (template) compiles the element into a VNode object, traverses the object, creates a Watcher, and adds dependencies to complete the response.
- Updata updates the data, then converts it to the real DOM and completes the rendering.
So far, Vue responsive principle and its design pattern should be very clear, if you have questions, welcome to leave a message.
Welcome friends who want to learn and progress together, join my study group, where you can discuss advanced skills and share your latest learning content!
If you think the article is good, you can click a like, give the author a little encouragement, thank you, you can choose to add my wechat friend, I will pull you into the group.