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
相关推荐
BugShare30 分钟前
有趣味的登录页它踏着七彩祥云来了
vue·css3
Harriet嘉20 小时前
vscode结合code buddy 和figma还原UI设计稿
vue·figma
木斯佳2 天前
前端八股文面经大全:阿里云AI应用开发二面(2026-03-21)·面经深度解析
前端·css·人工智能·阿里云·ai·面试·vue
工业互联网专业3 天前
基于Python的黑龙江旅游景点数据分析系统的实现_flask+spider
python·flask·vue·毕业设计·源码·课程设计·spider
大叔_爱编程3 天前
基于协同过滤算法的理财产品推荐系统-flask
python·flask·vue·毕业设计·源码·课程设计·协同过滤
小彭努力中3 天前
193.Vue3 + OpenLayers 实战:圆孔相机模型推算卫星拍摄区域
vue.js·数码相机·vue·openlayers·geojson
小彭努力中3 天前
192.Vue3 + OpenLayers 实战:点击地图 Feature,列表自动滚动定位
vue·webgl·openlayers·geojson·webgis
百锦再4 天前
Vue不是万能的:前后端不分离开发的优势
前端·javascript·vue.js·前端框架·vue
BUG创建者4 天前
openlayers上跟据经纬度画出轨迹
开发语言·javascript·vue·html
ん贤4 天前
首屏优化实践:如何将 Vue3 + Vite 项目的加载速度提升3倍
性能优化·vue·vite