Vue实现原理

vue实现原理

1、了解Object的属性defineProperty

const Book = {}
    let name = ''
    Object.defineProperty(Book, 'name', {
        set: (value) => {
            name = value
        },
        get: () => {
            return `《${name}》`
        }
    })
    Book.name = 'vue实现原理'
    console.log(Book.name) // 《vue实现原理》

2、vue中mvvm的实现: 数据变化更新视图,视图变化更新数据

  • 在非MVVM中的视图更新,是通过事件触发决定dom层该如何渲染,是一种事件行为操作。
  • 在MVVM中,通过劫持数据,监听数据变化,来决定一系列的dom操作,是数据驱动操作。
  • 在vue2.0中是通过Object.defineProperty监听对象属性值是否发生变化,来驱动dom渲染操作。

3、 实现过程

  1. 实现一个监听器Observer,用来劫持并监听data所有属性,如果有变动的,就通知订阅者。
  2. 实现一个订阅器Dep,用于收集订阅者,当属性变化,执行对应的订阅者函数。
  3. 实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
  4. 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器.

3.1 监听器Observer(递归处理)

/**
 * @author Pero
 * @date 2019/2/13
 * @Description: Observer实例
*/
class Observer {
    constructor (data) {
        this.data = data
        this.init(data)
    }
    init (data) {
        Object.keys(data).forEach((key) => {
            this.defineReactive(data, key, data[key]);
        });
    }
    /**
     * 监听data中子属性的值
     * @param data
     */
    observerChildProperty (data) {
        if (!data || typeof data !== 'object') {
            return;
        } else {
            this.init(data)
        }
    }
    /**
     * @param data: 对象
     * @param key: 当前对象的键
     * @param val: 当前对象的值
     * 监听vue中data的属性,变化时执行对应的订阅者处理器
     */
    defineReactive (data, key, val) {
        const dep = new Dep();
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            /**
             * @author Pero
             * @date 2019/2/14
             * @Description: 取值时添加订阅器
             */
            get: () => {
                if (Dep.target) {
                    dep.addSub(Dep.target);
                }
                return val;
            },
            /**
             * 当data数据发生改变,执行订阅器存储的函数(watcher.update方法)
             * @param newVal
             */
            set: (newVal) =>  {
                if (newVal === val) {
                    return;
                }
                val = newVal;
                dep.notify();
            }
        });
        /*监听对象的子属性*/
        this.observerChildProperty(val);
    }
}

3.2 订阅器 Dep

/**
 * @author Pero
 * @date 2019/2/13
 * @Description:  Dep订阅者收集器subs
*/
class Dep {
    constructor () {
        this.subs = []
    }
    addSub (sub) {
        this.subs.push(sub);
    }
    notify () {
        this.subs.forEach((sub) => {
            sub.update();
        });
    }
}
Dep.target = null;

3.3 订阅者 Watcher

class Watcher {
    /**
     * @param vm vue实例
     * @param exp 监听的值
     * @param cb 当值改变时的回调函数
     */
    constructor (vm, exp, cb) {
        this.cb = cb;
        this.vm = vm;
        this.exp = exp;
        // 初始添加到订阅器
        this.value = this.get();
    }
    update () {
        const value = this.vm.data[this.exp];
        const oldVal = this.value;
        if (value !== oldVal) {
            this.value = value;
            this.cb.call(this.vm, value, oldVal);
        }
    }
    get () {
        Dep.target = this;
        // 强制执行监听器里的get函数
        const value = this.vm.data[this.exp]
        Dep.target = null;
        return value;
    }
}

3.4 解析器 Compile

/**
 * @author Pero
 * @date 2019/2/14
 * @Description: Compile解析器
*/
class Compile {
    constructor (el, vm) {
        this.vm = vm;
        this.el = document.querySelector(el);
        this.fragment = null;
        this.init();
    }

    /**
     * 初始化dom树,解析dom结构
     */
    init () {
        if (this.el) {
            this.fragment = this.nodeToFragment(this.el);
            this.compileElement(this.fragment);
            this.el.appendChild(this.fragment);
        }
    }

