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;
    }
}

 

 

 

 

 

posted @ 2019-01-08 07:02  wzndkj  阅读(268)  评论(0编辑  收藏  举报