zoukankan      html  css  js  c++  java
  • MVVM框架

    1、了解MVVM框架吗?
    2、谈谈你对MVVM的认识?
    3、双向绑定是什么原理,可以写出来吗?
    4、使用了什么设计模式?
    5、生命周期是什么?
    6、有看过源码吗?
    1、了解MVVM框架吗?
    vue,react,angular都是用的MVVM框架,vue开源协议是MIT,react闹过一出,所以用vue的人多。
    2、谈谈你对MVVM的认识?
    这个问题没有标准答案,可以先聊MVC,再聊MVVM,MVVM是MVC(model view control)延伸过来的
    MVVM(model view viewModel),vuejs处理的就是view和model之间的viewModel这个核心枢纽,view里面内容改变了,viewModel就改了,viewModel改了,view就自动改了,viewModel数据改变了,怎么改变model,就是ajax请求,传递到服务器端
    对比mvvm和mvc
    3、双向绑定是什么原理,可以写出来吗?
    双向绑定是mvvm最核心的问题,大大减轻了开发
    view <-> data
    data到view 数据驱动页面(之前都是这种模式)
    view到data 比如input框,改变了值,通过v-model自动同时改变view,data的值
    如何实现的?从服务端拿到了数据,赋值给data,data的数据变了,那么view自动就变了,那么view怎么知道data变了。比如有个变量a为1,这个时候a改成了2,之前是获取dom,自己赋值,展示。这里面是怎么实现的,Object.defineProperty,react,vue是基于这个来做的。这个api能监听到data变化,里面有个回调函数,里面写了view与data对数据的操作。
    Object.defineProperty
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
    就是说你有一个对象,这也就是大家为什么说vue里面的data是一个对象那个,可以定义一个属性,然后返回这个对象
    里面有个get,set,读的时候会触发get操作,改变的时候会触发set操作,赋值的时候触发了set,这里会检测值有没有改变,如果改变了,就会触犯回调函数 
     
    所以,双向绑定的基石就是这个Object.defineProperty
     
    4、使用了什么设计模式?
    观察者模式,什么是观察者模式呢?第一个要有一个监听者,叫observer,他是来监听data发生的变化,,他发生变化以后,会通知所有的观察者列表,什么是观察者列表,比如我data中有一个a,a=1,然后有好多观察者列表在关注这个值,observer底层就是Object.defineProperty,他观察到变化,会对观察者列表对触发,比如观察者列表有,a,b,c,d,告诉observer,如果a发生了变化告诉我,然后observer监听到变化,就会触发观察者列表,这个列表中会有一个更新函数,通知了列表,列表会自动调用update函数,完了更新完就会更新view了。observer只是负责监听,他怎么知道谁是a,b,c,d,这个任务交给我了订阅watcher


    5、生命周期是什么?
    https://www.cnblogs.com/wzndkj/p/9612647.html


    6、有看过源码吗?
    源码里面东西太多,只要把里面最核心的东西,思路理清楚就可以了,下面有一段伪代码
    index.html
    <!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>
    </head>
    
    <style>
        #app {
            text-align: center;
        }
    </style>
    <body>
        <div id="app">
            <h2>{{title}}</h2>
            <input v-model="name">
            <h1>{{name}}</h1>
            <button v-on:click="clickMe">click me</button>
        </div>
    </body>
    <script src="js/observer.js"></script>
    <script src="js/watcher.js"></script>
    <script src="js/compile.js"></script>
    <script src="js/index.js"></script>
    
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {
                title: 'vue code',
                name: 'imooc'
            },
            methods: {
                clickMe: function() {
                    this.title = 'vue code click';
                }
            },
            mounted: function () {
                window.setTimeout(() => {
                    this.title = 'timout 1000';
                }, 1000);
            }
        })
    </script>
    </html>

    index.js

    function Vue (options) {
        var self = this;
        this.data = options.data;
        this.methods = options.methods;
    
        Object.keys(this.data).forEach(function(key){
            self.proxyKeys(key);
        })
        observe(this.data); // 实现了data的监听
        new Compile(options.el, this);
        options.mounted.call(this);
    }
    
    Vue.prototype = {
        proxyKeys: function(key) {
            var self = this;
            Object.defineProperty(this, key, {
                enumerable: false,
                configurable: true,
                get: function() {
                    return self.data[key];
                },
                set: function(newVal) {
                    self.data[key] = newVal;
                }
            })
        }
    }

    observer.js

    /**
     * 
     * @param {*} data
     * Observer是vue实例化的时候调用的
     */
    function Observer (data) {
        this.data = data;
        this.walk(data);
    }
    
    Observer.prototype = {
        walk: function (data) {
            var self = this;
            Object.keys(data).forEach(function (key) {
                self.defineReactive(data, key, data[key]);
            });
        },
        defineReactive: function (data, key, val) {
            var dep = new Dep(); // Dep对象,维护的是观察者列表
            var childObj = observe(val);
            Object.defineProperty(data, key, {
                enumerable: true,
                configurable: true,
                get: function getter() {
                    if (Dep.target) {
                        dep.addSub(Dep.target);
                    }
                    return val;
                },
                set: function setter (newVal) { // 数据变了,通知观察者
                    if (newVal === val) {
                        return;
                    }
                    val = newVal;
                    dep.notify();
                }
            })
        }
    }
    
    function observe (value, vm) {
        if (!value || typeof value !== 'object') {
            return;
        }
        return new Observer(value);
    }
    
    function Dep () {
        this.subs = [];
    }
    Dep.prototype = {
        addSub: function (sub) {
            this.subs.push(sub);
        },
        notify: function() {
            this.subs.forEach(function (sub) {
                sub.update();
            })
        }
    }
    Dep.target = null;

    watcher.js

    function Watcher (vm, exp, cb) {
        this.cb = cb;
        this.vm = vm;
        this.exp = exp;
        this.value = this.get(); // 将自己添加到订阅器的操作
    }
    
    Watcher.prototype = {
        update: function() {
            this.run;
        },
        run: function() {
            var value = this.vm.data[this.exp];
            var oldVal = this.value;
            if (value !== oldVal) {
                this.value = value;
                this.cb.call(this.vm, value, oldVal);
            }
        },
        get: function(){
            Dep.target = this; // 缓存自己
            var value = this.vm.data[this.exp]; // 强制执行监听器里的get函数
            Dep.target = null; // 释放自己
            return value;
        }
    }

    compile.js

    // 外层容器的对象
    function Compile (el, vm) {
        this.vm = vm;
        this.el = document.querySelector(el);
        this.fragment =  null;
        this.init();
    }
    
    Compile.prototype = {
        init: function() {
            if (this.el) {
                this.fragment = this.nodeToFragment(this.el);
                this.compileElement(this.fragment);
                this.el.appendChild(this.fragment);
            } else {
                console.log('Dom元素不存在');
            }
        },
        nodeToFragment: function(el) {
            var fragment = document.createDocumentFragment();
            var child = el.firstChild;
            while (child) {
                // 将Dom元素移入fragment中
                fragment.appendChild(child);
                child = el.firstChild;
            }
            return fragment;
        },
        compileElement: function (el) {
            /**
             * 把所有子节点遍历出来
             */
            var childNodes = el.childNodes;
            var self = this;
            [].slice.call(childNodes).forEach(function (node) {
                var reg = /{{(.*)}}/;
                var text = node.textContent;
    
                if (self.isElementNode(node)) {
                    self.Compile(node);
                } else if (self.isTextNode(node) && reg.test(text)) {
                    self.compileText(node, reg.exec(text)[1]);
                }
    
                if (node.childNodes && node.childNodes.length) {
                    self.compileElement(node);
                }
            });
        },
        compile: function (node) {
            var nodeAttrs = node.attributes;
            var self = this;
            Array.prototype.forEach.call(nodeAttrs, function(attr) {
                var attrName = attr.name;
                if (self.isDirective(attrName)) {
                    var exp = attr.value;
                    var dir = attrName.substring(2);
                    if (self.isEventDirective(dir)) { // 事件指令
                        self.compileEvent(node, self.vm, exp, dir);
                    } else { // v-model指令
                        self.compileModel(node, self.vm, exp, dir);
                    }
                    node.removeAttribute(attrName);
                }
            })
        },
        compileText: function(node, exp) {
            var self = this;
            var initText = this.vm[exp];
            this.updateText(node, initText);
            new Watcher(this.vm, exp, function(value) {
                self.updateText(node, value);
            })
        },
        compileEvent: function (node, vm, exp, dir) {
            var eventType = dir.split(':')[1];
            var cb = vm.methods && vm.methods[exp];
    
            if (eventType && cb) {
                node.addEventListener(eventType, cb.bind(vm), false);
            }
        },
        compileModel: function (node, vm, exp, dir) {
            var self = this;
            var val = this.vm[exp];
            this.modelUpdater(node, val);
            new Watcher(this.vm, exp, function (value) {
                self.modelUpdater(node, value);
            });
    
            // 实现双向绑定的
            node.addEventListener('input', function (e) {
                var newValue = e.target.value;
                if (val === newValue) {
                    return;
                }
                self.vm[exp] = newValue;
                val = newValue;
            })
        },
        updateText: function (node, value) {
            node.textContent = typeof value === 'undefined' ? '' : value;
        },
        modelUpdater: function (node, value, oldValue) {
            node.value = typeof value === 'undefined' ? '' : value;
        },
        isDirective: function (attr) {
            return attr.indexOf('v-') == 0;
        },
        isEventDirective: function (dir) {
            return dir.indexOf('on:') === 0;
        }
    }
  • 相关阅读:
    HYSBZ 3813 奇数国
    HYSBZ 4419 发微博
    HYSBZ 1079 着色方案
    HYSBZ 3506 排序机械臂
    HYSBZ 3224 Tyvj 1728 普通平衡树
    Unity 3D,地形属性
    nginx 的naginx 种包含include关键字
    Redis 出现NOAUTH Authentication required解决方案
    mysql 8.0出现 Public Key Retrieval is not allowed
    修改jar包里的源码时候需要注意的问题
  • 原文地址:https://www.cnblogs.com/wzndkj/p/10236872.html
Copyright © 2011-2022 走看看