需要了解Proxy、Reflect函数
目录结构:
配置环境:
- package.json
javascript
{
"name": "vue-reactive",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev":"webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
}
}
javascript
// 安装ts
npm install -g typescript
// 初始化
npm init
- tsconfig.json 关键的两个配置,target和module
javascript
{
"compilerOptions": {
"target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "es2015", /* Specify what module code is generated. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
- reactive.ts
javascript
//@ts-nocheck
import {track,trigger} from './effect'
// 判断代理对象的值是否为空,不为空的话值是否是一个对象
const isObject = (target)=>target!=null&&typeof target==='object'
// 创建代理,proxy对象,使用其中的get和set方法
export const reactive = <T extends object>(targert : T)=>{
let proxy = new Proxy(targert,{
get(target,key,receiver){
// Reflect:与proxy搭配使用,不会在出错时抛出错误,如果没有该属性的值返回undefined
let res = Reflect.get(target,key,receiver)
track(target,key)
// 代理对象的值是否为一个对象,如果是的话就进行代理,对深层的对象进行代理,使用递归的方法
if(isObject(res)){
return reactive(res)
}
return res
},
set(target,key,value,receiver){
// Reflect:给对象target的key属性设置值为value,如果成功返回true,失败返回false
let res = Reflect.set(target,key,value,receiver)
trigger(target,key)
return res
},
})
console.log(proxy);
return proxy
}
- effect.ts
javascript
//@ts-nocheck
let activeEffect;
// 用来执行副作用函数的方法,至今不明白真正的作用,fn就是副作用函数,是html页面传过来的函数
export const effect = (fn:Function)=>{
const _effect = function(){
activeEffect = _effect
fn()
}
_effect()
}
/*
** 可能是因为这是个学习响应式的简略的程序,所以必须先通过get方法创建targetMap对象这个数据结构才能使用set方法
1. 给user这个响应式对象创造另一种结构
3. 先通过响应式对象的get方法,执行此方法中的track方法,建立一个Map结构
4. 通过Map结构,将响应式对象和副作用函数建立联系,副作用函数是set结构
targetMap:{
{name: 'jack', age: 18, first: {...}} : {
"name" : "副作用函数",
"age" : "副作用函数",
"first: {...}" : "副作用函数",
"second: {...}" : "副作用函数",
"third" : "副作用函数"
}
}
*/
const targetMap = new WeakMap()
export const track = (target,key)=>{
console.log("执行了reactive.tarck");
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)
console.log(targetMap);
}
/*
1. 触发响应式对象的某一个key对应的value发生改变时,执行proxy的set方法,执行此方法中的trigger方法
2. 从targetMap中通过key找到对应的value,value中储存着副作用函数,依次将副作用函数全部执行
3. 假如在页面中有3处使用了响应式对象,则value中储存着3条副作用函数
*/
export const trigger = (target,key)=>{
console.log("执行了reactive.trigger");
const depsMap = targetMap.get(target)
const deps = depsMap.get(key)
deps.forEach(effect=>effect())
}
- index.html
javascript
<html>
<body>
<div id="app">
</div>
<script type="module">
import { reactive} from './reactive.js'
import { effect } from './effect.js'
// 这里的user已经通过reactive成为了一个proxy对象,对user对象的操作,就是对proxy对象的操作
const user = reactive({
name: 'Tom',
age: 18,
first:{
second:{
third:"第三层"
}
}
})
// 执行到这里的时候,只是做个proxy的代理
effect(() => {
// 在执行${user.name}时才执行proxy代理中相应的get方法
// 每个对象对应一个targetMap,但是结构中并不是储存所有的属性的map结构,只有在执行到对应属性的get方法时才在targetMap中添加该属性对应的结构
document.querySelector('#app').innerText = `${user.name}---${user.first.second.third}`
// document.querySelector('#app').innerText = `${user.name} -- ${user.age}`
})
user.name="jack"//执行赋值的操作时才会执行proxy的set方法
// setTimeout(() => {
// user.name="jack",//执行赋值的操作时才会执行proxy的set方法
// setTimeout(()=>{
// user.first.second.third="第三层修改"
// },1000)
// },1000)
</script>
</body>
</html>
- 执行操作:
- 在VScode中安装插件:
Live Server
- 在终端中运行
tsc
,将ts文件编译为js文件 - 修改reactive.js文件,
import { track, trigger } from './effect.js';
- 右键
index.html
,使用Open with live server