简易版vue

  • index.html
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="app">
      <input v-model="name" />
      <h1>{{ name }}</h1>
      <h1>{{ age }}</h1>
      <h1>{{ hello }}</h1>
      <h1>{{ myname }}</h1>
      <button v-on:click="addAge">过年了,大一岁</button>
      <button v-on:click="changeName">我叫李四</button>
    </div>

    <script src="./myVue.js"></script>
    <script>
      var vm = new MyVue({
        el: '#app',

        data: {
          name: '张三',
          age: 20,
          myname:'12312378'
        },

        computed: {
          hello() {
            return `你好我是${this.name}, 今年${this.age}岁。`;
          }
        },

        watch: {
          name(newVal, oldVal) {
            console.log('新数据:'+newVal, '旧数据:'+oldVal)
          }
        },

        methods: {
          addAge() {
            this.age++;
          },
          changeName() {
            this.name = '李四';
          }
        }
      });
    </script>
  </body>
</html>

  • myVue.js
class MyVue {
    constructor({ el, data, methods, computed, watch }) {
        let obs = new Observer(data,this); // 先期进行数据观测
        this.$el = el;
        this.$data = obs.data;
        this.$watch = watch;
        this.$computed = computed;
        this.$methods = methods;
        Object.keys(this.$data).forEach(i => {
            this.proxyKeys(i);
        });
        new Compile(this); // 指令,{{}}编译
        
    }
    proxyKeys(key) {
        let _this = this;
        Object.defineProperty(_this, key, {
            enumerable: false,
            configurable: true,
            get() {
                return _this.$data[key];
            },
            set(newVal) {
                let oldVal = _this.$data[key];
                _this.$data[key] = newVal;
            }
        })
    }
}

// 数据观测
/* 

对data里面每一个属性进行监听,利用Object.defineProperty,在get里面把数据添加到Dep里面,
当数据更新时,利用dep.notify()通知

*/
class Observer {
    constructor(data,_this) {
        this.data = data;
        Object.keys(data).forEach(key => {
            let value = this.data[key];
            let dep = new Dep();
            Object.defineProperty(data, key, {
                get() {
                    // Dep.target指向Watcher里面this
                    Dep.target && dep.add(Dep.target);
                    return value;
                },
                set(newVal) {
                    if (newVal !== value) {
                        value = newVal;
                        Object.keys(_this.$watch).forEach(key => {
                            new Watcher(_this.$data, key, () => {
                                _this.$watch[key].call(_this, value, newVal);
                            });
                        });
                        dep.notify(newVal);
                    }
                }
            })
        })
    }
}

// 订阅者
/* 
add()是添加相关订阅者,这里Dep.target既包括Watcher,也包括ComputedWatcher,notify()主要执行视图更新,根据
下述update执行视图更新
*/
class Dep {
    constructor() {
        this.subs = [];
    }
    add(sub) {
        console.log(sub)
        this.subs.push(sub)
    }
    notify(newVal) {
        // 这里update指向Watcher,ComputedWatcher里面的update方法
        this.subs.forEach(sub => {
            sub.update(newVal)
        })
    }
}
Dep.target = null;

/* 
cb指向MyVue里面的
(newVal) => {
    node.value = typeof newVal === 'undefined' ? '' : newVal;
}
newVal => {
    node.textContent = typeof newVal === 'undefined' ? '' : newVal;
}

*/
class Watcher {
    constructor(data, key, cb) {
        this.data = data;
        this.key = key;
        this.cb = cb;
        Dep.target = this;
        this.value = data[key];
        Dep.target = null;
    }
    update(newVal) {
        let oldVal = this.value;
        if (oldVal !== newVal) {
            this.value = newVal;
            this.cb(newVal);
        }
    }
}

// 编译器
class Compile {
    constructor(vm) {
        this.vm = vm;
        let el = document.querySelector(this.vm.$el);
        let fragment = document.createDocumentFragment();
        if (el) {
            while (el.firstChild) {
                fragment.appendChild(el.firstChild);
            }
            // 编译片断
            this.compileElement(fragment);
            el.appendChild(fragment);
        }
        else {
            alert('挂载元素不存在');
        }
    }

    compileElement(el) {
        for (let node of el.childNodes) {
            /*
                node.nodeType
                1:元素节点
                3:文本节点
            */
            if (node.nodeType == 1) {
                for (let attr of node.attributes) {
                    let { name: attrName, value: exp } = attr;
                    // v- 代表存在指令
                    if (attrName.indexOf('v-') == 0) {
                        /*
                        <div v-xxx=""> 元素上,可以用很多指令,这里仅做学习,所以不判断太多了
                        on    事件绑定
                        model 表单绑定
                        */
                        let [dir, value] = attrName.substring(2).split(':');
                        if (dir === 'on') {
                            // 取 vm.methods 相应的含税,进行绑定
                            let method = this.vm.$methods[exp];
                            method && node.addEventListener(value, method.bind(this.vm), false)
                        }
                        else if (dir === 'model') {
                            // 取 vm.data 进行 input 的赋值,并且在 input 的时候更新 vm.data 上的值
                            let value = this.vm.$data[exp];
                            node.value = typeof value != 'undefined' ? value : '';
                            node.addEventListener('input', e => {
                                if (e.target.value !== value) {
                                    this.vm.$data[exp] = e.target.value;
                                }
                            })
                            new Watcher(this.vm.$data, exp, (newVal) => {
                                node.value = typeof newVal === 'undefined' ? '' : newVal;
                            })
                        }
                    }
                }
            }
            else if (node.nodeType == 3) {
                let reg = /\{\{(.*)\}\}/;
                if(reg.test(node.textContent)){
                    let exp = reg.exec(node.textContent)[1].trim();
                    if(this.vm.$data[exp]){
                        let value = this.vm.$data[exp];
                        node.textContent = typeof value === 'undefined' ? '' : value;
                        new Watcher(this.vm.$data, exp, newVal => {
                            node.textContent = typeof newVal === 'undefined' ? '' : newVal;
                        });
                    }
                    else if(this.vm.$computed[exp]){
                        let computed = new ComputedWatcher(this.vm, exp, newVal => {
                            node.textContent = typeof newVal === 'undefined' ? '' : newVal;
                        });
                        node.textContent = computed.value;

                        // 将 computed 做一个代理,以便 this.xxx
                        Object.defineProperty(this.vm, exp, {
                            enumerable: false,
                            configurable: true,
                            get(){
                                return computed.value
                            }
                        })
                    }
                }
            }
            if (node.childNodes && node.childNodes.length) {
                this.compileElement(node);
            }
        }
    }
}

// 计算属性订阅者生成器
class ComputedWatcher{
    constructor(vm, key, cb){
        this.vm = vm;
        this.key = key;
        this.cb = cb;
        Dep.target = this;
        this.value = this.vm.$computed[key].call(this.vm);
        Dep.target = null;
    }
    update() {
        let newVal = this.vm.$computed[this.key].call(this.vm);
        let oldVal = this.value;
        if (newVal !== oldVal) {
            this.value = newVal;
            this.cb(newVal);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值