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
相关推荐
魂尾ac43 分钟前
Django + Vue3 前后端分离技术实现自动化测试平台从零到有系列 <第一章> 之 注册登录实现
后端·python·django·vue
是罐装可乐11 小时前
深入理解 Vue3 Router:三种路由模式的工作原理与实战应用
架构·vue·路由·history·hash·ssr·router
老华带你飞14 小时前
租房平台|租房管理平台小程序系统|基于java的租房系统 设计与实现(源码+数据库+文档)
java·数据库·小程序·vue·论文·毕设·租房系统管理平台
zhz52141 天前
Spring Boot + Redis 缓存性能优化实战:从5秒到毫秒级的性能提升
java·spring boot·redis·缓存·vue
小胖墩有点瘦2 天前
【基于协同过滤的校园二手交易平台】
java·vue·毕业设计·springboot·计算机毕业设计·协同过滤·校园二手交易平台
小圣贤君2 天前
小说创作中的时间轴体验设计:事序图交互与用户体验优化
electron·vue·甘特图·时序图·写作软件
知识分享小能手2 天前
React学习教程,从入门到精通,React 构造函数(Constructor)完整语法知识点与案例详解(16)
前端·javascript·学习·react.js·架构·前端框架·vue
@AfeiyuO3 天前
分类别柱状图(Vue3)
typescript·vue·echarts
大虾写代码3 天前
vue3+TS项目配置Eslint+prettier+husky语法校验
前端·vue·eslint
机构师3 天前
<uniapp><指针组件>基于uniapp,编写一个自定义箭头指针组件
javascript·uni-app·vue·html