If you think the article is good, please follow, like and share!
Continue to share technical blog posts and follow WeChat public account 👉🏻 Front-end LeBron
Long time no see, why hasn't it been updated for so long?
Emm...The recent performance evaluation season, performance summary, 360 evaluation, there are a lot of things to write, it took a while
Not much nonsense, welcome to the third part of JavaScript design pattern: proxy pattern~
Agency Pattern Concepts
The proxy mode provides a proxy object or placeholder for a certain object, and the proxy object controls the reference of the original object. It can also be understood that the exposed interface is not the original object. In layman's terms, there are more common agency models in life: intermediary, consignment, broker and so on. The significance of this mode is that when the visitor and the interviewee are inconvenient to directly access/contact, a surrogate is provided to handle the transaction process. The actual access is the avatar. After the avatar processes/filters the transaction, It is then transferred to the ontology object to reduce the burden of the ontology object.
Implementation of minimal agent mode
From simple to complex
The above understands the relevant concepts of the proxy mode. Next, we use an example of the simplest proxy mode to implement the proxy mode, and feel the process of the proxy mode from the code.
Talk is Cheap. Show me the code!
- The client sends a request to the server
- The proxy proxy forwards the request to the server
- server processing request
const Request = function () {};
const client = {
requestTo: (server) => {
const req = new Request();
server.receiveRequest(req);
},
};
const server = {
handleRequest: (request) => {
console.log('receive request: ', request);
},
};
const proxy = {
receiveRequest: (request) => {
console.log('proxy request: ', request);
server.handleRequest(request);
},
};
client.requestTo(proxy);
/**
* proxy request: Request {}
* receive request: Request {}
*/
protection agent
The protection agent, as the name implies, is to protect the ontology
Control access to resources based on permissions
Let’s use a scenario and an example to actually feel it. Based on the expansion of the simplest proxy mode above, we can use the protection proxy implementation to filter requests that do not pass identity verification, listen to the server ready to send requests and other operations to protect the entity server. It is not attacked by illegal requests and reduces the burden on the server.
const proxy = {
receiveRequest: (request) => {
// 校验身份
const pass = validatePassport(request);
if (pass) {
// 监听服务端 ready 后代理请求
server.listenReady(() => {
console.log('proxy request: ', request);
server.handleRequest(request);
});
}
},
};
virtual agent
As the representative of creating expensive objects, virtual agents assist in controlling the creation of expensive resources. When an object is really needed, it will be created. The virtual agent will act as a stand-in for the object. After the object is created, the resource will be directly delegated to entity object
The following will implement an example of a virtual agent to implement image preloading, and feel the role of the virtual agent from the code and the actual scene.
- The entity image object is mounted in the body
Due to the high time-consuming and high overhead of loading images, when loading image resources
- Set the entity image object to the loading state
- Perform image resource loading using an alias object
- Listen to the completion of the loading of the avatar object resource, and replace the resource with the entity object
const img = (() => {
const imgNode = document.createElement('img');
document.body.appendChild(img);
return {
setSrc: (src) => {
imgNode.src = src;
},
setLoading: () => {
imgNode.src = 'loading.gif'
}
};
})();
const proxyImg = (() => {
// 替身图片对象
const tempImg = new Image();
// 监听资源加载完成,将资源替换给实体图片对象
tempImg.onload = function () {
img.setSrc(this.src);
};
return {
// 代理开始将实体对象设置为loading状态,使用替身对象开始加载图片资源
setSrc:(src)=>{
img.setLoading()
tempImg.src = src;
}
}
})();
proxyImg.setSrc('file.jpg')
Application of proxy mode
After reading the protection proxy and virtual proxy, let's take a look at some specific applications of the proxy mode in the front end
Request optimization (buried point, wrong data aggregation report)
Some time ago, I was fortunate to be invited to participate in the judges of the ByteTech Byte Youth Training Camp, mainly participating in the evaluation of the front-end monitoring system theme project. Front-end monitoring will involve the reporting of some errors and other information, and some projects only implement the simplest HTTP request reporting.
Some projects have made the following optimizations on this content, which is a more appropriate proxy mode practice scenario:
Navigator.sendBeacon
- Make the user agent send data to the server asynchronously ( HTTP POST ) when it has a chance, without affecting interactive performance
- https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
Data aggregation reporting (the optimized version without proxy mode is, each report uses request reporting)
Reduce the number of requests and aggregate multiple events/information for reporting
- timing
- Quantitative grouping
The following is a simple implementation of the schematic code for the two reporting
- timing
const events = [];
const TIMER = 10000;
let timer = null;
const init = () => {
// 初始化时启动定时器
timer = setInterval(() => {
// 定时使用 sendBeacon 上报
const evts = events.splice(0, events.length);
navigator.sendBeacon('/path', { events: evts });
}, TIMER);
};
const destroyed = () => {
// 销毁时清除定时器
clearInterval(timer);
};
const report = (eventName, data) => {
// sdk 上报工具函数,聚合事件
events.push({
eventName,
data,
});
};
- Quantitative grouping
const events = [];
const LIMIT = 10;
const reportRequest = () => {
// 定量分组使用 sendBeacon 上报
const evts = events.splice(0, LIMIT);
navigator.sendBeacon('/path', { events: evts });
};
const report = (eventName, data) => {
// sdk 上报工具函数,聚合事件
events.push({
eventName,
data,
});
if (events.length >= LIMIT) {
reportRequest();
}
};
Data caching proxy
I saw an interview question before: How does the front end realize the automatic deletion of the cache when the cache expires?
- If you think positively, the cache expires and the program is closed, how to delete it?
Another angle: do not set immediately when it expires, and only need to judge whether the cache expires when getting
Judging whether it has expired at get, and then delete it after it expires~
- Proxy cache middleware through ProxyStorage to support setting cache expiration time
- Proxy layer, easy to switch cache middleware, increase maintainability
const Storage = {
set(key, value, maxAge) {
localStorage.setItem(
key,
JSON.stringify({
maxAge,
value,
time: Date.now(),
})
);
},
get(key) {
const v = localStorage.getItem(key);
if (!v) {
return null;
}
const { maxAge, value, time } = JSON.parse(v);
const now = Date.now();
if (now < time + maxAge * 1000) {
return value;
} else {
localStorage.removeItem(key);
return null;
}
},
has(key) {
const v = localStorage.getItem(key);
if (!v) {
return false;
}
const { maxAge, time } = JSON.parse(v);
const now = Date.now();
if (now < time + maxAge * 1000) {
return true;
} else {
localStorage.removeItem(key);
return false;
}
},
};
Encapsulation of the request function
By encapsulating the request function in the proxy mode, the following functions can be achieved:
- Implant common parameters, common request headers
- Global request buried point report
- Global exception status code handler
- Global request error, exception reporting and handling
const SUCCESS_STATUS_CODE = 200,
FAIL_STATUS_CODE = 400;
const isValidHttpStatus = (statusCode) =>
statusCode >= SUCCESS_STATUS_CODE && statusCode < FAIL_STATUS_CODE;
const ErrorCode = {
NotLogin: 2022,
};
const ErrorHandler = {
[ErrorCode.NotLogin]: redirectToLoginPage,
};
const request = async (reqParams) => {
const { headers, method, data, params, url } = reqParams;
// 封装请求参数,植入通用参数、通用请求头
const requestObj = {
url: url + `?${qs.stringify({ ...commonParams, ...params })}`,
headers: { ...commonHeaders, ...headers },
data,
method,
start: Date.now(),
};
try {
// 上报请求开始埋点
reportEvent(AJAX_START, requestObj);
const res = await ajax(requestObj);
requestObj.end = Date.now();
requestObj.response = res;
// 上报请求结束埋点
reportEvent(AJAX_END, requestObj);
const { statusCode, data: resData } = res;
const { errorCode } = resData;
if (!isValidHttpStatus(statusCode)) {
// 异常状态码埋点上报
reportEvent(AJAX_ERROR, requestObj);
throw resData;
} else if (errorCode) {
// 错误码全局处理器定义,未定义则把错误抛出给上层业务处理
reportEvent(AJAX_WARNING, requestObj);
if (ErrorHandler(errorCode)) {
ErrorHandler(errorCode)();
} else {
throw resData;
}
} else {
// 正常返回请求数据
return resData;
}
} catch (error) {
// 捕获错误并进行埋点上报,抛给上层业务处理
requestObj.error = error;
reportEvent(AJAX_ERROR, requestObj);
throw error;
}
};
Proxy pattern in Vue
Proxies data, methods, computed properties, etc. to component instances
let vm = new Vue({
data: {
msg: 'hello',
vue: 'vue'
},
computed:{
helloVue(){
return this.msg + ' ' + this.vue
}
},
mounted(){
console.log(this.helloVue)
}
})
Proxy pattern in Koa
context The properties of the context proxy encapsulated in request and response
app.use((context) => {
console.log(context.request.url)
console.log(context.url)
console.log(context.response.body)
console.log(context.body)
})
Other proxy modes
In addition to the proxy mode applications mentioned in this article, there are many other variants and applications
Here is a brief list and introduction, and will not be explained in detail one by one.
- Firewall proxy: Control access to network resources and protect the subject from "bad guys" approaching
- Remote agent: Provide local representation for an object in different address spaces, such as everyone's "Scientific Internet"
- Protection proxy: used when objects should have different access rights
- Smart reference proxy: instead of a simple pointer, it performs some additional operations when accessing an object, such as counting the number of times an object has been referenced (possibly used for GC reference counting
summary
There are many sub-categories of proxy mode. Commonly used in front-end development work are virtual proxies, protection proxies, and caching proxies. In fact, after reading this, everyone can also feel that an action often done in daily development work - "encapsulation" is actually the application of the proxy mode~
Design pattern series article recommendation
Continue to share technical blog posts, welcome to pay attention!
- WeChat public account: Front-end LeBron
- Nuggets: Front-end LeBron
- Knowing: Front-end LeBron
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。