美文网首页
Vue 手写vue

Vue 手写vue

作者: 爱吃馒头不吃辣 | 来源:发表于2021-01-09 14:44 被阅读0次
// 数组响应式
// 1.替换数组原型中7个方法
const orginalProto = Array.prototype;
// 备份一份,修改备份
const arrayProto = Object.create(orginalProto);
['push', 'pop', 'shift', 'unshift'].forEach(method => {
    arrayProto[method] = function () {
        // 原始操作
        orginalProto[method].apply(this, arguments)
        // 覆盖操作:通知更新
    }
});


// 1.创建Vue类执行初始化操作
class Vue {
    constructor(options) {
        this.$options = options
        this.$data = options.data
        // 2.对data执行响应化处理
        observe(this.$data);
        // 3.将data挂载到Vue实例上
        proxy(this)
        // 4.对模板进行编译
        new Compile(options.el, this);
    }
}
// 5.创建Watcher类,管理数据的更新函数
class Watcher {
    constructor(vm, key, updateFn) {
        this.$vm = vm;
        this.$key = key;
        this.$updateFn = updateFn;
        Dep.target = this;
        this.$vm[key];
        Dep.target = null;
    }
    update() {
        this.$updateFn.call(this.$vm, this.$vm[this.$key])
    }
}
// 6.创建Dep类,管理watcher更新
class Dep {
    constructor() {
        this.deps = []
    }
    addDep(dep) {
        this.deps.push(dep)
    }
    notify() {
        this.deps.forEach(vm => vm.update())
    }
}
class Compile {
    constructor(el, vm) {
        console.log(vm, 4444);
        this.$vm = vm;
        this.$methods = vm.$options.methods
        this.$el = document.querySelector(el)
        this.compile(this.$el);
    }
    // 遍历整个el树
    compile(el) {
        el.childNodes.forEach(node => {
            if (this.isElement(node)) {// 判断是否为元素标签
                // 获取自定义属性
                this.compileElement(node);
                if (node.childNodes && node.childNodes.length > 0) {
                    this.compile(node)
                }
            } else if (this.isInset(node)) {// 判断是是否为插值元素
                this.compileInset(node)
            }
        })
    }
    compileElement(node) {
        Array.from(node.attributes).forEach(attr => {
            let { name, value } = attr;
            //判断是否为自定义v-属性
            if (this.isDir(name)) {
                const dir = name.substring(2);
                this[dir] && this[dir](node, value)
            }
            if (this.isEvent(name)) {
                const event = name.substring(1);
                node.addEventListener(event, this.$methods[value].bind(this.$vm))
            }
        })
    }
    isDir(attr) {
        return attr.startsWith('v-')
    }
    isEvent(attr) {
        return attr.startsWith('@')
    }
    compileInset(node) {
        this.update(node, RegExp.$1, 'text')
    }
    model(node, exp) {
        this.update(node, exp, 'model')
        node.addEventListener('input', (e) => {
            this.$vm[exp] = e.target.value
        })
    }
    text(node, exp) {
        this.update(node, exp, 'text')
    }
    html(node, exp) {
        this.update(node, exp, 'html')
    }
    // 统一更新函数,指定节点操作
    update(node, exp, dir) {
        const fn = this[dir + 'Update'];
        fn && fn(node, this.$vm[exp]);
        new Watcher(this.$vm, exp, function (val) {
            fn && fn(node, val);
        })
    }
    textUpdate(node, val) {
        node.textContent = val
    }
    modelUpdate(node, val) {
        node.value = val
    }
    htmlUpdate(node, val) {
        node.innerHTML = val
    }
    isElement(node) {
        return node.nodeType == 1
    }
    isInset(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent)
    }
}
function proxy(vm) {
    Object.keys(vm.$data).forEach(key => {
        Object.defineProperty(vm, key, {
            get() {
                return vm.$data[key]
            },
            set(v) {
                vm.$data[key] = v
            }
        })
    })
}
function observe(obj) {
    // 判断传入的是否为对象
    if (typeof obj !== 'object' || obj === null) {
        return obj
    }
    // 如果是对象执行依次遍历进行劫持监听
    new Observer(obj)
}

class Observer {
    constructor(obj) {
        if (Array.isArray(obj)) {
            // 如果值是数组做相应处理
            this.array(obj);
        } else {
            // 如果是对象循环遍历每个属性进行劫持监听
            this.walk(obj);
        }
    }
    array(obj) {
        // 劫持数组
        // 覆盖原型,替换7个变更操作
        obj.__proto__ = arrayProto;
        // 对数组内元素执行相应话
        for (let i = 0; i < obj.length; i++) {
            observe(obj[i])
        }
    }
    walk(obj) {
        Object.keys(obj).forEach(key => {
            defindReactive(obj, key, obj[key])
        })
    }
}


function defindReactive(obj, key, val) {
    // 如果一个属性的值也是对象,继续嵌套进行劫持舰艇
    observe(val);
    // 8.创建依赖收集  每个key一个dep实例
    const dep = new Dep();
    Object.defineProperty(obj, key, {
        get() {
            console.log('get', key);
            Dep.target && dep.addDep(Dep.target)
            return val;
        },
        set(v) {
            console.log('set', key);
            if (v !== val) {
                val = v;
                dep.notify();
            }
        }
    })
}

相关文章

网友评论

      本文标题:Vue 手写vue

      本文链接:https://www.haomeiwen.com/subject/ysucaktx.html