Vue 3 源码

1、渲染器的实现

功能一:h 函数,用于返回一个VNode 对象

功能二:mount 函数,用于将VNode 挂载到DOM上;

功能三:patch 函数,用于将两个VNode 进行对比,决定如何处理新的VNode;

const h = (tag, props, children) => {
    // vnode -> javascript 对象
    return {
        tag,
        props,
        children
    }
}

const mount = (vnode, container) => {
    // vnode -> element
    // 1. 创建出真实的元素, 并且在vnode 上保留el
    const el = vnode.el = document.createElement(vnode.tag);
    // 2. 处理props
    if(vnode.props) {
        for(const key in vnode.props) {
            const value = vnode.props[key];

            // 对事件监听的判断
            if(key.startsWith("on")){
                el.addEventListener(key.slice(2).toLowerCase(), value)
            } else {
                el.setAttribute(key, value);
            }
        }
    }
    // 3. 处理children
    if(vnode.children) {
        if(typeof vnode.children === "string") {
            el.textContent = vnode.children
        } else {
            vnode.children.forEach(item => {
                mount(item, el);
            })
        }
    }
    // 4. 将el 挂载到container 上
    container.appendChild(el);
}

const patch = (n1, n2) => {
    if(n1.tag !== n2.tag) {
        const n1ElParent = n1.el.parentElement;
        n1ElParent.removeChild(n1.el);
        mount(n2, n1ElParent);
    } else {
        // 1. 取出element 对象,并且在n2 中进行保存
        const el = n2.el = n1.el;
        // 2. 处理props
        const oldProps = n1.props || {};
        const newProps = n2.props || {};
        // 2.1 获取所有的newProps 添加到el
        for(const key in newProps) {
            const oldValue = oldProps[key];
            const newValue = newProps[key];
            if(newValue !== oldValue) {
                // 对事件监听的判断
                if(key.startsWith("on")){
                    el.addEventListener(key.slice(2).toLowerCase(), newValue)
                } else {
                    el.setAttribute(key, newValue);
                }
            }
        }
        // 2.2 删除旧的props
        for(const key in oldProps) {
            if(key.startsWith("on")){
                const value = oldProps[key];
                el.removeEventListener(key.slice(2).toLowerCase(), value)
            }
            if(!(key in newProps)) {
                el.removeAttribute(key);
            }
        }
        // 3. 处理children
        const oldChildren = n1.children || [];
        const newChildren = n2.children || [];
        if(typeof newChildren === "string") {
            // 情况一: newChildren 本身是一个string
            // 边界情况 (edge case)
            if(typeof oldChildren === "string") {
                if(newChildren !== oldChildren) {
                    el.textContent = newChildren
                }
            } else {
                el.innerHTML = newChildren;
            }
        } else {
            // 情况二: newChildren本身是一个数组
            if(typeof oldChildren === "string") {
                el.innerHTML = "";
                newChildren.forEach(item => {
                    mount(item, el);
                })
            } else {
                // oldChildren: [v1,v2,v3,v8,v9]
                // newChildren: [v1,v5,v6]
                // 1.前面有相同节点的原生进行patch 操作
                const commonLength = Math.min(oldChildren.length, newChildren.length);
                for(let i = 0; i < commonLength; i++) {
                    patch(oldChildren[i], newChildren[i]);
                }
                // 2.newChildren.length > oldChildren.length
                if(newChildren.length > oldChildren.length) {
                    newChildren.slice(oldChildren.length).forEach(item => {
                        mount(item, el);
                    })
                }
                // 3.newChildren.length < oldChildren.length
                if(newChildren.length < oldChildren.length) {
                    oldChildren.slice(newChildren.length).forEach(item => {
                        el.removeChild(item.el);
                    })
                }
            }
        }
    }
}

2、响应式系统的实现

// reactive.js
class Dep {
    constructor() {
        this.subscriber = new Set();
    }
    depend() {
        if(activeEffect) {
            this.subscriber.add(activeEffect);
        }
    }
    notify() {
        this.subscriber.forEach(effect => {
            effect();
        })
    }
}

let activeEffect = null;
function watchEffect(effect) {
    activeEffect = effect;
    effect();
    activeEffect = null;
}

// Map({key: value}): key是一个字符串
// weakMap({key(对象): value}): key是一个对象,弱引用
const targetMap = new WeakMap();
function getDep(target, key) {
    // 1. 根据对象(target) 取出对应的Map 对象
    let depsMap = targetMap.get(target);
    if(!depsMap) {
        depsMap = new Map();
        targetMap.set(target, depsMap);
    }
    // 2.取出具体的dep 对象
    let dep = depsMap.get(key);
    if(!dep) {
        dep = new Dep();
        depsMap.set(key, dep);
    }
    return dep;
}

// 1. vue2 对raw进行数据劫持
// function reactive(raw) {
//     Object.keys(raw).forEach(key => {
//         const dep = getDep(raw, key);
//         let value = raw[key];
//         Object.defineProperty(raw, key, {
//             get() {
//                 dep.depend();
//                 return value;
//             },
//             set(newValue) {
//                 if(value !== newValue) {
//                     value = newValue;
//                     dep.notify();
//                 }
//             }
//         })
//     })
//     return raw;
// }

// 2. vue3 对raw进行数据劫持
function reactive(raw) {
    return new Proxy(raw, {
        get(target, key) {
            const dep = getDep(target, key);
            dep.depend()
            return target[key];
        },
        set(target, key, newValue) {
            const dep = getDep(target, key);
            target[key] = newValue;
            dep.notify()
        }
    })  
}

// 测试代码
const info = reactive({counter: 100, name: "why"});
const foo = reactive({height: 1.88});

// watchEffect1
watchEffect(function() {
    console.log("effect1",info.counter * 2, info.name);
})
// watchEffect2
watchEffect(function() {
    console.log("effect2",info.counter * info.counter);
})
// watchEffect3
watchEffect(function() {
    console.log("effect3",info.counter + 10, info.name);
})
// watchEffect4
watchEffect(function() {
    console.log("effect4",foo.height);
})

// info.counter++;
// info.name = "why"

foo.height = 2;

 3、Mini-Vue 的实现

// index.js
function createApp(routComponent) {
    return {
        mount(selector) {
            const container = document.querySelector(selector);
            let isMounted = false;
            let oldVNode = null; 

            watchEffect(function() {
                if(!isMounted) {
                    oldVNode = routComponent.render();
                    mount(oldVNode, container);
                    isMounted = true;
                } else {
                    const newVNode = routComponent.render();
                    patch(oldVNode, newVNode);
                    oldVNode = newVNode;
                }
            })
        }
    }
}
//index.html
<div id="app"></div>
<script src="./render.js"></script>
<script src="./reactive.js"></script>
<script src="./index.js"></script>
<script>
    // 1. 创建根组件
    const App = {
        data: reactive({
            counter: 0
        }),
        render() {
            return h("div", null, [
                h("h2", null, `当前计数: ${this.data.counter}`),
                h("button", { onClick:() => { 
                    this.data.counter++
                } }, "+1")
            ])
        }
    }
    // 2. 挂载根组件
    const app = createApp(App);
    app.mount("#app");
</script>

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值