经过一段时间的学习,自己简单模拟实现了vue3中部分方法的功能
// 定义reactiveHandler处理对象
const reactiveHandler = {
// 获取属性值
get(target, prop) {
if (prop === "_is_reactive") return true;
console.log("拦截到了读取属性值", prop);
return Reflect.get(target, prop);
},
// 修改属性值或者添加属性
set(target, prop, value) {
console.log("拦截到了修改属性", prop, value);
return Reflect.set(target, prop, value);
},
// 删除属性
deleteProperty(target, prop) {
console.log("拦截到了删除属性", prop);
return Reflect.deleteProperty(target, prop);
},
};
// shallowReactive和reactive
function shallowReactive(target) {
// 判断传入的参数是不是object类型
if (target && typeof target === "object") {
return new Proxy(target, reactiveHandler);
} else {
// 如果是基本类型,直接返回
return target;
}
}
function reactive(target) {
// 判断传入的参数是不是object类型
if (target && typeof target === "object") {
// 判断是对象还是数组,并且对数据或对象中的数据进行reactive的递递归处理
if (Array.isArray(target)) {
target.forEach((item, index) => {
target[index] = reactive(item);
});
} else {
Object.keys(target).forEach((key) => {
target[key] = reactive(target[key]);
});
}
return new Proxy(target, reactiveHandler);
} else {
// 如果是基本类型,直接返回
return target;
}
}
const readonlyHandler = {
get(target, prop) {
if (prop === "_is_readonly") return true;
console.log("拦截到了读取数据", prop);
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.warn("不能修改或添加数据,只能读取数据!");
return true;
},
deleteProperty(target, prop) {
console.warn("不能删除数据,只能读取数据!");
return true;
},
};
// shallowReadonly和readonly
function shallowReadonly(target) {
if (target && typeof target === "object") {
return new Proxy(target, readonlyHandler);
} else {
return target;
}
}
function readonly(target) {
if (target && typeof target === "object") {
if (Array.isArray(target)) {
target.forEach((item, index) => {
target[index] = readonly(item);
});
} else {
Object.keys(target).forEach((key) => {
target[key] = readonly(target[key]);
});
}
return new Proxy(target, readonlyHandler);
} else {
return target;
}
}
// shallowRef和ref
function shallowRef(target) {
return {
_is_ref: true, //标识当前对象是ref对象
_value: target,
get value() {
console.log("拦截到了读取数据");
return this._value;
},
set value(val) {
console.log("拦截到了修改数据");
return (this._value = val);
},
};
}
function ref(target) {
if (target && typeof target === "object") {
target = reactive(target);
}
return {
_is_ref: true, //标识当前对象是ref对象
_value: target,
get value() {
console.log("拦截到了读取数据");
return this._value;
},
set value(val) {
console.log("拦截到了修改数据", val);
this._value = val;
},
};
}
// isRef isReactive isReadonly isProxy
function isRef(obj) {
return obj && obj._is_ref ? obj && obj._is_ref : false;
}
function isReactive(obj) {
return obj && obj._is_reactive ? obj && obj._is_reactive : false;
}
function isReadonly(obj) {
return obj && obj._is_readonly ? obj && obj._is_readonly : false;
}
function isProxy(obj) {
return isReactive(obj) || isReadonly(obj);
}
下面为测试代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>手搓vue3中常用api,只是实现劫持操作,页面刷新没有</title>
</head>
<body>
</body>
<script src="./index.js"></script>
<script type="text/javascript">
console.log("-------------------测试shallowReactive--------------------");
const proxyData1 = shallowReactive({
name: "张三",
car: {
color: "red",
type: ["奔驰", "宝马", "劳斯莱斯"]
}
})
proxyData1.name += "~" // 拦截到了读和写
proxyData1.car.color = "blue" // 只拦截到了读
console.log(proxyData1.car.color); // blue
delete proxyData1.name //拦截到了删除数据
delete proxyData1.car.color // 只拦截到了读取数据,删除数据拦截不到
console.log(proxyData1.car); // {type: ["奔驰", "宝马", "劳斯莱斯"]}
console.log("-------------------测试reactive--------------------");
const proxyData2 = reactive({
name: "张三",
car: {
color: "red",
type: ["奔驰", "宝马", "劳斯莱斯"]
}
})
proxyData2.name += "!" // 拦截到了读和写
proxyData2.car.type[0] = "特斯拉" // 拦截到了读和写
delete proxyData2.car.color // 拦截到了读取和删除
console.log("-------------------测试shallowReadonly--------------------");
const proxyData3 = shallowReadonly({
name: "小明",
work: {
address: "北京",
money: 100000000
}
})
proxyData3.name += "@" // 只能读取数据
console.log(proxyData3.name); // 小明
proxyData3.work.address = "保定" //拦截到了读取数据,修改数据没有拦截到
console.log(proxyData3.work.address); // 保定
delete proxyData3.name // 只能读取数据,不能删除数据
delete proxyData3.work.money //拦截到了读取数据,删除数据没有拦截到
console.log(proxyData3.work); // {address: "北京"}
console.log("-------------------测试readonly--------------------");
const proxyData4 = readonly({
name: "小明",
work: {
address: "北京",
money: 100000000
}
})
proxyData4.name += "@" // 只能读取数据
console.log(proxyData4.name); // 小明
proxyData4.work.address = "保定" //只能读取数据,不能修改数据
console.log(proxyData4.work.address); // 北京
delete proxyData4.name // 只能读取数据,不能删除数据
delete proxyData4.work.money //只能读取数据
console.log(proxyData4.work); // {address: '北京', money: 100000000}
console.log("-------------------测试shallowRef--------------------");
const proxyData5 = shallowRef({
name: "小强",
car: {
color: "red"
}
})
console.log(proxyData5.value); // 拦截到了读取数据
proxyData5.value = "!" // 拦截到了修改数据
proxyData5.value.name = "小six" // 拦截不到修改数据
console.log("-------------------测试ref--------------------");
const proxyData6 = ref({
name: "老八",
car: {
type: "宝马"
}
})
console.log(proxyData6.value.car); // 拦截到了读取数据
proxyData6.value.car.type = "奔驰" // 拦截到了读取和修改数据
console.log("-------------------测试isRef--------------------");
console.log(isRef(ref({}))); // true
console.log(isRef(readonly({}))); //false
console.log(isRef(shallowReactive({}))); //false
console.log(isRef(reactive({}))); //false
console.log("-------------------测试isReactive--------------------");
console.log(isReactive(reactive({}))); //true
console.log(isReactive(readonly({}))); //false
console.log(isReactive(shallowReadonly({}))); //false
console.log(isReactive(ref({}))); //false
console.log(isReactive(shallowReactive({}))); //true
console.log("-------------------测试isReadonly--------------------");
console.log(isReadonly(readonly({}))); //true
console.log(isReadonly(shallowReadonly({}))); //true
console.log(isReadonly(reactive({}))); // false
console.log("-------------------测试isProxy--------------------");
console.log(isProxy(readonly({}))); //true
console.log(isProxy(reactive({}))); //true
console.log(isProxy(ref({}))); //false
</script>
</html>
经过测试达到预期效果