控制台的EventStream是什么意思?是后端有大量数据推送到view层
SSE是什么? SSE是server sent event 的缩写, 去搜索服务端推送数据SSE
需求: 实现像idea控制台输出日志功能, 所以需要在后端拿log数据, 点击开关开始live Tail
方法: 下面是点击按钮触发的函数
// 点击开始live Tail
async function handleLiveTail(flag: boolean) {
setLiveTailRefresh(false);
setMoreBtn(false);
setEmpty(false);
isScroll.current = true;
setLiveTailSwitch(flag);
if (!flag) {
setContentSource([]);
setLoading(true);
sessionId.current = String(dayjs().valueOf());
const _eventSource = await new EventSource(
`/skyeye/log/livetail/fetch?uniqueCode=${uniqueCode}&logFileName=${pathName}&searchText=${searchText}&sessionId=${sessionId.current}`,
{ withCredentials: true },
);
_eventSource.onopen = (_e) => {
eventSource.current = _eventSource;
};
_eventSource.onerror = (e) => {
console.log('error________', e);
if (liveTailSwitch) {
eventSource.current?.close();
}
};
_eventSource.onmessage = (e) => {
if (e && e?.data) {
let view: string[] = [];
setContentSource((value) => {
const html = hljs.highlight(e?.data, { language: 'java' }).value;
let newData = value;
const len = value?.length;
if (len >= 1000) {
newData.shift();
}
newData?.push(html);
view = newData;
return newData;
});
viewData(view);
}
};
setLoading(false);
}
}
// 节流渲染数据 后端推送数据太快页面卡段
const viewData = throttle((data: string[]) => {
setViewList([...data]);
const codeBox = document.getElementById('code_box')?.children;
if (codeBox && codeBox?.length > 0 && codeBox[0]) {
scrollTop.current = codeBox[0]?.scrollTop;
if (isScroll.current) {
codeBox[0].scrollTop = codeBox[0]?.scrollHeight;
}
}
}, 2000);
渲染位置
这里用了react-window做了虚拟滚动, 因为最多可展示1000行
<code id={'code_box'} style={{ visibility: loading && !moreBtn ? 'hidden' : 'visible' }}>
<List
height={codeHeight?.height || 600}
itemCount={viewList?.length}
itemSize={24}
width={1136}
style={{ margin: '0 8px', overflowAnchor: 'none' }}
>
{({ index, style }) => {
return <div key={index} style={style} dangerouslySetInnerHTML={{ __html: viewList[index] }}></div>;
}}
</List>
</code>
重点一: 是webpack配置文件中需要加上compress: false
不加这个请求就会一直在pending, 大概等一分钟才会有数据返回, 也可能是项目配置的问题
// 测试服务
devServer: {
compress: false,
}
重点二: 一定要在error方法中判断用户是否关闭了live Tail, 和在页面关闭页面销毁的时候调用EventSource.close()
后端返回较慢, 或者请求一直在pending, 或者是nginx配置错误的时候,
就会走到eventSource.onerror方法 并会自己发起重连请求, 假如与用户手动点击关闭错开, 就会造成内存泄漏,程序错误等问题
结果: 发送请求之后就是这样
模拟控制台的滚动效果
另外再写一个细节的需求, Live Tail时,后端返回的最新内容,滚动滑块也应该在最底部, 应当是一直向上滚动, 展示最新的数据; 当用户手动向上滚动后, 后端返回最新内容, 滚动滑块位置不改变。
实现方法:
一、监听滚动,判断滚动滑块的高度是否大于等于后端返回数据时保存的高度,
大于时: 设置isSrcoll为true. 表示后端返回新数据后,滚动滑块也滚动到最新位置
小于时: 为false. 表示用户向上查看, 后端返回新数据, 滚动滑块位置不改变
// 监听滚动条
useEventListener(
'scroll',
(_e) => {
const codeBox = document.getElementById('code_box')?.children;
if (codeBox && codeBox?.length > 0 && codeBox[0] && codeBox[0]?.scrollTop >= scrollTop.current) {
isScroll.current = true;
} else {
isScroll.current = false;
}
},
{ target: document.getElementById('code_box')?.children ? document.getElementById('code_box')?.children[0] : null },
);
二、在后端返回数据的方法里判断isSrcoll, 并存储滚动条高度
const codeBox = document.getElementById('code_box')?.children;
if (codeBox && codeBox?.length > 0 && codeBox[0]) {
scrollTop.current = codeBox[0]?.scrollTop;
if (isScroll.current) {
codeBox[0].scrollTop = codeBox[0]?.scrollHeight;
}
}