首页 > 其他 > 详细

MVVM

时间:2020-05-21 23:03:18      阅读:62      评论:0      收藏:0      [点我收藏+]
function Mvvm(options = {}) {
    this.$options = options
    let data = this._data = this.$options.data

    //数据劫持
    observe(data)

    //数据代理
    //为了不用写如mvvm._data.a.b这种
    //可以直接写mvvm.a.b
    for(let key in data) {
        Object.defineProperty(this, key, {
            configurable: true,
            get() {
                return this._data[key]
            },
            set(newVal) {
                this._data[key] = newVal
            }
        })
    }

    //初始化computed,将this指向实例
    initComputed.call(this)
    //数据编译
    new Compile(options.el, this)
    //所有事情处理好后执行mounted钩子函数
    options.mounted.call(this)  //这就实现了mounted钩子函数
}
function initComputed() {
    let vm = this
    let computed = this.$options.computed
    Object.keys(computed).forEach(key=>{
        Object.defineProperty(vm,key,{
            // 这里判断是computed里的key是对象还是函数
            // 如果是函数直接就会调get方法
            // 如果是对象的话,手动调一下get方法即可
            // 如: sum() {return this.a + this.b;},他们获取a和b的值就会调用get方法
            get:typeof computed[key] === ‘function‘ ? computed[key] : computed[key].get,
            set() {}
        })
    })
}
let options = {
    data: {
        a:1,
        b:9
    },
    computed: {
        sum() {
            return this.a+this.b
        },
        noop() {

        }
    },
    mounted() {
        setTimeout(()=>{
            console.log(‘所有事情都搞定了‘)
        },1000)
    }
}

function Observe(data) {
    
    for(let key in data) {
        let dep = new Dep()
        let val = data[key]
        observe(val)        //递归劫持
        Object.defineProperty(data, key, {
            configurable: true,
            get() {
                //进来到这里的Dep.targe就是那个Watch的实例,这是通过get这个操作进来的,get这个操作在Watch的构造函数里面
                //所以此时的Dep.targe还没有执行Dep.targe=null这个
                Dep.target && dep.addSub(Dep.target)// 将watcher添加到订阅事件中 [watcher]
                return val      //闭包返回值
            },
            set(newVal) {
                if (val === newVal) {
                    return
                }
                val = newVal;       //如果以后再获取值(get)的时候,将刚才设置的值再返回去(其实就是利用了闭包的原理保存了val的状态)
                observe(newVal)     //mvvm._data.a = {b:‘ok‘}这种情况要对其进行递归
                dep.notify()        // 让所有watcher的update方法执行即可
            }
        })
    }
}

function observe(data) {
    //如果不是对象直接return防止递归溢出
    if (!data || typeof data !== ‘object‘) return 
    return new Observe(data)
}

function Compile(el, vm) {
    vm.$el = document.querySelector(el)
    let fragment = document.createDocumentFragment()

    while (child = vm.$el.firstChild) {
        fragment.appendChild(child)
    }

    //对el里的内容进行替换
    function replace(frag) {
        Array.from(frag.childNodes).forEach(node => {
            let txt = node.textContent
            let reg = /\{\{(.*?)\}\}/g
            // 即是文本节点又有大括号的情况{{}}
            if (node.nodeType === 3 && reg.test(txt)) {
                // console.log(RegExp.$1)
                // let arr = RegExp.$1.split(‘.‘)
                // let val = vm
                // arr.forEach(key => {
                //     val = val[key]  //如this.a.b
                // })
                // node.textContent = txt.replace(reg,val).trim()

                // //监听变化
                // //给Watcher再添加两个参数,用来取新的值(newVal)给回调函数传参
                // new Watcher(vm, RegExp.$1, newval=>{
                //     node.textContent = txt.replace(reg,newval).trim()
                // })
                function replaceTxt() {
                    node.textContent = txt.replace(reg, (matched,placeholder)=>{
                        console.log(placeholder);
                        new Watcher(vm,placeholder,replaceTxt)  //只是把replaceTxt作为参数传入没有递归调用。。。

                        return placeholder.split(‘.‘).reduce((val,key)=>{
                            return val[key]
                        },vm)
                    })
                }

                replaceTxt()

            }
            if (node.nodeType === 1) {
                let nodeAttr = node.attributes// 获取dom上的所有属性,是个类数组
                Array.from(nodeAttr).forEach(attr=>{
                    let name = attr.name    //属性的名字    v-mode
                    let exp = attr.value    //属性的值      c
                    if (name.includes(‘v-‘)) {
                        node.value = vm[exp]              //this.c  为  2
                    }
                    //监听变化
                    new Watcher(vm, exp, function(newVal) {
                        node.value = newVal         // 当watcher触发时会自动将内容放进输入框中
                    })

                    node.addEventListener(‘input‘,e => {
                        let newVal = e.target.value
                        // 相当于给this.c赋了一个新值
                        // 而值的改变会调用set,set中又会调用notify,notify中调用watcher的update方法实现了更新
                        vm[exp] = newVal
                    })
                })
            }

            //如果还有子节点,继续递归replace
            if (node.childNodes && node.childNodes.length) {
                replace(node)       //形成一个递归,DFS向下找。直到找到一个text节点
            }
        })
    }

    replace(fragment)
    vm.$el.appendChild(fragment)
}

//发布订阅模式
function Dep() {
    //一个数组(存放函数的事件池)
    this.subs = []
}
Dep.prototype = {
    addSub(sub) {
        this.subs.push(sub)
    },
    notify() {
        this.subs.forEach(sub=>sub.update())
    }
}

//监听函数
//通过Watch这个类创建的实例都拥有update方法
function Watcher(vm, exp, fn) {
    this.fn = fn
    this.vm = vm
    this.exp = exp
    Dep.target = this       //这个操作是为了可以使用Dep直接获取到new出来的Watch实例,
    let arr = exp.split(‘.‘)
    let val = vm
    arr.forEach(key=>{
        val = val[key]  // 获取到this.a.b,默认就会调用get方法
    })
    Dep.target = null
}
Watcher.prototype.update = function () {
    // notify的时候值已经更改了
    // 再通过vm, exp来获取新的值
    let arr = this.exp.split(‘.‘)
    let val = this.vm
    arr.forEach(key=> {
        val = val[key]
    })
    this.fn(val)
}
// //test
// let watcher = new Watcher(() => console.log(111));  // 
// let dep = new Dep();
// dep.addSub(watcher);    // 将watcher放到数组中,watcher自带update方法, => [watcher]
// dep.addSub(watcher);
// dep.notify();   //  111, 111



MVVM

原文:https://www.cnblogs.com/lbzli/p/12933778.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!