一、Vue数据双向绑定的原理:

        1、实例化一个app对象——定义一个Vue类(类的参数是一个对象,包括id属性、数据和方法等)

                id属性:为了获取原生DOM对象

        2、循环通过set,get方法实现app对象的数据代理

        3、观察数据动态,对更新的数据进行管理——设置一个劫持事件        

        4、设置一个观察对象,对节点的内容进行更新——定义一个watch类

        5、把view的数据和事件进行绑定(参数是DOM对象)

二、结果展示:

        1、文本框输入内容,全部数据同时被修改。

        2、点击修改内容,全部数据同时被修改。

 三、代码展示:

<!DOCTYPE html>
<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>
    <style>
        #app{
            width: 200px;
            height: 200px;
            margin: 100px auto;
        }
    </style>
</head>

<body>
    <div id="app">
        <input type="text" v-model="msg">
        <h1>{{msg}}</h1>
        <!-- v-html 绑定的是h1对象上的innerHtml -->
        <h1 v-html="msg"></h1>
        <button type="button" @click="changeEvent">修改内容</button>
    </div>

    <script>
        class Vue {
            constructor(options) { //options是传入的对象
                // 通过选择器获取根对象
                this.$el = document.querySelector(options.el);
                // 数据创造之前
                if(typeof options.beforeCreate == 'function') {
                    options.beforeCreate.bind(this)();
                }
                this.$options = options;
                // 设置一个对象用来修改更新事件
                this.$watchEvent = {};
                // 代理options的data数据
                this.proxyData();
                // 劫持事件,一旦修改则触发某件事情
                this.observe();
                // 数据创造之后
                if(typeof options.created == 'function') {
                    options.created.bind(this)();
                }
                // 挂载前
                if(typeof options.beforeMount == 'function') {
                    options.beforeMount.bind(this)();
                }
                // 把view的数据和事件进行绑定
                this.compile(this.$el);
                // 挂载后
                if(typeof options.mounted == 'function') {
                    options.mounted.bind(this)();
                }
            }
            // 劫持事件
            observe() {
                for (let key in this.$options.data) {
                    // 获取此处value保存
                    let value = this.$options.data[key];
                    let that = this;
                    Object.defineProperty(this.$options.data, key, {
                        configurable: false,
                        enumerable: true,
                        // value:"定义值"
                        // writeable:true,false 定义是否可以进行修改 
                        set(val) {
                            value = val;
                            // 触发以这个key值得更新事件
                            if (that.$watchEvent[key]) {
                                that.$watchEvent[key].forEach((item, index) => {
                                    item.updata();
                                })
                            }
                        },
                        get() {
                            // 获取this[key]时,即返回options的data[key]
                            // 触发获取内容事件
                            return value;
                        }
                    });
                }
            }
            // 循环通过set,get方法来实现代理数据
            proxyData() {
                for (let key in this.$options.data) {
                    // console.log(key);  //msg username
                    // 定义属性的方法 参数:对象、属性、配置的信息
                    Object.defineProperty(this, key, {
                        configurable: false, //是否可再定义 
                        enumerable: true, //是否可迭代,即使用for循环
                        // value:"定义值"
                        // writeable:true,false 定义是否可以进行修改 
                        set(val) {
                            this.$options.data[key] = val;
                        },
                        get() {
                            // 获取this[key]时,即返回options的data[key]
                            return this.$options.data[key];
                        }
                    });
                }
            }
            // 把view的数据和事件进行绑定
            compile(cNode) {
                // 获取所有节点进行循环
                cNode.childNodes.forEach((node, index) => {
                    if (node.nodeType == 1) {
                        // 元素类型
                        if (node.hasAttribute('v-html')) {
                            let vmKey = node.getAttribute('v-html').trim(); // msg
                            if (this.hasOwnProperty(vmKey)) {
                                node.innerHTML = this[vmKey];
                                let watcher = new Watch(this, vmKey, node, 'innerHTML');
                                if (this.$watchEvent[vmKey]) {
                                    this.$watchEvent[vmKey].push(watcher);
                                } else {
                                    this.$watchEvent[vmKey] = [];
                                    this.$watchEvent[vmKey].push(watcher);
                                }
                                // 删除节点事件
                                node.removeAttribute('v-html');
                            }
                        }
                        // 判断是否有v-model属性
                        if (node.hasAttribute('v-model')) {
                            let vmKey = node.getAttribute('v-model').trim();
                            if (this.hasOwnProperty(vmKey)) {
                                node.value = this[vmKey];
                                let watcher = new Watch(this, vmKey, node, 'value');
                                if (this.$watchEvent[vmKey]) {
                                    this.$watchEvent[vmKey].push(watcher);
                                } else {
                                    this.$watchEvent[vmKey] = [];
                                    this.$watchEvent[vmKey].push(watcher);
                                }
                            }
                            node.addEventListener('input', (event) => {
                                this[vmKey] = node.value;
                            })
                            // 删除节点事件
                            node.removeAttribute('v-model');
                        }
                        // 判断是否绑定@click属性
                        if (node.hasAttribute('@click')) {
                            let vmKey = node.getAttribute('@click').trim(); // changeEvent
                            node.addEventListener('click', (event) => {
                                this.eventFn = this.$options.methods[vmKey].bind(this);
                                this.eventFn(event);
                            })
                        }
                        // 如果还有子节点
                        if (node.childNodes.length > 0) {
                            this.compile(node);
                        }
                    }
                    if (node.nodeType == 3) {
                        // 文本类型
                        let reg = /\{\{(.*?)\}\}/g;
                        let text = node.textContent;
                        node.textContent = text.replace(reg,(match,vmKey)=>{
                            vmKey = vmKey.trim(); // 去除空格
                            if (this.hasOwnProperty(vmKey)) {
                                node.innerHTML = this[vmKey];
                                let watcher = new Watch(this, vmKey, node, 'textContent');
                                if (this.$watchEvent[vmKey]) {
                                    this.$watchEvent[vmKey].push(watcher);
                                } else {
                                    this.$watchEvent[vmKey] = [];
                                    this.$watchEvent[vmKey].push(watcher);
                                }
                            }
                            return this[vmKey];
                        });
                    }
                })
            }
        }
        // 设置一个watch对象
        class Watch {
            //属性:DOM对象、key、绑定的节点对象、节点属性、节点类型
            constructor(vm, key, node, attr, nType) {
                // 实例化的app对象
                this.vm = vm;
                // 绑定的vm触发的属性
                this.key = key;
                // 此vm[key]数据绑定的HTML结点
                this.node = node;
                // vm数据所绑定的HTML结点的属性名称
                this.attr = attr;
            }
            updata() {
                // 更新前
                if(typeof options.beforeUpdate == 'function') {
                    options.beforeUpdate.bind(this)();
                }
                this.node[this.attr] = this.vm[this.key];
                // 更新后
                if(typeof options.updated == 'function') {
                    options.updated.bind(this)();
                }
            }
        }
    </script>
    <script>
        let options = {
            el: "#app",
            data: {
                msg: "hello zzs",
                username: "zzs"
            },
            methods: {
                changeEvent: function () {
                    this.msg = "hello vue"
                }
            },
            // 创造之前
            beforeCreate() {
                console.log("beforeCreate")
            },
            // 创造
            created() {
                console.log("created")
            },
            // 渲染之前
            beforeMount() {
                console.log("beforeMount")
            },
            // 渲染之后
            mounted() {
                console.log("mounted")
            },
            beforeUpdate() {
                // 数据更改,但内容未更改之前
                console.log("beforeUpdata")
            },
            updated() {
                // 内容更新完毕
                console.log("updated")
            }
        };
        let app = new Vue(options);

        console.log(app);
    </script>
</body>

</html>

Logo

前往低代码交流专区

更多推荐