支持IE6、IE7、IE8等低端浏览器的简化版vue

最近研究Vue的底层原理,写了一个简化版的Vue,可以在支持IE6、IE7、IE8等低端浏览器运行。由于低端浏览器不支持对象属性定义,所以设置属性不支持直接赋值,需要调用虚拟机实例的set方法。目前只实现了基础的方法,后续继续完善!

index.html

<!DOCTYPE html>
<html>
    <head>
        <title>简化版Vue</title>
        <script>
            window.onerror=function(){
                return true;
            }
        </script>
    </head>
    <body>
        <hr />
        <div id="simpleVue">
            <button v-on:click="copy">戳我</button>
            <div>
                <textarea v-model="name"></textarea>
                <div v-text="name"></div>
            </div>
            <div>
                <select v-model="name">
                    <option value="name1" selected>name1</option>
                    <option value="name2">name2</option>
                    <option value="name3">name3</option>
                </select>
            </div>
            <hr>
            <button v-on:click="show">显示/隐藏</button>
            <div v-if="isShow">
                <input type="text" style="width: 300px" v-model="webSite">
                <div v-text="webSite"></div>
            </div>
        </div>
        <script src="vmm.js"></script>
        <script>
        var vm = new VMM({
            el: '#simpleVue',
            data: {
                name: '测试',
                webSite: 'https://github.com/steezer',
                isShow: true
            },
            methods: {
                copy: function(){
                    vm.set('name', this.name +'测试');
                },
                show: function(){
                    vm.set('isShow', !this.isShow);
                }
            }
        });
        </script>
    </body>

</html>

vmm.js

function VMM(options){
    /**
     * 订阅器构造 用来接收属性值的相关数据的变化通知 从而更新视图
     * 
     * @param {Object} vm 虚拟机对象
     * @param {HTMLElement} el Node节点
     * @param {String} attr 属性名称
     * @param {Object} val 属性值
     */
    function Watcher(vm, el, attr, val){
        this.vm = vm;
        this.el = el;
        this.attr = attr;
        this.val = val;
        /**
         * 将收到的新的数据更新在视图中
         */
        this.update = function() {
            if (this.vm.$data[this.val] === true) {
                this.el.style.display = 'block';
            } else if (this.vm.$data[this.val] === false) {
                this.el.style.display = 'none';
            } else {
                this.el[this.attr] = this.vm.$data[this.val];
            }
        }
        
        // 初始化订阅器时更新一下视图
        this.update();
    }

    /**
     * 获取对象
     * 
     * @param {Object|String} id 
     * @returns Object
     */
    function getElem(id){
        if(typeof(id)=='object'){
            return id;
        }
        var target=id+'',
            prefix=target.substr(0,1),
            target=target.substr(1);
        if(prefix=='#'){
            return document.getElementById(target);
        }
        if(prefix=='.'){
            return document.getElementsByClassName(target);
        }
        return document.getElementsByTagName(target);
    }
    
    function getAttr(elem, name) {
        var node = elem.getAttributeNode(name);
        if (node && node.specified) {
            return node.nodeValue;
        } else {
            return undefined;
        }
    }
    
    function addEvent(node, type, handle){
        if(document.addEventListener){
            node.addEventListener(type, handle, false);
        }else{
            node.attachEvent('on'+type, function(){
                handle.call(node, arguments);
            });
        };
    }
    
    this.$el = getElem(options.el);
    this.$data = options.data;
    this.$methods = options.methods;
    this.oWatcherObj = {};
    
    // 获取属性
    this.get=function(key){
        return this.$data[key];
    }
    
    // 设置属性
    this.set=function(key, newVal){
        var value=this.$data[key];
        if (newVal !== value) {
            this.$data[key] = newVal;
            if(typeof(this.oWatcherObj[key])!="undefined"){
                var watchers=this.oWatcherObj[key];
                for(var i=0; i< watchers.length; i++){
                    watchers[i].update();
                }
            }
        }
    }
    
    /**
     * 节点DOM解析器
     */
    this.compile=function(el) {
        var nodes = el.children,
            $this=this,
            addWatcher=function(node, attr, val){
                if(typeof($this.oWatcherObj[val])=='undefined'){
                    $this.oWatcherObj[val]=[];
                }
                $this.oWatcherObj[val].push(new Watcher($this, node, attr, val));
            };
        // 迭代同级所有节点
        var values=[];
        for(var k in el){
            values.push(k)
        }
        
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i],val;
            if (node.children.length > 0) {
                this.compile(node); // 递归所有子节点
            }
            
             // 点击事件
            val=getAttr(node, 'v-on:click');
            if (val) {
                if(typeof($this.$methods[val])=="function"){
                    addEvent(node, 'click', (function(val){
                        return function(e){
                            $this.$methods[val].call($this.$data, e);
                        }
                    })(val));
                }
            }
            
            // IF指令
            val=getAttr(node, 'v-if');
            if (val) {
                addWatcher(node, "", val);
            }
            
            // Model
            val=getAttr(node, 'v-model');
            if (val) {
                var event=node.tagName.match(/select/i) ? 'change' : 
                        ('oninput' in node ? 'input' : 'propertychange');
                addWatcher(node, "value", val);
                addEvent(node, event, (function(i, val){
                    return function(e){
                        $this.set(val, nodes[i].value);
                    }
                })(i, val));
            }
            
            // Text
            val=getAttr(node, 'v-text');
            if (val) {
                addWatcher(node, "innerText", val);
            }
            
            // Html
            val=getAttr(node, 'v-html');
            if (val) {
                addWatcher(node, "innerHTML", val);
            }
        }
    }
    
    // 节点解析
    this.compile(this.$el);
}

 

posted @ 2020-03-08 12:37  程序人生♨︎  阅读(2192)  评论(0编辑  收藏  举报