工程搭建
- 类型约束 TypeScript
- 开发框架 React Vue
- CSS处理
- 构建工具
版本控制
- 提交规范
- 分支管理
- git hook 自动化
代码质量
代码质量指标
- 主观指标:Code Review
- 统一团队CR规范
- 评审到位,避免形式主义
- 可扩展性;可读性
- 客观指标
- 圈复杂度(Cyclomatic complexity)
- 千行代码Bug率
- 代码重复率
代码管理
- 代码规范
- 代码格式化
- 工程目录结构
- 类型约束
接口管理
- 接口mock
代码质量平台 SonarQube
组件建设
- 组件规范
- 目录结构
- 样式主题
- 国际化
- 组件测试
- 文档管理
- 发布规范
页面监控
性能监控
window.performance
- 最大内容绘制(Largest Contentful Paint,LCP)测量加载性能。为了提供良好的用户体验,LCP应在页面首次开始加载后的2.5s内发生
- 首次输入延迟(First Input Delay,FID)测量交互性。为了提供良好的用户体验,页面的FID应为100ms或更短
- 累积布局偏移(Cumulative Layout Shift,CLS)测量视觉稳定性。为了提供良好的用户体验,页面的CLS值应保持在0.1或更少
异常监控
错误采集
window.onerror
window.onerror的缺陷在于它没有办法捕获资源加载的错误
window.addEventListener(‘error’)
- window.addEventListener(‘error’)具备window.onerror的多数功能,但它不能通过返回true的形式来阻止默认事件处理函数的执行,可以通过调用e.preventDefault()方法来阻止默认行为
- 在错误捕获的时机上,它比onerror更早被触发
- 虽然资源加载的事件不会在window上“冒泡”,但以在事件捕获阶段采集错误,将addEventListener的第三个参数设置为true,即可在事件捕获阶段采集到错误,从而实现对资源加载错误的监听
其他
- JavaScript代码中还存在异步错误,这种错误没办法被上述三种方法捕获,需要使用addEventListener监听unhandledrejection
- 部分代码中的JavaScript异常会使用try catch函数进行处理,然后通过console.error打印。由于错误被catch捕获了,所以不会被全局监听的方法捕获。针对这种情况,开发人员可以通过劫持console.error的方式来采集打印的错误信息
错误处理
为了方便后续错误的监控、排查定位等工作,应该为每种错误类型都定义一个固定的数据结构
行、列、stack等信息
错误排查
Source Map
因为Source Map文件可以逆向生成处理前的源代码,所以Source Map文件不允许被暴露在生产环境中。为了便于排查问题,开发人员一般会将Source Map文件上传到内网服务器中,只有内部员工才能访问,这样就不会导致代码泄露
白屏监控
- 导致白屏的原因大致可以分为JavaScript执行错误和请求未返回两类
- 开发人员需要重点关注的是JavaScript执行错误导致的白屏问题。请求未返回导致的白屏可以通过性能监控和资源保障进行防御,也可以通过实时监控进行处理
异常白屏监控
使用addEventListener监听error和unhandledrejection事件捕获JavaScript错误。在捕获JavaScript错误成功后,需要对页面进行检测,判断当前界面的可视区域是否存在内容
实时白屏监控
实时白屏监控在大多数情况下用于采集页面流程中耗时占比过长的环节,然后进行性能优化
…
卡顿监控
FPS 画面每秒传输的帧数
浏览器FPS Failed
模拟FPS
使用setTimeout将1s切成60份,每经过16ms便创建一个定时任务,统计1s内定时任务的执行次数,即可得出页面的FPS数值
该方案问题:setTimeout并不能有效结合设备的刷新率和动画绘制的特性,setTimeout在页面标签隐藏状态下依然会执行,会带来额外的性能开销
可以借助requestAnimationFrame进行优化
将calcFPS函数中的setTimeout替换成requestAnimationFrame
FPS无法反映超过1s的页面卡顿,所以为了监控超过1s的长任务,可以增加逻辑判断相邻任务的时间差
用户行为监控
分析
产品/设计/运营/研发
事件监听
鼠标事件、键盘事件、AJAX请求和URL变更
录制回放
rrweb:dom树状态
请求监控
XMLHttpRequest拦截器
首先定义一个类,取名为XhrInterceptor,用于实现复写XMLHttpRequest的逻辑,其构造函数会接受一个apiCallback函数作为回调参数,用于回调捕获到的请求信息
可以直接看skywalking-client-js代码
Fetch拦截器
fetch polyfill
请求过滤
请求类型
- 高并发请求监控,可以帮助开发人员发现页面中存在的性能和质量问题,提前进行优化和修复
- 重复请求监控
- HTTP状态码异常
- 被取消的请求
- 高延迟请求
资源保障
资源加载异常
- 图片资源加载失败:图片内容区域出现带有红色小叉的大白块
- CSS加载失败:页面字体大小异常、布局错乱等样式异常
- JavaScript资源加载失败:页面白屏或者交互无响应
- 页面长时间白屏:资源加载耗时过长
MutationEvent:有性能问题
MutationObserver
Content-Security-Policy 内容安全策略
获取指令:控制某些可能被加载的确切的资源类型的位置
文档指令:管理文档属性或者Worker环境应用的策略
导航指令:管理用户能打开的链接或者表单可提交的链接
报告指令:控制CSP违规的报告过程
防iframe劫持
- JS判断:window.self !== window.top
- X-Frame-Options:DENY/SAMEORIGIN/ALLOW-FROM
HTTPS
稳定性保障
资源加载监控
performance.getEntries
资源重试
可以使用window.addEventListener来监听error事件,监控资源加载失败的情况。以CSS资源为例,开发人员可以把监控到的加载失败的CSS资源记录下来,通过重新插入link标签来重新加载资源
失败的JavaScript资源可能存在依赖顺序关系。因为HTML加载完成后动态插入的script标签不会阻塞,也不会按照标签插入的先后顺序执行JavaScript代码,所以不能直接插入所有script标签进行资源重试,应该在前一个资源加载完成后再插入下一个标签
为保障资源的稳定性,需要在资源重试的基础上添加一套CDN资源容灾方案,其原理就是在资源重试后更换新的CDN资源域名
除了解决不同资源重试方案不一致的问题,开发人员还应该为资源重试次数添加上限,否则当出现一直加载失败的资源时,浏览器会一直进行资源重试,极大损耗性能
由于Application Cache一直无法有效解决离线资源精细化控制问题,service worker应运而生。相比于Application Cache,service worker提供独立的后台线程,是一种特殊的worker上下文访问环境。它不仅能够有效做到离线资源精细化控制,还能拦截所有向服务端发起的请求,进行自定义操作
工程质量优化
定位问题、发现问题、解决问题是问题处理的常见步骤,工程化的意义便是将解决同一类问题的手段进行沉淀,将解决方案规范化、系统化,避免同一类问题重复出现。当同一类问题再次发生时,开发人员也能基于现有的方案进行快速处理
构建优化
构建过程分析
优化构建速度,那么第一步就是分析出每个构建流程花费的时间,从而找到当前构建速度的性能瓶颈
分析工具 speed-measure-webpack-plugin(SMP)
避免无意义解析
resolve.extensions
…
体积优化
- 构建结果分析
webpack-bundle-analyzer - 提取公共代码
optimization.splitChunks - Tree Shaking
用于移除死代码,即上下文中没有使用到的代码 - 代码忽略
IgnorePlugin - 资源压缩
GZIP - 作用域提升(Scope Hoisting)
性能优化
- 性能分析:Performance,Lighthouse
- CDN加速
- 按需加载
- 预编译优化:Prepack,Closure Compiler
- 服务端渲染