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
相关推荐
Chikaoya39 分钟前
项目中用户数据获取遇到bug
前端·typescript·vue·bug
计算机-秋大田5 小时前
基于微信小程序的乡村研学游平台设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
计算机-秋大田16 小时前
基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
龙哥·三年风水1 天前
群控系统服务端开发模式-应用开发-前端个人信息功能
分布式·vue·群控系统
真的很上进1 天前
⚡️如何在 React 和 Next.js 项目里优雅的使用 Zustand
java·前端·javascript·react.js·前端框架·vue·es6
幸运小圣1 天前
Vue3 -- 项目配置之prettier【企业级项目配置保姆级教程2】
前端·vue.js·vue
计算机-秋大田1 天前
基于微信小程序的农场管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
fukaiit1 天前
vue项目npm run serve出现【- Network: unavailable】(从排查到放弃)
npm·vue·node·win11
_处女座程序员的日常2 天前
Rollup failed to resolve import “destr“ from ***/node_modules/pinia-plugin-pers
javascript·uni-app·vue
四喜花露水2 天前
vue2.x elementui 固定顶部、左侧菜单与面包屑,自适应 iframe 页面布局
前端·elementui·vue