Vue3响应式源码实现
初始化项目结构
vue-proxy
├── effect.js
├── effect.ts
├── index.html
├── index.js
├── package.json
├── reactive.js
├── reactive.ts
└── webpack.config.js
reactive.ts
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
// 更新视图的方法
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
npm install typescript -g
新建 index.js
,分别引入 effect.js
和 reactive.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
<!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>
然后再根目录执行
npm init -y
安装依赖
npm install webpack webpack-cli webpack-dev-server html-webpack-plugin -D
然后新建 webpack.config.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, // 是否自动打开浏览器
},
}
执行命令启动项目
npx webpack serve