美文网首页
miniVue源码学习

miniVue源码学习

作者: 斗伽 | 来源:发表于2021-05-11 19:49 被阅读0次

Vue -- MVVM

  • 数据响应式
  • 模板引擎
  • 渲染--真实DOM

监听数据变化、并在视图更新

  • Object.defineProperty
  • Proxy

数据响应式

// 数据响应式
function defineReactive(obj, key, value) {
    // 递归
    observe(value)
    Object.defineProperty(obj, key, {
        get() {
            console.log(`get: ${key}`);
            return value;
        },
        set(newValue) {
            if(newValue !== value) {
                // 递归 -->  设置某属性值为对象时;
                observe(newValue)
                console.log(`set: ${key}`)
                value = newValue;
            }
        }
    })
}

// 递归实现对象的数据响应式
function observe(obj) {
    if(typeof obj !== 'object' || obj == null) {
        return obj;
    }
    Object.keys(obj).forEach((key) => {
        defineReactive(obj, key, obj[key])
    })
}

// 新增加属性,自定义set; Vue.set模拟
function set(obj, key, value) {
    defineReactive(obj, key, value)
}

简单vue实现,实现l-text="counter"、{{counter}}

// 数据响应式
function defineReactive(obj, key, value) {
    // 递归
    observe(value)
    // 创建dep实例,与key一一对应
    const dep = new Dep();

    Object.defineProperty(obj, key, {
        get() {
            console.log(`get: ${key}`);

            // 依赖收集
            Dep.target && dep.addDeps(Dep.target)
            return value;
        },
        set(newValue) {
            if(newValue !== value) {
                // 递归 -->  设置某属性值为对象时;
                console.log(`set: ${key}`)
                value = newValue
                observe(newValue)

                // 派发更新
                dep.notify()
            }
        }
    })
}

// 递归实现对象的数据响应式
function observe(obj) {
    if(typeof obj !== 'object' || obj == null) {
        return obj;
    }
    // Object.keys(obj).forEach((key) => {
    //     defineReactive(obj, key, obj[key])
    // })

    new Observe(obj)
}

// 新增加属性,自定义set; Vue.set模拟
function set(obj, key, value) {
    defineReactive(obj, key, value)
}


// 代理
function proxy(vm) {
    Object.keys(vm.$data).forEach(key => {
        Object.defineProperty(vm, key, {
            get() {
                return vm.$data[key]
            },
            set(newValue) {
                vm.$data[key] = newValue;
            }
        })
    })
}

// 数据响应式
class Observe {
    constructor(obj) {
        if(Array.isArray(obj)) {
            // todo 数组响应式
        } else {
            this.walk(obj)
        }
    }

    walk(obj) {
        Object.keys(obj).forEach((key) => {
            defineReactive(obj, key, obj[key])
        })
    }
}
// mini-vue
class Kvue {
    constructor(options) {
        // 对data进行响应式处理
        this.$options = options;
        this.$data = options.data;

        // 1. 对data做响应式处理
        observe(options.data)
        
        // 2. 将data数据代理到Kvue实例
        proxy(this)

        // 3. 解析编译 compile: 初始化和更新
        new Compile(options.el, this)

    }
}

// 编译
class Compile {
    constructor(el, vm) {
        this.$vm = vm;
        this.$el = document.querySelector(el);

        if(this.$el) {
            this.compile(this.$el)
        }
    }

    compile(node) {
        const childNodes = node.childNodes;
        Array.from(childNodes).forEach(n => {
            // 判断类型
            if(this.isElement(n)) {
                console.log(`元素: ${n.nodeName}`);
                this.compileElement(n)
                if(n.childNodes.length) this.compile(n)

            } else if(this.isInter(n)) {
                console.log(`文本: ${n.textContent}`);
                this.compileText(n)
            }
        })
    }

    // 元素节点
    isElement(node) {
        return node.nodeType === 1;
    }
// 元素节点
    isElement(node) {
        return node.nodeType === 1;
    }

    // 插值文本节点
    isInter(node) {
        return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }


    // 编译文本
    compileText(node) {
        // 获取到插值表达式 RegExp.$1 --> 分组
        // node.textContent = this.$vm[RegExp.$1.trim()]
        this.update(node, RegExp.$1.trim(), 'text')
    }

    // 编译元素
    compileElement(node) {
        const attrs = node.attributes;

        Array.from(attrs).forEach(attr => {
            // l-text="xxx"
            const attrName = attr.name;
            const expiration = attr.value;
            
            if(this.isDirective(attrName)) {
                const dir = attrName.substring(2)
                this[dir] && this[dir](node, expiration)
            }
        })
    }
 isDirective(attrName) {
        return attrName.startsWith('l-')
    }

    // 指令方法
    text(node, exp) {
        // node.textContent = this.$vm[exp];
        this.update(node, exp, 'text')
    }

    html(node, exp) {
        // node.innerHTML = this.$vm[exp]
        this.update(node, exp, 'html')
    }

    // 更新textUpdater
    textUpdater(node, value) {
        node.textContent = value;
    }

    htmlUpdater(node, value) {
        node.innerHTML = value;
    }
  // watcher重构,统一更新
    update(node, exp, dir) {
        // 初始化
        const fn = this[`${dir}Updater`]
        fn && fn(node, this.$vm[exp])

        // 更新,闭包实现fn保留,更新函数保留
        new Watcher(this.$vm, exp, val => {
            fn && fn(node, val)
        })
    }
}

相关文章

网友评论

      本文标题:miniVue源码学习

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