自建开发工具系列 -Webkit 内存动量监控 UI(四)
发布于: 3 小时前
本期将 UI 部分分为计算核心部分和输出 UI 的部分, 分类规划逻辑与实践
计算核心部分使用的纯函数,状态其实没有存储,而是使用 DOM 的数据进行状态增补。
const nodeUI = document.createElement('div');
nodeUI.id = 'nodeUI';
nodeUI.style.cssText = `position:relative;width:120px;height:${TOOLS_HEIGHT}px;background-color:lightyellow`;
panel.appendChild(nodeUI);
// 刷新后增加新的内存值和GC状态
while (nodeUI.children.length < 120) {
const bar = document.createElement('span');
bar.style.cssText = `width:1px;height:${TOOLS_HEIGHT}px;float:left;background-color:black`;
nodeUI.appendChild(bar);
}
复制代码
现在先制定一个 panel,然后将内存记录按照柱状条更新在 panel 中,结合 GC,需要合并两种信息。
const updateData = function (dom, height, color) {
const child = dom.appendChild(dom.firstChild);
child.style.height = `${height}px`;
if (color) child.style.backgroundColor = color;
};
const refreshGraph = function (dom, oHFactor, hFactor) {
[].forEach.call(dom.children, (c) => {
const cHeight = c.style.height.substring(0, c.style.height.length - 2);
// 转换到 MB
const newVal = TOOLS_HEIGHT - ((TOOLS_HEIGHT - cHeight) / oHFactor) * hFactor;
c.style.height = `${newVal}px`;
});
};
复制代码
然后再输出刷新函数
return {
domElement: toolsDom,
update() {
// 每秒刷新
if (Date.now() - lastTime < 1000 / 1) return;
lastTime = Date.now();
delta = performance.memory.usedJSHeapSize - lastUsedHeap;
lastUsedHeap = performance.memory.usedJSHeapSize;
// GC 判定,如果是清空内存,则使用绿色标识
color = delta < 0 ? 'green' : 'black';
ms = lastUsedHeap;
memoryBottom = Math.min(memoryBottom, ms);
memoryTop = Math.max(memoryTop, ms);
noticeLabel.textContent = window.performance.memory.jsHeapSizeLimit ? `内存使用: ${bytesToMB(ms)}` : 'Only Webkit';
mbValue = ms / (1024 * 1024);
if (mbValue > MemoryLimitValue) {
factor = (mbValue - (mbValue % TOOLS_HEIGHT)) / TOOLS_HEIGHT;
newThreshold = TOOLS_HEIGHT * (factor + 1);
refreshGraph(nodeUI, TOOLS_HEIGHT / MemoryLimitValue, TOOLS_HEIGHT / newThreshold);
MemoryLimitValue = newThreshold;
}
updateData(nodeUI, TOOLS_HEIGHT - mbValue * (TOOLS_HEIGHT / MemoryLimitValue), color);
}
};
复制代码
完成体的代码为,放在目录的 utils.js 里
const displayPanel = function () {
let memoryBottom = 100;
let memoryTop = 0;
const TOOLS_HEIGHT = 30;
let MemoryLimitValue = TOOLS_HEIGHT;
const toolsDom = document.createElement('div');
toolsDom.id = 'twm';
toolsDom.className = 'tools';
toolsDom.style.cssText = 'width:130;height:48px;opacity:0.9;cursor:pointer;overflow:hidden;z-index:10000;will-change:transform;';
const panel = document.createElement('div');
panel.id = 'ms';
panel.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:black;';
toolsDom.appendChild(panel);
const noticeLabel = document.createElement('div');
noticeLabel.id = 'noticeLabel';
noticeLabel.style.cssText = 'color:white;font-family:Arial;font-size:9px;font-weight:bold;line-height:15px';
noticeLabel.innerHTML = '';
panel.appendChild(noticeLabel);
const nodeUI = document.createElement('div');
nodeUI.id = 'nodeUI';
nodeUI.style.cssText = `position:relative;width:120px;height:${TOOLS_HEIGHT}px;background-color:lightyellow`;
panel.appendChild(nodeUI);
// 刷新后增加新的内存值和GC状态
while (nodeUI.children.length < 120) {
const bar = document.createElement('span');
bar.style.cssText = `width:1px;height:${TOOLS_HEIGHT}px;float:left;background-color:black`;
nodeUI.appendChild(bar);
}
const updateData = function (dom, height, color) {
const child = dom.appendChild(dom.firstChild);
child.style.height = `${height}px`;
if (color) child.style.backgroundColor = color;
};
const refreshGraph = function (dom, oHFactor, hFactor) {
[].forEach.call(dom.children, (c) => {
const cHeight = c.style.height.substring(0, c.style.height.length - 2);
// 转换到 MB
const newVal = TOOLS_HEIGHT - ((TOOLS_HEIGHT - cHeight) / oHFactor) * hFactor;
c.style.height = `${newVal}px`;
});
};
// 避免没有 window.performance.memory 的浏览器崩溃
if (window.performance && !performance.memory) {
performance.memory = { usedJSHeapSize: 0, totalJSHeapSize: 0 };
}
let precision;
let i;
function bytesToMB(bytes) {
precision = Math.pow(10, 0);
i = Math.floor(Math.log(bytes) / Math.log(1024));
return `${Math.round((bytes * precision) / Math.pow(1024, i)) / precision} MB`;
}
let lastTime = Date.now();
let lastUsedHeap = performance.memory.usedJSHeapSize;
let delta = 0;
let color = 'black';
let ms = 0;
let mbValue = 0;
let factor = 0;
let newThreshold = 0;
return {
domElement: toolsDom,
update() {
// 每秒刷新
if (Date.now() - lastTime < 1000 / 1) return;
lastTime = Date.now();
delta = performance.memory.usedJSHeapSize - lastUsedHeap;
lastUsedHeap = performance.memory.usedJSHeapSize;
// GC 判定,如果是清空内存,则使用绿色标识
color = delta < 0 ? 'green' : 'black';
ms = lastUsedHeap;
memoryBottom = Math.min(memoryBottom, ms);
memoryTop = Math.max(memoryTop, ms);
noticeLabel.textContent = window.performance.memory.jsHeapSizeLimit ? `内存使用: ${bytesToMB(ms)}` : 'Only Webkit';
mbValue = ms / (1024 * 1024);
if (mbValue > MemoryLimitValue) {
factor = (mbValue - (mbValue % TOOLS_HEIGHT)) / TOOLS_HEIGHT;
newThreshold = TOOLS_HEIGHT * (factor + 1);
refreshGraph(nodeUI, TOOLS_HEIGHT / MemoryLimitValue, TOOLS_HEIGHT / newThreshold);
MemoryLimitValue = newThreshold;
}
updateData(nodeUI, TOOLS_HEIGHT - mbValue * (TOOLS_HEIGHT / MemoryLimitValue), color);
}
};
};
export default displayPanel;
复制代码
制作 UI 需要将组件以一种简单的方式输出,以便加载时简单引用,这部分用 react 来封装
import * as React from 'react';
import './App.css';
import displayPanel from './utils';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
twm: displayPanel()
};
}
componentDidMount() {
const updateLoop = () => {
if (this.refs.toolsNode.appendChild) {
this.refs.toolsNode.appendChild(this.state.twm.domElement);
}
if (this.state.twm) {
this.state.twm.update();
}
if (window.performance.memory.jsHeapSizeLimit) {
window.setTimeout(updateLoop, 1000)
}
}
window.setTimeout(updateLoop, 1000)
}
render() {
return <div style={{top: '0px', right: '0px', position: 'fixed'}} ref="toolsNode" className="displayPanel" />;
}
}
export default App;
复制代码
然后咱们来看一下在 Create React App 生成的项目中简单的引用
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
复制代码
下期继续更新完善这个工具
划线
评论
复制
发布于: 3 小时前阅读数: 2
版权声明: 本文为 InfoQ 作者【Tim】的原创文章。
原文链接:【http://xie.infoq.cn/article/74512193e502080be86b5b8cc】。文章转载请联系作者。
Tim
关注
还未添加个人签名 2018.05.01 加入
还未添加个人简介
评论