[Vue3]简易版Vue

简易版Vue

实现ref功能

ref功能主要是收集依赖和触发依赖的过程。

export class Dep {   // 创建一个类,使用时使用new Dep
    constructor(value) { // 初始化可传入一个值
        this._val = value;
        this.effects = new Set(); //收集依赖的容器,使用set数据结构
    }

    get value() { //对value值进行拦截,获取时收集依赖到effects中
        this.depend();
        return this._val;
    }

    set value(val) { // 设置的同时触发所有收集到的依赖
        this._val = val;
        this.notice()
    }

    depend() {
        if (currentEffect) {
            this.effects.add(currentEffect); //将依赖放入effects中
        }
    }

    notice() {
        this.effects.forEach( fn => {  //触发所有收集的依赖
            fn();
        })
    }
}

let currentEffect = null;

export function effectWatch(fn) { //收集依赖的函数,所有函数必须用这个函数包裹
    currentEffect = fn;
    fn()
    currentEffect = null;
}
// index.js
const a = new Dep(10);
let b = 0;
effectWatch( () => {
    b = a.value + 10;
    console.log(b)
})
a.value = 20;

实现reactive功能

reactive主要是让对象也可以进行依赖的收集,这就需要为对象的每一个key创建对应的Dep。

const targetsMap = new Map(); // 用map数据结构来存储,因为它的key可以是对象

export function reactive(raw) { //传入的raw是一个对象
    return new Proxy(raw, { //拦截raw上所有的get和set
        get(target, key) {
            let depMap = targetsMap.get(raw); // 为每一个raw创建对应的map
            if (!depMap) {
                depMap = new Map();
                targetsMap.set(raw, depMap);
            }
            let dep = depMap.get(key); // 给raw上的每一个值创建Dep
            if (!dep) {
                dep = new Dep();
                depMap.set(key, dep);
            }
            dep.depend();
            return Reflect.get(target, key)
        },
        set(target, key, value) {
            let depMap = targetsMap.get(raw);
            if (!depMap) {
                depMap = new Map();
                targetsMap.set(raw, depMap);
            }
            let dep = depMap.get(key);
            if (!dep) {
                dep = new Dep();
                depMap.set(key, dep);
            }
            const result = Reflect.set(target, key, value)
            dep.notice();
            return result;
        }
    })
}
//test
const user = reactive({
    age: 10
})
let nextAge = 0;
effectWatch( () => {
    nextAge = user.age + 1;
    console.log(nextAge);
})
user.age++;

简易版Vue雏形

使用上面的reactive和effectWatch功能可以实现miniVue的雏形

import { effectWatch, reactive } from './core/index.js';

const App = {
    render(context) {
        effectWatch(() => {
            document.querySelector('#app').textContent = '';
            const element = document.createElement('div');
            const text = document.createTextNode('nihao');
            const text1 = document.createTextNode(context.obj.count);
            element.append(text);
            element.append(text1);
            document.querySelector('#app').append(element)
        })
    },

    setup() {
        const obj = reactive({
            count: 1
        })
        window.obj = obj
        return{
            obj
        }
    }
}

App.render(App.setup())
//通过在console中输入obj.count的值修改视图

优化

将代码抽离,effectWatch在框架中调用,视图的清空和append也在框架中调用

export function createApp(rootComponent) {
    return {
        mount(rootContainer) {
            const setupResult = rootComponent.setup();
            effectWatch(() => {
                rootContainer.textContent = '';
                const element = rootComponent.render(setupResult);
                rootContainer.append(element);
            })
        }
    }
}

export const App = {
    render(context) {
        const element = document.createElement('div');
        const text = document.createTextNode('nihao');
        const text1 = document.createTextNode(context.obj.count);
        element.append(text);
        element.append(text1);
        return element;
    },

    setup() {
        const obj = reactive({
            count: 1
        })
        window.obj = obj
        return {
            obj
        }
    }
}

优化并使用虚拟Dom

在上面的代码中,每次都会更新所有节点,需要进行优化,只更新变化的节点
将节点关键信息转化成一个对象。props是一个对象,代表节点上的attrs,children是一个数组,可以有多个

export function h(tag, props, children) {
    return {
        tag,
        props,
        children
    }
}

在App中

import { reactive, h } from './core/index.js';

export const App = {
    render(context) {
        return h('div', {}, [h('p', {}, 'nihao'), h('p', {}, context.obj.count)])
    },

    setup() {
        const obj = reactive({
            count: 1
        })
        window.obj = obj
        return {
            obj
        }
    }
}

此时获取的element只是一个对象,需要将其映射成真实的Dom

映射真实Dom

依次处理tag props 和children,把他们变成真实的节点

function createElement(tag) {
    return document.createElement(tag);
}
function patchProps(el, key, prevValue, nextValue) {
    el.setAttribute(key, nextValue);
}
export function mountElement(element, root) {

    const { tag, props, children } = element;
    const el = createElement(tag);

    for (const key in props) {
        const val = props[key];
        patchProps(el, key, null, val);
    }

    if (typeof children === 'string') {
        const textNode = document.createTextNode(children);
        el.append(textNode)
    } else if (Array.isArray(children)) {
        children.forEach((v) => {
            mountElement(v, el)
        })
    }

    root.append(el)

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值