Node/Puppeteer/ChromeDevTools疑难:变量属性明明已定义,Shell却提示未定义?原因解析
Hey there, I see exactly where things are going wrong here—let's walk through the issues and get your script working properly.
The Root Cause: Misusing for...in Loops
The error you're hitting comes down to using for...in to iterate over arrays. for...in is designed to loop through object keys, not array elements. When you use it on an array, you're getting the string indices (like "0", "1") instead of the actual coverage entry objects. That's why entry.text is undefined—you're trying to access a property on a string, not the coverage object!
On top of that, there are a few other small bugs in your script that we'll fix along the way:
- You're only iterating over the first entry in your coverage arrays instead of all entries
- A typo in the JS section: you named your collected JS variable
js_cssbut tried to writecovered_jsto file - Same
for...inmistake when looping through therangesarray of each entry
Corrected Script
Here's the fixed version with comments pointing out the key changes:
const puppeteer = require('puppeteer'); const fs = require('fs'); const iPhone = puppeteer.devices['iPhone 6']; (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.emulate(iPhone); // Start coverage collection await Promise.all([ page.coverage.startCSSCoverage(), page.coverage.startJSCoverage() ]); await page.goto('http://127.0.0.1:5500/index.html'); // Stop coverage collection const [cssCoverage, jsCoverage] = await Promise.all([ page.coverage.stopCSSCoverage(), page.coverage.stopJSCoverage() ]); // Process CSS Coverage let cssUsedBytes = 0; let cssTotalBytes = 0; let coveredCss = ""; // Use for...of to iterate over coverage entries (arrays, not objects) for (const entry of cssCoverage) { cssTotalBytes += entry.text.length; console.log(`Total Bytes for ${entry.url}: ${entry.text.length}`); // Same fix for ranges array for (const range of entry.ranges) { cssUsedBytes += range.end - range.start; coveredCss += entry.text.slice(range.start, range.end) + "\n"; } } // Process JS Coverage let jsUsedBytes = 0; let jsTotalBytes = 0; let coveredJs = ""; // Fixed variable name from js_css to coveredJs // Again, for...of for array iteration for (const entry of jsCoverage) { jsTotalBytes += entry.text.length; console.log(`Total Bytes for ${entry.url}: ${entry.text.length}`); for (const range of entry.ranges) { jsUsedBytes += range.end - range.start; coveredJs += entry.text.slice(range.start, range.end) + "\n"; } } // Log stats console.log(`Total Bytes of CSS: ${cssTotalBytes}`); console.log(`Used Bytes of CSS: ${cssUsedBytes}`); console.log(`Total Bytes of JS: ${jsTotalBytes}`); console.log(`Used Bytes of JS: ${jsUsedBytes}`); // Save CSS file fs.writeFile("./exported_css.css", coveredCss, function(err) { if(err) { return console.log(err); } console.log("The CSS file was saved!"); }); // Save JS file (now using the correct variable name) fs.writeFile("./exported_js.js", coveredJs, function(err) { if(err) { return console.log(err); } console.log("The JS file was saved!"); }); await browser.close(); })();
Key Changes Explained
- Swapped all
for...inloops tofor...ofwhen iterating over arrays (cssCoverage,jsCoverage, and each entry'srangesarray) - Removed unnecessary array spreading (you can use the original coverage arrays directly)
- Fixed the variable name typo in the JS section (
js_css→coveredJs) - Adjusted the byte calculation to
range.end - range.start(your original-1was likely a mistake, since slice is end-exclusive)
This should resolve the "Cannot read property 'length' of undefined" error and correctly extract the used CSS/JS into separate files.
内容的提问来源于stack exchange,提问作者syrinj

