current position:Home>Vue bidirectional binding principle
Vue bidirectional binding principle
2022-04-29 04:28:42【weixin_ forty-two million two hundred and fifty-four thousand a】
vue Two way binding principle
vue The most unique feature is its responsive system , When modifying data , The view updates accordingly
When one js Object to vue Instance as data Options ,vue Will traverse all of the objects property, And use Object.defindProperty Put these property Convert to getter/setter
these getter/setter Invisible to users , But internally, they can make vue Track dependence , Notify changes when properties are accessed and modified
Each component instance corresponds to a watcher example , It will render the exposed data in the process of component rendering property Record as dependency , When the setter When triggered , Will inform watcher, So that its associated components are re rendered
When interpreting the source code, first look at some concepts :
data:vue Data items in the instance
observer: The observer of the data , Read and write operation of monitoring data
dep: Message subscriber , Have a collection of subscribers 、 Release updated features
watcher: Subscribers to the message , You can subscribe to dep, Then accept dep Publish the update and perform the update of the corresponding view or expression
dep and watcher The relationship between :dep It's a newspaper ,watcher It's the person who subscribes to the newspaper , Subscriptions establish a relationship between them , So whenever the data is updated , The corresponding subscribers will be notified
Collection dependency :watcher Add... To your own properties dep act
Collect subscribers :dep Add... To your own properties watcher act
The following figure is based on the source code , Some of my own understanding
Source version 2.6.11
First find vue The method of data processing initData:
// Source directory :src/core/instance/state.js
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
//<1>data Property agent
proxy(vm, `_data`, key)
}
}
//<2>observe Data binding
// observe data
observe(data, true /* asRootData */)
}
<1> General data The above properties are proxied to vm For instance , After that, you can go directly through vm.key visit data.key.
// Our data objects
var data = { a: 1 }
// The object is added to a Vue In the example
var vm = new Vue({
data: data
})
// Get... On this instance property
// Returns the corresponding field in the source data
vm.a == data.a // => true
If the official document is correct vue Explanation of examples , A data object is added vue After instance ,vm.a == data.a //true also vm ==this //true
Besides , Only when the instance is created already exists in data Medium property It's responsive . That is, if you add a new property, Such as vm.b = 1, that b Changes to the will not trigger updates . The solution is to set the initial value .
<2> Start data binding at ,asRootData, Here is the root data , Next, we will recursively bind deep objects
// Source directory :src/core/observer/index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { // Check whether the current data is observe too
ob = value.__ob__
} else if ( // Check whether the current data is an ordinary object , Not functions or Regexp , etc.
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
Observer
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this) // to value increase _ob_ attribute , As data has been observer A sign of observation
if (Array.isArray(value)) { //value Is an array
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
} else {
this.walk(value)
}
}
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
* Traversing objects , And call the corresponding function defineReactive Add... To the attribute getter and setter
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
/**
* Observe a list of Array items.
* Traversal array , And call the corresponding function observe to item add to getter and setter
*/
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
observer There are three classes of methods ,value The value passed in is ,dep: Dep example , Used to collect subscriptions and publish updates ,vmCount: The number of times the instance was called .walk: Operation methods on objects ,observeArray: Operation method of array
stay observer Class , First of all value increase _ob_ attribute , As data has been observer A sign of observation
as follows
You can see that the data has _ob_ sign , Indicates that it has been observed . Next , There are different operation methods for objects and arrays , For the object , Would call walk Method to traverse the properties of the object defineReactive Method binding . For arrays , Is to rewrite its seven methods
Here are defineReactive Methods specific analysis
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
//<1>/* Define a in a closure dep object */
const dep = new Dep()
// Returns the property descriptor corresponding to a self owned property on the specified object
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
// If you have defined getter and setter function , Take it out
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
//<2> Object's children recursively observe, And return the Observe object
let childOb = !shallow && observe(val)
//<3>
Object.defineProperty(obj, key, {
enumerable: true, // enumeration
configurable: true, // Modifiable and removable attributes
get: function reactiveGetter () {
// If there were getter Method is executed
const value = getter ? getter.call(obj) : val
if (Dep.target) { //<4>
// Do dependency collection
dep.depend()
if (childOb) {
// Dependency collection for child objects
childOb.dep.depend()
if (Array.isArray(value)) {
// If it is an array, you need to collect dependencies on each member , If the array member is still an array, recursion
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
// adopt getter Method to get the current value , Compare with the new value , If it is consistent, the following operations will not be carried out
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
//dep The object informs all observers
dep.notify()
}
})
}
In the code <1> It's about ,new One. dep example ;<2> If there are nested objects at , Then make a recursive call observe, This will ensure that each layer is observe
Code <3> Place is defineReactive The core content , adopt Object.defineProperty() Of get and set Method to perform corresponding operations on the attribute
about Object.defineProperty() Of get and set Method
get:
Attribute getter function , without getter, Then for undefined. When accessing this property , This function will be called . No parameters are passed in during execution , But it will pass in this object ( By inheritance , there this It does not have to be the object that defines the property ). The return value of this function will be used as the value of the property .
The default is undefined.
set:
Attribute setter function , without setter, Then for undefined. When the property value is modified , This function will be called . This method takes a parameter ( That is, the new value given ), The this object .
The default is undefined.
When a property is accessed , You will enter get Method , stay get in , The core content is to carry out the current watcher( That is to say Dap.target) and dep Binding between dep.depend(), If the current data has child objects , Then the child objects are also bound ; If it is an array, you need to collect dependencies on each member , If the array member is still an array, recursion
When a property is modified , You will enter set Method , stay set in , The core content is through dep.notify() To inform the current dep The subscriber data bound is updated
Array processing
stay Observer Class , There is such a treatment for arrays
if (Array.isArray(value)) { //value Is an array
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
}
If it's an array , Replace the original method in the prototype of the array with the modified array method that can intercept the response , To achieve the effect of listening to the response of array data changes .
If the current browser supports __proto__ attribute , Directly overwrite the native array method on the current array object prototype , If this property is not supported , The prototype of the array object is directly overwritten .
arrayMethods Is an override of the array method , Specific source code :
import { def } from '../util/index'
/* Get the prototype of the native array */
const arrayProto = Array.prototype
/* Create a new array object , Seven methods to modify the array on this object , Methods to prevent contamination of native arrays */
export const arrayMethods = Object.create(arrayProto)
/**
* Intercept mutating methods and emit events
*/
/* These array methods are overridden here , These methods of rewriting arrays without polluting the native array prototype , Intercepts changes in the members of the array , While performing native array operations dep Notify all associated observers for responsive processing */
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
.forEach(function (method) {
// cache original method
/* Cache the native methods of the array , Call... Later */
const original = arrayProto[method]
def(arrayMethods, method, function mutator () {
// avoid leaking arguments:
// http://jsperf.com/closure-with-arguments
let i = arguments.length
const args = new Array(i)
while (i--) {
args[i] = arguments[i]
}
/* Call the native array method */
const result = original.apply(this, args)
/* The newly inserted element of the array needs to be redone observe To be responsive */
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
inserted = args
break
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
/*dep Notify all registered observers for responsive processing */
ob.dep.notify()
return result
})
})
You can see , about push,unshift,splice These three methods , need observe, Changes to other methods are updated on the current index , So there's no need to execute ob.observeArray
copyright notice
author[weixin_ forty-two million two hundred and fifty-four thousand a],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/117/202204270551122287.html
The sidebar is recommended
- vue2. How to register and login with 0 + koa2 + mongodb
- How to set the background color for individual pages in Vue cli
- Vue2 how to load more data on the scroll bar
- How to use margin and padding in CSS
- How does vue2x implement the vueslidershow function of the responsive adaptive rotation component plug-in
- How does vue2 recursive component implement tree menu
- How to use the three selectors of CSS
- Same blog, different styles -- hexo alternative construction
- Optimize nginx HTTPS latency - see how I can speed up nginx by 30%?
- The development model in the era of consumer Internet only transfers industrial elements from offline to online
guess what you like
Advanced features of Vue
CSS shadow advanced, to achieve a more three-dimensional shadow effect!
Introduction to the number type method in JavaScript
Differences between nginx forward and reverse proxy
Front end schema form configuration generation scheme
Units that can be used by the rotate method of transform in CSS
Why CSS3 sticky doesn't work?
[bug resolution] webpack error: module not found Did you mean xxx
[bug resolution] can't perform a react state update on an unmounted component This is > a no-op, but it...
Remember to speed up the construction of a webpack
Random recommended
- Browser review hover element
- Vue insert iframe
- Vue realizes downloading pictures
- Some common problem-solving methods in Vue (sort out some truly effective methods that can be used in the project) (wait for continuous update and accumulation)
- About database data response to HTML page, Chinese appears? Solution to garbled code (multiple question marks)
- Ask a question about CSS selector
- When Vue jumps the route, the timer cannot be cleared in time
- Document is not defined
- Vuepress packaging deployment stepping on the road of the pit
- Vue quill editor stepping on pit record -- the echo style of rich text content is wrong
- The multi selection box of table in element is combined with paging to echo the bug step hole
- Vue project optimizes the loading speed of the first screen
- Use / deep / to report an error CSS (CSS selected expected)
- Nginx configuration
- Vue cli reverse proxy
- Vuex persistedstate plug-in - vuex persistent storage
- Automatic import of less
- Understanding and summary of CSS
- vue2. Implementation of X data response
- JavaScript content understanding
- Props in react
- JavaScript stack“
- Bootstrap blazor table component (III) intelligent generation
- Based on centos7 + nginx + Daphne + uwsgi + django3 2 + supervisor + mysql8 single architecture server production environment deployment (I)
- Differences between vue2 and vue3 named slots
- Vue3: Axios cross domain request problem
- The difference between vue2 and vue3: keep alive
- Configure nginx and SSL certificate for Django project under Windows Environment
- Ant Design Vue: a-table custom column
- Using jQuery in Vue
- Vue dynamic loading picture problem
- Icons using Alibaba vector icon library in Vue
- Java Android mobile phone automatic basic learning element positioning
- Rancher configuring HTTPS domain name to access graphic tutorial
- Building a blog with GitHub pages + hexo (7) how to delete a published article successfully solved: delete it at the same time deploy_ Git folder
- Eight blog views / hexubs
- Build a blog with GitHub pages + hexo (9) set the next theme of hexo blog, and only part of the home page is displayed (not the full text)
- Building a blog with GitHub pages + hexo (10) the next theme of hexo blog mathjax mathematical formula rendering problem
- Hexo/Github. IO configure Tencent cloud CDN
- Details of Vue to react useeffect