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;
        }
    }
  • 相关阅读:
    圆圈中最后剩下的数字 【微软面试100题 第十八题】
    第一个只出现一次的字符 【微软面试100题 第十七题】
    从上往下打印二叉树 【微软面试100题 第十六题】
    二叉树的镜像 【微软面试100题 第十五题】
    和为s的两个数字 【微软面试100题 第十四题】
    链表中倒数第k个结点 【微软面试100题 第十三题】
    求1+2+...+n 【微软面试100题 第十二题】
    求二叉树中结点的最大距离 【微软面试100题 第十一题】
    翻转句子中单词的顺序 【微软面试100题 第十题】
    二叉搜索树的后序遍历序列 【微软面试100题 第九题】
  • 原文地址:https://www.cnblogs.com/wzndkj/p/10236872.html
Copyright © 2011-2022 走看看