directory

(1) the vueTwo-way bindingOverall 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 firstObserverClass 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 oneDepclass

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 oneWatcherClass 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.