Vue3响应式源码实现

Vue3响应式源码实现

初始化项目结构

tex 复制代码
vue-proxy
├── effect.js
├── effect.ts
├── index.html
├── index.js
├── package.json
├── reactive.js
├── reactive.ts
└── webpack.config.js

reactive.ts

js 复制代码
import { track, trigger } from "./effect"

// 判断是否是对象
const isObject = (target) => target !== null && typeof target === "object"

// 泛型约束只能传入Object类型
export const reactive = <T extends object>(target: T) => {

    return new Proxy(target, {
        get(target, key, receiver) {
            console.log(target);
            console.log(key);
            console.log(receiver);

            let res = Reflect.get(target, key, receiver)

            track(target, key)

            if (isObject(res)) {
                return reactive(res)
            }

            return res
        },
        set(target, key, value, receiver) {
            let res = Reflect.set(target, key, value, receiver)
            console.log(target, key, value);

            trigger(target, key)
            return res
        }
    })

}

effect.ts

js 复制代码
// 更新视图的方法
let activeEffect;
export const effect = (fn: Function) => {
    const _effect = function () {
        activeEffect = _effect;
        fn()
    }
    _effect()
}

// 收集依赖
const targetMap = new WeakMap()
export const track = (target, key) => {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        depsMap = new Map()
        targetMap.set(target, depsMap)
    }
    let deps = depsMap.get(key)
    if (!deps) {
        deps = new Set()
        depsMap.set(key, deps)
    }
    deps.add(activeEffect)
}

// 触发更新
export const trigger = (target, key) => {
    const depsMap = targetMap.get(target)
    const deps = depsMap.get(key)
    deps.forEach(effect => effect())
}

测试

执行 tsc 转成 js 代码,没有 tsc 的全局安装 typescript

sh 复制代码
npm install typescript -g

新建 index.js,分别引入 effect.jsreactive.js

js 复制代码
import { effect } from "./effect.js";
import { reactive } from "./reactive.js";

let data = reactive({
    name: "lisit",
    age: 18,
    foor: {
        bar: "汽车"
    }
})

effect(() => {
    document.getElementById("app").innerText = `数据绑定:${data.name} -- ${data.age} -- ${data.foor.bar}`
})

document.getElementById("btn").addEventListener("click", () => {
    data.age++
})

新建index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app"></div>
    <button id="btn">按钮</button>
</body>

然后再根目录执行

sh 复制代码
npm init -y

安装依赖

sh 复制代码
npm install webpack webpack-cli webpack-dev-server html-webpack-plugin -D

然后新建 webpack.config.js

js 复制代码
const path = require("path")
const HtmlWebpakcPlugin = require("html-webpack-plugin")

module.exports = {
    entry: "./index.js",
    output: {
        path: path.resolve(__dirname, "dist")
    },

    plugins: [
        new HtmlWebpakcPlugin({
            template: path.resolve(__dirname, "./index.html")
        })
    ],
    mode: "development",
    // 开发服务器
    devServer: {
        host: "localhost", // 启动服务器域名
        port: "3000", // 启动服务器端口号
        open: true, // 是否自动打开浏览器
    },
}

执行命令启动项目

sh 复制代码
npx webpack serve
相关推荐
溪语流沙5 小时前
Django + Vue电商项目第001讲:开篇|注册登录加增删改查,那不是电商
redis·python·mysql·docker·typescript·django·vue
程序员阿黄5 小时前
基于 Django 与 Vue 3 的智能实验室预约系统设计与实现
后端·python·mysql·django·vue·毕设
.Hypocritical.5 小时前
Vue CLI(vue-cli)和 Vite 超详细对比
vue
志尊宝1 天前
Vue3 零基础每日笔记(040):Vite 创建 Vue3 + TS 项目——企业标配从这一篇开始
笔记·vue·html·前端开发·软件开发
凤城老人1 天前
从零手写开源私有云盘|Flask+Vue3+Electron全栈网盘,单容器一键部署
python·docker·electron·vue
球球不吃虾1 天前
Nuxt在使用过程中的一些小总结(二)
前端·vue·nuxt
志尊宝2 天前
Vue3 零基础每日笔记(038):亲手封装 useDebounceFn 与 useThrottleFn——高频事件的流量阀
前端·javascript·vue·html·vue3
愛芳芳2 天前
基于 Electron + Vue3 的仿 PC 微信客户端项目实战
前端·javascript·css·elementui·typescript·electron·vue
梦想的颜色2 天前
【AI科普】AI 时代,纯 H5+CSS PK React & Vue:前端技术孰优孰劣深入剖析
ai·前端框架·大模型·vue·react·html5·vibecoding
志尊宝2 天前
Vue3 零基础每日笔记(043):defineModel 与 defineExpose 的 TS 用法——3.4+ 新 API 进阶
前端·javascript·vue·html·vue3