[Vue3]简易版Vue

简易版Vue

实现ref功能

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

javascript 复制代码
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;
}
javascript 复制代码
// 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。

javascript 复制代码
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;
        }
    })
}
javascript 复制代码
//test
const user = reactive({
    age: 10
})
let nextAge = 0;
effectWatch( () => {
    nextAge = user.age + 1;
    console.log(nextAge);
})
user.age++;

简易版Vue雏形

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

javascript 复制代码
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也在框架中调用

javascript 复制代码
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是一个数组,可以有多个

javascript 复制代码
export function h(tag, props, children) {
    return {
        tag,
        props,
        children
    }
}

在App中

javascript 复制代码
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,把他们变成真实的节点

javascript 复制代码
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)

}
相关推荐
B站计算机毕业设计超人2 分钟前
计算机毕业设计SpringBoot+Vue.jst0甘肃非物质文化网站(源码+LW文档+PPT+讲解)
java·vue.js·spring boot·后端·spring·intellij-idea·课程设计
烂蜻蜓37 分钟前
深入理解 Uniapp 中的 px 与 rpx
前端·css·vue.js·uni-app·html
烂蜻蜓1 小时前
Uniapp 中布局魔法:display 属性
前端·javascript·css·vue.js·uni-app·html
视觉CG1 小时前
【Viewer.js】vue3封装图片查看器
开发语言·javascript·vue.js
GDAL2 小时前
UniApp SelectorQuery 讲解
vue.js
计算机学姐2 小时前
基于SpringBoot的校园消费点评管理系统
java·vue.js·spring boot·后端·mysql·spring·java-ee
Jet_closer_burning3 小时前
Vue2 和 Vue3 的响应式原理对比
前端·javascript·vue.js·html
belldeep3 小时前
nodejs:vue 3 + vite 作为前端,将 html 填入<iframe>,在线查询英汉词典
vue.js·nodejs·vite·ifame
bin91535 小时前
DeepSeek 助力 Vue 开发:打造丝滑的单选按钮(Radio Button)
前端·javascript·vue.js·ecmascript·deepseek
飞天大河豚7 小时前
2025前端框架最新组件解析与实战技巧:Vue与React的革新之路
vue.js·react.js·前端框架