    /**
     * 将node添加到一个dom容器
     * @param el
     * @returns {DocumentFragment}
     */
    nodeToFragment (el) {
        const fragment = document.createDocumentFragment();
        let child = el.firstChild;
        while (child) {
            fragment.appendChild(child);
            child = el.firstChild
        }
        return fragment;
    }

    /**
     * 解析dom树结构
     * @param el
     */
    compileElement(el) {
        const childNodes = el.childNodes;
        [].slice.call(childNodes).forEach((node) => {
            const reg = /\{\{(.*)\}\}/;
            const text = node.textContent;
            if (this.isElementNode(node)) {
                this.compile(node);
            } else if (this.isTextNode(node) && reg.test(text)) {
                this.compileText(node, reg.exec(text)[1]);
            }
            if (node.childNodes && node.childNodes.length) {
                this.compileElement(node);
            }
        });
    }

    /**
     * 分发指令
     * @param node
     */
    compile (node) {
        const nodeAttrs = node.attributes;
        Array.prototype.forEach.call(nodeAttrs, (attr) => {
            const attrName = attr.name;
            if (this.isDirective(attrName)) {
                const exp = attr.value;
                const dir = attrName.substring(2);
                if (this.isEventDirective(dir)) {  // 事件指令
                    this.compileEvent(node, this.vm, exp, dir);
                } else {  // v-model 指令
                    this.compileModel(node, this.vm, exp, dir);
                }
                node.removeAttribute(attrName);
            }
        });
    }

    /**
     * 解析textNode
     * @param node
     * @param exp
     */
    compileText (node, exp) {
        const initText = this.vm[exp];
        this.updateText(node, initText);
        new Watcher(this.vm, exp,  (value) => {
            this.updateText(node, value);
        });
    }

    /**
     * 解析事件
     * @param node
     * @param vm
     * @param exp
     * @param dir
     */
    compileEvent (node, vm, exp, dir) {
        const eventType = dir.split(':')[1];
        const cb = vm.methods && vm.methods[exp];
        if (eventType && cb) {
            node.addEventListener(eventType, cb.bind(vm), false);
        }
    }

    /**
     * 解析v-model
     * @param node
     * @param vm
     * @param exp
     * @param dir
     */
    compileModel (node, vm, exp, dir) {
        let val = this.vm[exp];
        this.modelUpdater(node, val);
        new Watcher(this.vm, exp,  (value) => {
            this.modelUpdater(node, value);
        });

        node.addEventListener('input', (e) => {
            const newValue = e.target.value;
            if (val === newValue) {
                return;
            }
            this.vm[exp] = newValue;
            val = newValue;
        });
    }

    /**
     * 更新textNode的值
     * @param node
     * @param value
     */
    updateText (node, value) {
        node.textContent = typeof value == 'undefined' ? '' : value;
    }

    /**
     * 更新v-model的值
     * @param node
     * @param value
     * @param oldValue
     */
    modelUpdater (node, value, oldValue) {
        node.value = typeof value == 'undefined' ? '' : value;
    }
    isDirective (attr) {
        return attr.indexOf('v-') == 0;
    }
    isEventDirective (dir) {
        return dir.indexOf('on:') === 0;
    }
    isElementNode (node) {
        return node.nodeType == 1;
    }
    isTextNode (node) {
        return node.nodeType == 3;
    }
}

3.5 实例Vue

/**
 * @author Pero
 * @date 2019/2/13
 * @Description: Vue实例
*/
class Vue {
    /**
     * @param options
     */
    constructor (options) {
        this.data = options.data;
        this.methods = options.methods;
        Object.keys(this.data).forEach((key) => {
            this.proxyKeys(key);
        });
        new Observer(this.data);
        new Compile(options.el, this);
        options.mounted.call(this);
    }
    /**
     * this.data[xx]可以直接通过this[xx]修改属性值
     * @param key
     */
    proxyKeys (key) {
        Object.defineProperty(this, key, {
            enumerable: false,
            configurable: true,
            get: () => {
                return this.data[key];
            },
            set: (newVal) => {
                this.data[key] = newVal;
            }
        });
    }
}




    
    vue



{{title}}

{{name}}

你可能感兴趣的:(Vue实现原理)