笔记-20220405-miniVue实现

Vue简单实现

Vue

类图

  • class
    • Vue
  • 属性
    • $options
    • $data
    • $el
  • 方法
    • _proxyData()

  • class
    • Observer
  • 属性
  • 方法
    • walk()
    • defineReactive()

  • class
    • Compiler
  • 属性
    • el
    • vm
  • 方法
    • compile()
    • compileElement()
    • compileText()
    • isDirective()
    • isTextNode()
    • isElementNode()
    • …编译指令对应的 方法

  • class
    • Dep
  • 属性
    • subs
  • 方法
    • addSubs()
    • notify()

  • class
    • Watcher
  • 属性
    • vm
    • key
    • cb
    • oldValue
  • 方法
    • update()

实现

Vue
class Vue {
    $el;
    $options;
    $data;
    constructor(options) {
        // 保存选项数据
        this.$options = options || {};
        this.$data = options.data || {};
        this.$el = typeof options.el === "string" ? document.querySelector(options.el) : options.el;
        // 将data中的成员转化为getter setter 注入到Vue实例中
        this._proxyData(this.$data);
        // observer对象 监听数据变化
        new Observer(this.$data)
        // 调用compiler对象,解析指令和插值表达式
        new Compiler(this);
    }
    _proxyData(data) {
        // 将属性从data代理到了this上 可以通过this对属性进行修改
        Object.keys(data).forEach(key=>{
            Object.defineProperty(this, key, {
                enumerable:true,
                configurable: true,
                get() {
                    return data[key];
                },
                set(newValue) {
                    if (newValue === data[key]) return;
                    data[key] = newValue;
                }
            })
        })
        /**
         *  代理后 增加了如下属性
         *  get count: ƒ get()
            set count: ƒ set(newValue)
            get msg: ƒ get()
            set msg: ƒ set(newValue)
            get person: ƒ get()
            set person: ƒ set(newValue)
         */
    }
}
Observer
/**
 * 负责数据劫持
 * 将data内部的属性 变为响应式
 */
class Observer {
    constructor(data){
        this.walk(data)
    }
    walk(data){
        // 处理非法数据
        if (typeof data !== "object" || !data) return;
        Object.keys(data).forEach(key=>{
            this.defineReactive(data, key, data[key])
        })
    }
    defineReactive(data, key, value) {
        let that = this;
        let dep = new Dep();
        this.walk(value); // 用于处理data中的对象数据  循环引用对象时会出现错误needtodo
        Object.defineProperty(data, key, {
            enumerable:true,
            configurable:true,
            get() {
                Dep.target && dep.addSubs(Dep.target);
                return value
            },
            set(newValue) {
                if (newValue === value) return;
                value = newValue;
                that.walk(newValue);
                dep.notify();
            }
        })

        // 参数有data 有key 为什么还要增加value形参?
        // 答: 如果直接写data[key] 则又会触发get 形成无限递归 进而报出栈溢出错误
        // 除此之外 get set 使用value后 函数执行完后形成闭包 扩展了value的生命周期

    }
}
Compiler
class Compiler {
    constructor(vm) {
        this.el = vm.$el;
        this.vm = vm;
        this.compile(this.el);
    }


    // 编译模板 处理文本和元素节点
    compile (el) {
        let childNodes = el.childNodes;
        Array.from(childNodes).forEach(node => {
            if (this.isTextNode(node)) {
                this.compileText(node);
            } else if (this.isElementNode(node)) {
                this.compileElement(node);
            }
            // 是否有子节点
            if (node.childNodes && node.childNodes.length) this.compile(node);
        })
    }

    // 编译元素节点 处理指令
    compileElement (node) {
        Array.from(node.attributes).forEach(attr=>{
            if (!this.isDirective(attr.name)) return;
            // 只做v-text 和v-model
            const directive = attr.name.replace('v-','');
            const key = attr.value;
            this.update(node, key, directive)
        })
    }

    update(node, key, directive) {
        const confirmFn = this[directive+'Updater'];
        confirmFn && confirmFn.call(this, node, this.vm[key], key);
    }

    textUpdater(node, value, key) {
        node.textContent = value;
        new Watcher(this.vm, key, newValue=>{
            node.textContent = newValue;
        })
    }

    modelUpdater(node, value, key) {
        node.value = value;
        new Watcher(this.vm, key, newValue=>{
            node.value = newValue;
        })
        node.addEventListener('input', ()=>{
            this.vm[key] = node.value;
        })
    }
    // textUpdater(node, key) {
    //     node.textContent = this.vm[key];
    // }

    // modelUpdater(node,key) {
    //     node.value = this.vm[key];
    // }

    // 编译文本节点 处理插值表达式
    compileText (node) {
        const value = node.textContent;
        const reg = /\{\{(.+?)\}\}/;
        if (reg.test(value)) {
            const key = RegExp.$1.trim();
            node.textContent = value.replace(reg, this.vm[key]);
            new Watcher(this.vm, key, (newValue)=>{
                node.textContent = value.replace(reg, newValue);
            })
        }
    }

    // 是否是指令
    isDirective (attrName) {
        return attrName.startsWith('v-')
    }

    // 是否是文本节点
    isTextNode (node) {
        return node.nodeType === 3;
    }

    // 是否是元素节点
    isElementNode (node) {
        return node.nodeType === 1;
    }
}
Dep
// dep类用来收集依赖  和通知观察者
// 每个响应式数据都添加一个dep实例  这样每次数据变化时就可以通知观察者了

class Dep {
    constructor() {
        this.subs = []
    }

    addSubs(sub) {
        if (!(sub.update && typeof sub.update === 'function')) return;
        this.subs.push(sub)
    }

    notify() {
        this.subs.forEach(sub => {
            sub.update();
        });
    }
}
Watcher
class Watcher {
    constructor(vm, key, cb) {
        this.vm = vm; // vm实例
        this.key = key; // data中的属性
        this.cb = cb; // 更新视图时的回调
        // 将watcher对象添加到Dep静态属性中;
        Dep.target = this;
        // 调用observer中的getter 将watcher对象添加到dep对象中;
        this.oldValue = vm[key];
        // 防止重复添加
        Dep.target = null;
    }

    update() {
        let newValue = this.vm[this.key];
        if (newValue === this.oldValue) return;
        this.cb(newValue);
    }
}

流程图

大概流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值