函数式组件 -- 触发锁屏
1. 故事背景
在前端开发中,常常需要实现锁屏功能;最为常见的情况为点击某个按钮进入锁屏。本文从实际业务出发,从已经实现的锁屏功能中抽取出锁屏功能模板,从功能实现和注意细节两个角度进行说明,以实现后续开发过程中的复用。
2. 锁屏活动流程
-
- 点击锁屏按钮
-
- 记录锁屏前的前端路由到本地存储,方便锁屏解除之后回到当前页
-
- 关闭当前页面弹出的所有窗口
-
- 关闭锁屏期间不需要的通信服务,提升性能
-
- 使用
react-router-dom
的useNavigate
进行前端路由跳转
- 使用
-
- 通知后端更新前端的锁屏信息(某些服务可以先暂停,保存时间戳等)
2.1 本地存储方法 setLock2Storage
将锁屏前地址写入本地,方便解除锁屏之后恢复
tsx
// 将锁屏前地址写入本地,方便解除锁屏之后恢复
const setLock2Storage = (pathName: string) => {
// store some info in storage
sessionStorage.setItem('urlBeforeLock', pathName);
};
2.2 关闭现有弹窗方法 clearModals
父组件通过接口将回调函数队列传入此组件,在clearModals方法中依次执行这些回调函数,逐个关闭打开的Modal
tsx
clearModals(){
this.props.clearModals?.forEach(cb => void cb());
}
2.3 锁屏期间关闭和后端不必要的通信服务的方法 clearWs
clearWs方法显然需要从父组件传递过来,直接调用就可以了:this.props.clearWs();
2.4 导航到前端锁屏路由
使用钩子函数``即可完成任务,对于类组件而言,使用适配器设计模式稍微包装一下即可
tsx
this.props.navigate("/lock");
...
// 包装组件
const withNavigation = (Component) => {
return (props) => <Component {...props} navigate={useNavigate()} />;
};
// 暴露包装后的组件
export default withNavigation(Lock);
2.5 发送消息通知后端更新状态的方法 notify
使用的是fetch方法和URL构造函数
tsx
// 通知后端更新锁屏状态
const notify = (apiUrl:string, pathName: string) => {
// send some info to server
const requestUrl = new URL('/Lock', apiUrl);
const requestBody = {
params: {
pathName: pathName
}
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
};
fetch(requestUrl, requestOptions);
};
2.6 功能内聚方法 handleClick(按钮点击函数的回调)
tsx
handleClick() {
// 将锁屏前的url存到本地,解除锁屏的时候能够恢复到锁屏之间的位置
setLock2Storage(this.location);
// 锁屏之前关闭当前页面上的所有弹窗
this.clearModals();
// 锁屏之后和有些ws的通信需要关掉,提高性能
this.props.clearWs();
// 导航到锁屏
this.props.navigate("/lock");
// 向后端发送请求,更新前端状态
notify(this.appUri, this.location);
}
3. Lock组件的接口 -- ILockProps
tsx
export interface ILockProps {
cls4style: string; // 父组件为了控制按钮样式传递过来的类名
navigate: NavigateFunction; // withNavigation钩子函数向props中注入的方法,作用是进行前端跳转
clearModals: Array<()=>void>; // 清除当前屏幕上的弹窗
clearWs: ()=>void; // 关闭锁屏之后不需要通信的ws
notify: ()=>void; // 通知后端更新前端锁屏状态
}
4. Lock组件的参数
tsx
private appUri = 'http://localhost:3000'; // 后端通讯基地址
private location = location.pathname; // 当前页路由
private copywriting = "锁屏"; // 按钮上的文案
5. Lock组件依赖库
tsx
import React from "react";
import { Tooltip } from "antd"; // 组件
import { useNavigate } from "react-router-dom"; // 前端路由钩子
import { NavigateFunction } from "react-router/dist/lib/hooks"; // 类型约束
6. Lock组件内容 render函数的返回值
tsx
render() {
return (
<Tooltip
title={this.copywriting}
placement="top"
>
<span
className={this.props.cls4style}
onClick={this.handleClick}
style={{width: 100}}
>
{this.copywriting}
</span>
</Tooltip>
);
}
7. Lock组件全部内容
tsx
import React from "react";
import { Tooltip } from "antd";
import { useNavigate } from "react-router-dom";
import { NavigateFunction } from "react-router/dist/lib/hooks";
const withNavigation = (Component) => {
return (props) => <Component {...props} navigate={useNavigate()} />;
};
export interface ILockProps {
cls4style: string; // 父组件为了控制按钮样式传递过来的类名
navigate: NavigateFunction; // withNavigation钩子函数向props中注入的方法,作用是进行前端跳转
clearModals: Array<()=>void>; // 清除当前屏幕上的弹窗
clearWs: ()=>void; // 关闭锁屏之后不需要通信的ws
notify: ()=>void; // 通知后端更新前端锁屏状态
}
// 将锁屏前地址写入本地,方便解除锁屏之后恢复
const setLock2Storage = (pathName: string) => {
// store some info in storage
sessionStorage.setItem('urlBeforeLock', pathName);
};
// 通知后端更新锁屏状态
const notify = (apiUrl:string, pathName: string) => {
// send some info to server
const requestUrl = new URL('/Lock', apiUrl);
const requestBody = {
params: {
pathName: pathName
}
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
};
fetch(requestUrl, requestOptions);
};
class Lock extends React.Component<ILockProps, undefined> {
private appUri = 'http://localhost:3000'; // 后端通讯基地址
private location = location.pathname; // 当前页路由
private copywriting = "锁屏"; // 按钮上的文案
handleClick() {
// 将锁屏前的url存到本地,解除锁屏的时候能够恢复到锁屏之间的位置
setLock2Storage(this.location);
// 锁屏之前关闭当前页面上的所有弹窗
this.clearModals();
// 锁屏之后和有些ws的通信需要关掉,提高性能
this.props.clearWs();
// 导航到锁屏
this.props.navigate("/lock");
// 向后端发送请求,更新前端状态
notify(this.appUri, this.location);
}
clearModals(){
this.props.clearModals?.forEach(cb => void cb());
}
render() {
return (
<Tooltip
title={this.copywriting}
placement="top"
>
<span
className={this.props.cls4style}
onClick={this.handleClick}
style={{display: 'inline-block', width: '126px'}}
>
{this.copywriting}
</span>
</Tooltip>
);
}
}
export default withNavigation(Lock);