这是我参与「第五届青训营」伴学笔记创作活动的第 8 天
1. 介绍
针对一个对象
设置代理,控制这个对象的访问
用户不得直接访问对象,而要通过代理
2. 举例
房产中介
明星经纪人:明星搞艺术不谈钱,你去和经纪人谈钱,明星在和经纪人谈钱
3. 代理模式
1)UML 类图
2)代码演示
class RealImg {
fileName: string;
constructor(fileName: string) {
this.fileName = fileName;
}
display() {
this.loadFromDist();
console.log("display...", this.fileName);
}
private loadFromDist() {
console.log("loading...", this.fileName);
}
}
class ProxyImg {
fileName: RealImg;
constructor(fileName: string) {
this.fileName = new RealImg(fileName);
}
display() {
this.fileName.display();
}
}
const n = new ProxyImg("xiaochai");
n.display();
3)是否符合设计原则
代理和目标分离,解耦
代理可自行扩展
目标也可自行扩展
4. 代理模式 - 场景
DOM 事件代理(委托)
Webpack devServer proxy 正向代理
Nginx 反向代理
1)DOM 事件代理
事件绑定到父容器上,而非目标节点
适合目标较多或数量不确定(如无线加载的瀑布流图片列表)
const container = document.getElementById("container");
if (container) {
// DOM 事件代理(委托)
container.addEventListener("click", event => {
const target = event.target as Element;
if (target.nodeName === "A") {
alert(target.innerHTML);
}
});
}
2)Webpack devServer proxy 正向代理
开发环境,前端请求服务端 API
代理到本地服务器,或者使用 mock 接口
正向代理
// webpack.config.js
{
devServer: {
port: 3000,
proxy:{
'/api': 'http://localhost:8081' // 用nodejs起一个服务器
}
}
}
// 发起请求
import axios formm 'axios'
document.getElementById('btn1')?addEventListener('click', () => {
axios.get('/api/getInfo')
.then(res => console.log(res.data))
})
3)Nginx 反向代理
4)总结
DOM 事件代理(委托)
Webpack devServer proxy 正向代理
Nginx 反向代理
5. Proxy 语法
模拟:明星经纪人
Proxy 的应用场景
Proxy 可能遇到的坑
1)模拟:明星经纪人
// 明细
const star = {
name: "张三",
age: 25,
phone: "18611112222",
price: 0, // 明星不谈钱,俗
};
// 经纪人
const agent = new Proxy(star, {
get(target, key) {
if (key === "phone") {
return "13966667777"; // 经纪人电话,明星电话不能泄露
}
if (key === "price") {
return 100 * 1000; // 明星报价
}
return Reflect.get(target, key);
},
set(target, key, val): boolean {
if (key === "price") {
if (val < 100 * 1000) {
throw new Error("报价太低,合作失败");
} else {
console.log("圆子不错,合作成功");
Reflect.set(target, key, val);
return true;
}
}
// 其他属性不可设置
return false;
},
});
console.log(agent.name);
console.log(agent.age);
console.log(agent.phone);
console.log(agent.price);
// agent.price = 666;
agent.price = 110000;
console.log(agent.price);
2)Proxy 使用场景
跟踪属性访问
隐藏属性
验证属性
记录实例
A. 跟踪属性访问
// 1. 跟踪属性访问
const user = {
name: "张三",
};
const proxy = new Proxy(user, {
get(...args) {
console.log("get..."); // 监听
return Reflect.get(...args);
},
set(target, key, val) {
console.log("set..."); // 监听
return Reflect.get(target, key, val);
},
});
proxy.name;
proxy.name = "李四";
B. 隐藏属性
const hiddenProps = ["girlfriend"];
const user = {
name: "张三",
age: 25,
girlfriend: "小红",
};
const proxy = new Proxy(user, {
get(target, key) {
if (hiddenProps.includes(key as string)) return undefined;
return Reflect.get(target, key);
},
has(target, key) {
if (hiddenProps.includes(key as string)) return false;
return Reflect.get(target, key);
},
set(target, key, val) {
if (hiddenProps.includes(key as string)) return false;
return Reflect.get(target, key, val);
},
});
proxy.girlfriend = "小丽"; // error 抛错
console.log(proxy.girlfriend); // undefined
console.log("girlfriend" in proxy); // false 因为has里做了隐藏
C. 验证属性格式
const user = {
name: "张三",
age: 25,
};
const proxy = new Proxy(user, {
set(target, key, val) {
if (key === "age") {
if (typeof val !== "number") return false;
}
return Reflect.get(target, key, val);
},
});
D. 记录实例
const userList = new Set(); // 每次初始化 user,都记录在这里
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
const ProxyUser = new Proxy(User, {
construct(...agrs) {
const user = Reflect.construct(...agrs);
userList.add(user); // 记录
return user;
},
});
const user1 = new ProxyUser("张三");
const user2 = new ProxyUser("李四");
console.log(userList);
3)Proxy 可能遇到的坑
捕获器不变式
关于 this
A. 捕获器不变式
// 1. 捕获器不变式 - 如果你原有的属性进行了一些描述符限制,代理则不能再去更改原有对象的属性的一些限制
// 代理之前一定要先看看,属性的描述符是否符合你的预期
const obj = { x: 100, y: 0 };
Object.defineProperty(obj, "y", {
value: 200,
writable: false,
configurable: false,
});
const proxy = new Proxy(obj, {
get() {
return "abc";
},
});
console.log(proxy.x);
console.log(proxy.y);
代理之前一定要先看看,属性的描述符是否符合你的预期
B. this
const user = {
name: "张三",
getName() {
console.log("this...", this); // this 在执行中确定的
return this.name;
},
};
const proxy = new Proxy(user, {});
// user.getName() 在这里执行 this 就是 user
proxy.getName(); // 这里就是 proxy
代理的方法里面存在 this 的时候,一定要注意这个问题
4)总结
模拟:明星经纪人
Proxy 的应用场景
- 属性跟踪、属性隐藏、类型校验、记录实例
Proxy 可能会遇到的坑
6. 总结
1)内容回顾
概念介绍,解决的问题
代码演示 和 UML 类图
应用场景 + Proxy 语法
2)重要细节
正向代理(客户端代理) vs 反向代理(服务端代理)
3)注意事项
注意 Proxy 语法的几个坑