自建开发工具系列 -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 加入
还未添加个人简介











评论