directory
(1) the vueTwo-way binding
Overall Demo code
② Code idea
③ Vue initialization process and data modification process
(4) the watcher object
① VUE bidirectional binding overall code Demo
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<h2 id="test"></h2>
<button id="but">+ 1</button>
<script>
class Watcher { //
constructor(vm, exp, cb) {
this.vm = vm
this.exp = exp
this.cb = cb
this.value = this.get() // Call the following get method when watcher is instantiated
}
get() {
Dep.target = this // Cache the current this, which is a Watcher object
const value = this.vm.data[this.exp] // Get the value of the corresponding attribute,
// The get method of the monitored data is called, from which the dep.depend() method is called.
// Since dep. target exists, we add a dependency, the Watcher object, to the subs array in the Dep instance.
Dep.target = null
return value
}
update() { // When data changes, the set method that listens for data is called,
// The deP instance calls notify to call update for each dependency in the subs array,
// The update method calls the callback function to update the contents of the element.
const value = this.vm.data[this.exp]
this.cb.call(this.vm,value)
}
}
class Dep { // The deP instance is used to collect dependencies
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
depend() {
if (Dep.target) {
this.addSub(Dep.target)
console.log(this.subs)
}
}
notify() {
const subs = this.subs.slice()
for (let i = 0; i < subs.length; i++) {
subs[i].update()
}
}
}
class Observer {
defineReactive(data) {
if(! data ||typeofdata ! ='object') return
let dep = new Dep()
Object.keys(data).forEach(key= > {
let value = data[key]
this.defineReactive(value) // If value is still an object, the object recursively continues to use defineReactive for deep binding
Object.defineProperty(data, key, { // Use this method to listen for changes in object properties
enumerable: true.configurable: true.get: function () {
console.log(value, 'get method')
dep.depend()
return value
},
set: function (newValue) {
console.log(value, 'set method')
if (value === newValue) return
value = newValue
dep.notify()
}
})
})
}
}
class Vue {
constructor(options = {}) {
this.el = options.el
this.exp = options.exp
this.data = options.data
el.innerHTML = this.data[this.exp]
let observer = new Observer()
observer.defineReactive(this.data)
new Watcher(this.this.exp, function(val) {
el.innerHTML = val
})
return this}}let el = document.getElementById("test")
let vue = new Vue({
el: el,
exp: 'count'.data: {
count: 1}})let but = document.getElementById("but")
but.addEventListener('click'.() = > {
vue.data.count += 1
})
</script>
</body>
</html>
Copy the code
② Code idea
1) Create one firstObserver
Class to listen for data in an object.
class Observer {
defineReactive(data) {
if(! data ||typeofdata ! ='object') return
let dep = new Dep()
Object.keys(data).forEach(key= > {
let value = data[key]
this.defineReactive(value) // If value is still an object, the object recursively continues to use defineReactive for deep binding
Object.defineProperty(data, key, { // Use this method to listen for changes in object properties
enumerable: true.configurable: true.get: function () {
console.log(value, 'get method')
dep.depend()
return value
},
set: function (newValue) {
console.log(value, 'set method')
if (value === newValue) return
value = newValue
dep.notify()
}
})
})
}
}
Copy the code
2) Then create oneDep
class
Collect dependencies (Watcher instances) for later centralized notification to watcher(subscribers).
class Dep { // The deP instance is used to collect dependencies
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
depend() {
if (Dep.target) {
this.addSub(Dep.target)
console.log(this.subs)
}
}
notify() {
const subs = this.subs.slice()
for (let i = 0; i < subs.length; i++) {
subs[i].update()
}
}
}
Copy the code
3) Create oneWatcher
Class to update views when data changes.
class Watcher { //
constructor(vm, exp, cb) {
this.vm = vm
this.exp = exp
this.cb = cb
this.value = this.get() // Call the following get method when watcher is instantiated
}
get() {
Dep.target = this // Cache the current this, which is a Watcher object
const value = this.vm.data[this.exp] // Get the value of the corresponding attribute,
// Call the get method of the monitored data,
// The dep.depend() method is called from this. Since dep.target exists,
// Add a dependency, the Watcher object, to the subs array in the Dep instance.
Dep.target = null
return value
}
update() { // When data changes, the set method that listens for data is called,
// The deP instance calls notify to call update for each dependency in the subs array,
// The update method calls the callback function to update the contents of the element.
const value = this.vm.data[this.exp]
this.cb.call(this.vm,value)
}
}
Copy the code
4) Finally create a Vue class for initialization.
This allows for responsive handling of objects.
class Vue {
constructor(options = {}) {
this.el = options.el
this.exp = options.exp
this.data = options.data
el.innerHTML = this.data[this.exp] // Initialize the page content
let observer = new Observer()
observer.defineReactive(this.data) // Listen for data
new Watcher(this.this.exp, function(val) { // Create the watcher instance and call the constructor.
el.innerHTML = val
})
return this}}Copy the code
③ Vue initialization process and data modification process
1) Initialization process:
Instantiate Vue — > call defineReactive method to listen for data in an object (recursively if property is obj) — > The Watcher constructor is called — get method that triggers monitored data — Dep collects dependencies.
2) Process after data modification:
Data is modified — > trigger the set method of the listened data — > call dep.notify — > trigger each dependent update method that has been collected in the subs array (defined in Watcher) — > View update.
(4) the watcher object
Dep. Target is a Watcher object. The purpose of collecting a Watcher object in a Dep instance is to call the update method of the collected Watcher object to update the view when the data is updated.