1. 页面白屏如何排查?
第一步:快速分类(30秒) "页面白屏主要有五种原因:JavaScript执行错误、资源加载失败、CSS样式问题、接口异常和浏览器兼容性。其中JavaScript错误最常见,特别是SPA应用中的未捕获异常。"
第二步:排查方法(1分钟) "我的排查步骤是:首先查看Console面板的错误信息,这能快速定位JS异常;然后检查Network面板确认资源加载状态;接着用Elements面板验证DOM和样式;移动端问题会用vConsole或真机调试。生产环境结合Sentry等监控系统分析。"
第三步:预防措施(30秒) "预防方面建立错误边界、资源容错机制、统一接口异常处理、兼容性检测,同时搭建监控告警体系。"
一、基础检测流程
1. 控制台检查(Console)
// 主动捕获全局错误(放在入口文件最前面)
window.addEventListener('error', function(event) {
console.error('全局捕获:', event.error);
// 可上报到监控系统
});
// 检查console是否有以下类型错误:
// - SyntaxError (语法错误)
// - TypeError (类型错误)
// - ReferenceError (引用错误)
// - 404资源加载失败
2. 网络请求检查(Network)
-
关键指标:
- HTML文档状态码(200/304/404/500)
- JS/CSS资源加载状态
- 接口请求是否阻塞渲染
-
检测示例:
// 检查关键资源是否加载完成
const resourceCheck = () => {
const entries = performance.getEntriesByType('resource');
const criticalResources = entries.filter(entry =>
entry.initiatorType === 'script' ||
entry.initiatorType === 'css'
);
criticalResources.forEach(res => {
if(res.responseStatus >= 400) {
console.error(`资源加载失败: ${res.name}`, res);
}
});
};
window.addEventListener('load', resourceCheck);
二、深度检测方法
1. DOM渲染检测
// 检测DOM树是否正常构建
function checkDOMReady() {
return new Promise((resolve) => {
const check = () => {
if(document.body && document.body.children.length > 0) {
resolve(true);
} else {
setTimeout(check, 50);
}
};
check();
});
}
// 使用示例
checkDOMReady().then((isReady) => {
if(!isReady) {
console.error('DOM渲染超时');
// 上报白屏信息
}
});
2. 框架特定检测
Vue应用检测:
// 在main.js中添加
new Vue({
render: h => h(App),
errorCaptured(err, vm, info) {
console.error('Vue组件错误:', err, info);
// 可上报错误
return false; // 阻止错误继续向上传播
}
}).$mount('#app');
// 检查根组件挂载
if(!document.querySelector('#app').__vue__) {
console.error('Vue根实例挂载失败');
}
React应用检测:
// Error Boundary组件
class ErrorBoundary extends React.Component {
componentDidCatch(error, info) {
console.error('React组件错误:', error, info);
// 上报错误
}
render() {
return this.props.children;
}
}
// 检查React根组件
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ErrorBoundary>
<App />
</ErrorBoundary>
);
三、性能相关检测
1. 长任务检测
// 检测阻塞渲染的长时间任务
const observer = new PerformanceObserver((list) => {
for(const entry of list.getEntries()) {
if(entry.duration > 50) { // 超过50ms的任务
console.warn('长任务影响渲染:', entry);
}
}
});
observer.observe({entryTypes: ["longtask"]});
2. 关键渲染路径监控
// 使用Performance API监控关键时间点
const perfData = window.performance.timing;
const metrics = {
domReady: perfData.domComplete - perfData.domLoading,
loadTime: perfData.loadEventEnd - perfData.navigationStart
};
if(metrics.domReady > 3000) {
console.error('DOM解析时间过长:', metrics);
}
四、自动化检测方案
1. Puppeteer检测脚本
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 监听控制台错误
page.on('console', msg => {
if(msg.type() === 'error') {
console.log('页面错误:', msg.text());
}
});
// 设置超时检测
await Promise.race([
page.goto('https://your-site.com'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('页面加载超时')), 5000)
)
]);
// 检查可见内容
const content = await page.evaluate(() => {
return {
bodyText: document.body.innerText,
childCount: document.body.children.length
};
});
if(content.childCount === 0 || content.bodyText.length < 10) {
console.error('检测到白屏现象');
}
await browser.close();
})();
2. 真实用户监控(RUM)
// 使用浏览器的MutationObserver监控DOM变化
const observer = new MutationObserver((mutations) => {
if(!document.querySelector('#app')?.innerHTML) {
// 上报白屏事件
beacon.send('white-screen', {
url: location.href,
ua: navigator.userAgent
});
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
五、常见白屏场景示例
-
资源加载失败:
<!-- 错误的资源路径 --> <script src="/wrong-path/app.js"></script> -
语法错误:
// 缺少括号导致整个脚本不执行 function test() { console.log('hello' } -
框架初始化失败:
// Vue示例 - 挂载元素不存在 new Vue({el: '#not-exist'}); -
CSS阻塞:
<!-- 不正确的CSS引入阻塞渲染 --> <link rel="stylesheet" href="nonexist.css"> -
第三方库冲突:
// 两个库都修改了Array原型 libraryA.modifyPrototype(); libraryB.modifyPrototype(); // 冲突导致错误 ```
2.前端埋点
一、页面生命周期埋点
1. 页面加载阶段
// 记录页面开始加载时间
const pageStartTime = Date.now();
// 监听页面加载完成
window.addEventListener('load', () => {
const loadTime = Date.now() - pageStartTime;
track('page_load', {
load_time: loadTime,
referrer: document.referrer,
resource_status: checkResources()
});
});
// 检查关键资源加载状态
function checkResources() {
return performance.getEntriesByType('resource').map(res => ({
name: res.name,
type: res.initiatorType,
duration: res.duration.toFixed(2)
}));
}
2. 用户交互阶段
// 点击事件埋点(支持事件委托)
document.body.addEventListener('click', (e) => {
const target = e.target.closest('[data-track]');
if(target) {
track('element_click', {
element_id: target.id,
track_type: target.dataset.track,
position: `${e.clientX},${e.clientY}`
});
}
});
// 滚动深度记录
let maxScroll = 0;
window.addEventListener('scroll', _.throttle(() => {
const currentScroll = window.scrollY / document.body.scrollHeight;
if(currentScroll > maxScroll) {
maxScroll = currentScroll;
track('scroll_depth', { depth: Math.round(maxScroll * 100) });
}
}, 1000));
3. 页面停留时长计算
let activeStart = Date.now();
let inactiveTime = 0;
// 用户活跃状态检测
document.addEventListener('mousemove', resetActiveTimer);
document.addEventListener('keydown', resetActiveTimer);
function resetActiveTimer() {
if(inactiveTime > 0) {
track('user_inactive', { duration: inactiveTime });
inactiveTime = 0;
}
activeStart = Date.now();
}
// 每10秒检测一次活跃状态
setInterval(() => {
if(Date.now() - activeStart > 15000) { // 15秒无操作视为不活跃
inactiveTime += 10000;
} else {
track('user_active', { duration: 10000 });
}
}, 10000);
二、特殊场景处理
1. 页面隐藏/显示
// 页面可见性变化监听
document.addEventListener('visibilitychange', () => {
if(document.hidden) {
track('page_hide', {
stay_time: Date.now() - pageStartTime,
scroll_depth: maxScroll
});
} else {
track('page_show');
}
});
2. 页面关闭前上报
// 确保页面关闭前数据上报
window.addEventListener('beforeunload', () => {
const totalStay = Date.now() - pageStartTime;
navigator.sendBeacon('/api/track', JSON.stringify({
event: 'page_close',
active_time: totalStay - inactiveTime,
scroll_depth: maxScroll
}));
});
三、数据上报优化方案
1. 批量上报机制
let eventQueue = [];
const MAX_QUEUE = 5;
const FLUSH_INTERVAL = 3000;
function addToQueue(event) {
eventQueue.push(event);
if(eventQueue.length >= MAX_QUEUE) {
flushQueue();
}
}
function flushQueue() {
if(eventQueue.length === 0) return;
const batchData = { batch: eventQueue };
navigator.sendBeacon('/api/batch', JSON.stringify(batchData));
eventQueue = [];
}
// 定时刷新队列
setInterval(flushQueue, FLUSH_INTERVAL);
2. 关键指标计算
// 计算FMP(首次有效绘制)
new PerformanceObserver((entryList) => {
const [entry] = entryList.getEntriesByName('first-contentful-paint');
track('fmp', { value: entry.startTime.toFixed(2) });
}).observe({type: 'paint', buffered: true});
// 计算LCP(最大内容绘制)
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1];
track('lcp', { value: lastEntry.startTime.toFixed(2) });
}).observe({type: 'largest-contentful-paint', buffered: true});
四、面试回答精简版
"我们实现全链路埋点主要分三个阶段:
-
加载阶段:
- 用
performance API采集DNS/TTFB等指标 - 监听
load事件记录完整加载时间 - 检查关键资源状态(如图片/脚本)
- 用
-
交互阶段:
- 事件委托监听全局点击(带
data-track属性) - 节流处理滚动事件计算最大深度
- 通过
mousemove/keydown检测活跃状态
- 事件委托监听全局点击(带
-
离开阶段:
visibilitychange处理页面切换beforeunload+sendBeacon确保关闭前上报- 计算总停留时长和有效活跃时间
3.那为什么大家都使用请求 GIF 图片的方式上报埋点数据呢?
-
防止跨域问题:前端监控的请求常常会遇到跨域问题,这可能会影响监控的准确性和可用性。然而,图片的src属性并不会跨域,因此使用GIF图片作为埋点可以正常发起请求,从而有效避免跨域问题。
-
防止阻塞页面加载:在创建资源节点后,通常只有当对象注入到浏览器的DOM树后,浏览器才会实际发送资源请求。但反复操作DOM会引发性能问题,且载入js/css资源会阻塞页面渲染,从而影响用户体验。与此不同,构造图片打点不需要插入DOM,只要在js中new出Image对象就能发起请求,这样就不会有阻塞问题。即使在没有js的浏览器环境中,也能通过img标签正常打点,这是其他类型的资源请求所做不到的。
-
体积小,节约流量:相比其他图片格式(如BMP和PNG),GIF图片具有更小的体积。例如,最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF只需要43个字节。因此,使用GIF作为埋点可以显著节约流量,提高数据传输效率。
-
浏览器支持性好:所有浏览器都支持Image对象,即使不支持XMLHttpRequest对象也一样。这意味着使用GIF进行埋点上报可以在各种浏览器环境中稳定运行。
-
记录错误的过程很少出错:某些情况下,如Ajax通信过程中页面跳转,请求可能会被取消。但使用图片进行埋点上报则不会遇到这个问题,特别是在记录离开页面打点行为的时候会很有用。
4. 前端SEO怎样做
前端 SEO(Search Engine Optimization) 是指通过优化网站的前端代码、结构和内容,使搜索引擎能够更好地理解和收录网站,从而提升在搜索结果中的自然排名。以下是前端ESO优化手段
- 设置TDK:TDK 是 Title(标题)、Description(描述)和 Keywords(关键词)的缩写,TDK 是一个网站 SEO 的核心。 Keywords:逗号分隔的关键词列表, description:网站描述,搜索引擎会把这个描述显示在搜索结果中, format-detection:格式检测,比如禁止识别电话,邮箱等, Robots:用来告诉搜索机器人哪些页面需要索引,哪些页面不需要索引, theme-color:网站主题色,
<title>2025 最新 Python 全栈开发教程 - 从入门到精通(含实战项目)</title>
<meta name="description" content="分享 Python 全栈开发完整学习路线,涵盖 Python 基础、Web 框架(Django/Flask)、数据分析、爬虫实战,附 10+ 企业级项目源码,帮助零基础小白 6 个月入行高薪岗位。">
<meta name="keywords" content="Python 教程,Python 全栈开发,Python 入门,Web 开发,Django,Flask,数据分析,Python 爬虫">
-
HTML语义化:h1、h2、h3、h4、h5 和 h6;强调标签:strong、em;图片标签: 使用时加上 alt 属性对图片进行描述,可以帮助蜘蛛快速理解图片的具体内容;段落标签: 页面中段落文字可使用 p 标签替代 列表标签:ul、ol、li,搜索引擎能够通过这些标签更好地理解信息的层次结构和关联性,从而更准确地评估网页的内容和价值;布局标签:header、nav、article、section、aside、footer,根据页面的区域模块划分选择对应布局标签
-
HTML lang:设置 HTML 标签的 lang 属性主要用于指定网页内容的语言。这个属性对于搜索引擎优化(SEO)是有益的,因为它帮助搜索引擎了解页面内容的语言,从而能够更准确地将页面呈现给搜索特定语言内容的用户。
-
服务端渲染:由于爬虫只能抓取到网页的静态源代码,而无法执行其中的 js 脚本,当网站采用 Vue 构建的单页面应用时,实际上是采用客户端渲染的方式,页面上的大部分 DOM 元素是在客户端通过JavaScript 动态生成的。客户端渲染的过程是需要时间的,爬虫不会等你渲染好,因此爬虫能够直接抓取和分析的内容会大幅减少。
-
Sitemap(站点地图) 是 SEO 的“基础设施”,它不直接提升排名,而是通过确保搜索引擎能够发现和高效抓取你的所有重要页面,为后续所有SEO工作奠定基础。是一种 xml 文件
-
robots文件 蜘蛛在访问一个网站时,会首先会检查该网站的根域下是否有一个叫做 robots.txt 的纯文本文件,这个文件用于指定 spider 在您网站上的抓取范围,掘金的robots文juejin.cn/robots.txt
-
内链和外链 内链决定搜索引擎如何“看待”你的网站内部,外链决定搜索引擎如何“看待”你的网站在整个互联网中的位置,a标签属性 rel="nofollow" ,nofollow:会告诉搜索引擎忽略这个链接,阻止搜索引擎对该页面进行追踪,从而避免权重分散,external:会告诉搜索引擎这是一个外部链接,非本站的链接
-
结构化数据 结构化数据是 HTML 中标记数据的方式,有助于搜索引擎理解网站内容并引导更高质量的搜索结果,搜索引擎可以知道您的页面包含哪些信息以及如何将其呈现给用户
-
使用HTTPS 使用HTTPS 谷歌曾发公告表示,使用 HTTPS 是搜索引擎排名的一项参考因素,HTTPS 站点相比于 HTTP 站点,能获得更好的排名。
-
网站重定向 301/302重定向是 SEO优化中一种重要的自动转向技术。301重定向是当搜索引擎向网站服务器发出访问请求时,服务返回的HTTP数据流中头信息(header)部分状态码的一种,表示本网址永久性转移到另一个地址。302重定向则表示暂时转移。301重定向与上一点所说的网址规范化有着类似的作用,与此同时,它还具有以下作用:集中域名权重,301 网址跳转其实是对域名权重进行转移,比如
www.juejin.cn重定向到juejin.cn,其实是把www.juejin.cn的权重转移到了juejin.cn,从而增加juejin.cn域名的权重;避免重复收录;网页PR(PageRank-网页级别)是用来评估一个网站页面相对于网站其他页面重要性的一个算法,301定向跳转有利于网站PR的传递;优化用户体验,网址规范化可以让用户更好地记住我们的网站,可以将域名统一重定向到某一个域名,增加网站的记忆度,获取更好的用户体验。 -
各搜索引擎提交站点收录 除了 robots.txt + sitemap.xml 方式增加网址被收录的可能性外,还可以在各搜索引擎站长平台手动提交网址,以缩短爬虫发现网站链接时间,加快爬虫抓取速度
-
预渲染prerender-spa-plugin 如果你只想改善部分页面的SEO,可以不采用 SSR 的解决方案,毕竟无论是next.js,还是nuxt.js,都是有一定学习成本的。那么你可以使用
prerender-spa-plugin等插件来实现预渲染页面,在构建时就针对特定的路有生成静态的 html 文件。 -
Open Graph 协议标签 Open Graph 协议标签通过 OG Tags (OG 标签)实现的,它属于 Meta 标签的一种,可以用来标识网页类型和元素,让分享到社交网络的内容可以被有效的抓取,还可以控制分享的网站卡片呈现我们想要显示的内容。
只要看到以 og: 为前缀的 Meta 标签就可以判断该网页支持 OG 标签了,如下:
<meta property="og:title" content="设置Open Graph 标签!">
<meta property="og:site_name" content="前端">
<meta property="og:type" content="article">
<meta property="og:description" content="Open Graph Protocol(开放图谱协议),简称 OG 协议或 OGP。">
设置og协议前后的网站分享卡片对比: