Two-way binding principle of Vue 3.0

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/weixin_44489221/article/details/102668017

proxy method

vue.js is the use of data in conjunction with hijacking a publisher - way mode subscribers to hijack each attribute by new Proxy () setter, getter, posted a message to subscribers when data changes, triggering the corresponding listener callback.

Vue difference between 3.0 and Vue 2.0 is the only way to change the data hijacking Object.defineProperty to Proxy agent, other code unchanged. View the realization of the principle of two-way binding Vue 2.0

Code for specific implementation process is as follows:

  1. Defined constructor
    function Vue(option){
        this.$el = document.querySelector(option.el);   //获取挂载节点
        this.$data = option.data;
        this.$methods = option.methods;
        this.deps = {};     //所有订阅者集合 目标格式(一对多的关系):{msg: [订阅者1, 订阅者2, 订阅者3], info: [订阅者1, 订阅者2]}
        this.observer(this.$data);  //调用观察者
        this.compile(this.$el);     //调用指令解析器
    }
    
  2. Defined command parser
    Vue.prototype.compile = function (el) {
        let nodes = el.children; //获取挂载节点的子节点
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            if (node.children.length) {
                this.compile(node) //递归获取子节点
            }
            if (node.hasAttribute('l-model')) { //当子节点存在l-model指令
                let attrVal = node.getAttribute('l-model'); //获取属性值
                node.addEventListener('input', (() => {
                    this.deps[attrVal].push(new Watcher(node, "value", this, attrVal)); //添加一个订阅者
                    let thisNode = node;
                    return () => {
                        this.$data[attrVal] = thisNode.value //更新数据层的数据
                    }
                })())
            }
            if (node.hasAttribute('l-html')) {
                let attrVal = node.getAttribute('l-html'); //获取属性值
                this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者
            }
            if (node.innerHTML.match(/{{([^\{|\}]+)}}/)) {
                let attrVal = node.innerHTML.replace(/[{{|}}]/g, '');   //获取插值表达式内容
                this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者
            }
            if (node.hasAttribute('l-on:click')) {
                let attrVal = node.getAttribute('l-on:click'); //获取事件触发的方法名
                node.addEventListener('click', this.$methods[attrVal].bind(this.$data)); //将this指向this.$data
            }
        }
    }
    
  3. Definition of the observer (in which a distinction code)
    Liu.prototype.observer = function (data) {
        const that = this;
        for(var key in data){
            that.deps[key] = [];    //初始化所有订阅者对象{msg: [订阅者], info: []}
        }
        let handler = {
            get(target, property) {
                return target[property];
            },
            set(target, key, value) {
                let res = Reflect.set(target, key, value);
                var watchers = that.deps[key];
                watchers.map(item => {
                    item.update();
                });
                return res;
            }
        }
        this.$data = new Proxy(data, handler);
    }
    
  4. Definition of subscribers
    function Watcher(el, attr, vm, attrVal) {
        this.el = el;
        this.attr = attr;
        this.vm = vm;
        this.val = attrVal;
        this.update(); //更新视图
    }
    
  5. Update View
    Watcher.prototype.update = function () {
        this.el[this.attr] = this.vm.$data[this.val]
    }
    

Vue.js defined above code in a file, where you need to use two-way binding can be introduced.
Try to use it:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="./vue.js"></script>
</head>
<body>
    <!--
        实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过new Proxy()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。就必须要实现以下几点:
            1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
            2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
            3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
            4、mvvm入口函数,整合以上三者
    -->
    <div id="app">
        <input type="text" l-model="msg" >
        <p l-html="msg"></p>
        <input type="text" l-model="info" >
        <p l-html="info"></p>
        <button l-on:click="clickMe">点我</button>
        <p>{{msg}}</p>
    </div>

    <script>
        var vm = new Vue({
            el: "#app",
            data: {
                msg: "恭喜发财",
                info: "好好学习, 天天向上"
            },
            methods: {
                clickMe(){
                    this.msg = "我爱敲代码";
                }
            }
        })
    </script>
</body>
</html>

Guess you like

Origin blog.csdn.net/weixin_44489221/article/details/102668017