zoukankan      html  css  js  c++  java
  • 实现一个简单vue

    1. 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
    2. 实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
    3. 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。  
    <div id="app">
     <h2>{{title}}</h2>
     <input v-model="name">
     <h1>{{name}}</h1>
     <button v-on:click="clickMe">click me!</buttuon>
    </div>
    <script type="text/javascript">
     new Vue({
         el: '#app',
         data: {
             title: 'hello world',
             name: 'canfoo'
         },
         methods: {
             clickMe: function () {
                 this.title = 'hello world';
             }
         }
     })
    </script>
    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);
        new Compile(options.el, this);
        options.mounted.call(this);//所有事情处理好后执行mounted函数
    }
    
    Vue.prototype = {
        proxyKeys: function(key) {
            var self = this;
            Object.defneProperty(this, key, {
                enumerable: false;
                configurable: true;
                get: function getter () {
                    return self.data[key];
                },
                set: function setter (newVal) {
                    self.data[key] = newVal;
                }
            })
        }
    }
    
    function Observer(data) {
        Object.keys(data).forEach(function(key) {
            defineReactive(data, key, data[key]);
        })
    }
    
    function defineReactive(data, key, val) {
        var dep = new Dep();
        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 Dep () {
        this.subs = [];
    }
    
    Dep.prototype = {
        addSub: function(sub) {
            this.subs.push(sub);
        },
        notify: function() {
            this.subs.forEach(function(sub) {
                sub.update();
            });
        }
    };
    
    Dep.target = null;
    
    function Compile(el, vm) {
        this.vm = vm;
        this.el =  document.querySlector(el);
        this.fragment = null;
        this,init();
    }
    
    Compile.prototype = {
        init: function() {
            this.fragment = this.nodeToFragment(this.el);
            this.compileElement(this.fragment);
            this.el.appendChild(this.fragment);
        },
        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(test)[1]);
                }
                if(node.childNodes && node.childNodes.length) {
                    self.compileElement(node);
                }
            })
        },
        compile: function(node) {
            var nodeAttrs = node.atrributes;
            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);
                }
            })
        },
        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;
            })
        }
        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);
            })
    
        },
        modelUpdater: function(node, value, oldValue) {
            node.value = typeof value == 'undefined'?'':value;
        }
        updateText: function(node, value) {
            node.textContent = typeof value == 'undefined'?'':value;
        },
        isDirective: function(attr) {
            return attr.indexOf('v-') == 0;
        },
        isElementNode: function(node) {
            return node.nodeType == 1;
        },
        isTextNode: function(node) {
            return node.nodeType == 3;
        },
        isEventDirective: function(dir) {
            return dir.indexOf('on:') === 0;
        }
    }
    
    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;
        }
    }
  • 相关阅读:
    CentOS 7 安装MySQL 5.7
    Introduction to BGP (4)
    Introduction to BGP (3)
    Introduction to BGP (2)
    Introduction to BGP (1)
    Cisco NAT Fundation
    Linux安装Nginx
    GRE协议学习与练习
    Oracle Study Note : Users and Basic Security
    Oracle Study Note : Tablespace and Data Files
  • 原文地址:https://www.cnblogs.com/fe-huahai/p/11535363.html
Copyright © 2011-2022 走看看