# 项目介绍
负责*****业务相关需求的开发,某业务的话就是从买家意向购买车,到车交到买家手上的流程;包括,相关材料的邮寄,车的物流,提档,落档,过户,接车(车况检查),交付顾问邀约客户最后的接待看车;完成交车;送车上门等流程;
主要包括对车商的工单,提档落档,车商账单,
对一线人员如交付顾问提供的工具应用,材料的邮寄接收,车况的记录等
整个交付流程的crm后台,gps管理。手续材料,车务工单,交付任务,交付顾问带看工单,交付引擎
# 项目难点
- 上传组件,兼容多个app,包括测试时候的web环境,私有云,公有云,七牛金山云
- 业务上各种表单提交比较多,入材料手续,快递材料,具体的车况信息等待;抽离封装
- 流程图
- 录音,视频
- h5
- h5录制状态
- 兜底时间30min, 客户端的同步(多次调用)
- 视频的分段,到达兜底时间的时候是否继续录制
- 多个接待看车任务的时候,只能有一个录制任务存在
- 退出webview页面的时候,状态会丢失;需要把录制状态,或者兜底时间存在native端(小的插曲,就是native对clear支持不是很好,经常会导致app崩溃,定位问题找了好久)
- 所以为了方便开发和调试,整理开发了一个用于native的交互的调试页面,上面包含了常用的交付api的调用;和打印出报错信息
- 外接设备的不稳定性,导致设备断开等问题,需要对这些特殊的边界情况进行处理,是重置状态,还是直接完成录制
- 业务过程中操作的不断升级,录制音频,智能眼镜。为了不对流程进行卡点,兼容多种的方式
- crm 播放,mp4和流
- h5
# 项目优化
# 首屏优化
- 图片懒加载
# 开发体验优化
# 切换test pre online环境
# 如何定位性能瓶颈
控制台观察
- 观察js css 图片的加载速率
- performance
- 看饼状图 看性能瓶颈
- 准备发起请求
# 网络
- DSN(DNS预解析DNS Prefetch)
- 建立TCP连接(减少http请求的量 资源整合) 请求资源 返回资源 渲染domm
# webpack
基础环境
- include exclude
- 压缩打包
- 分包处理
- 懒加载路由
- splitChunks
- dll 抽取公用的模块
测试环境
unused-files-webpack-plugin
没有使用到的文件webpack-bundle-analyzer
分析包的大小 上传到远端查看sw-precache-webpack-plugin
打包速度webpack-dev-server
开启热更新
# nginx
- 配置里 index.html no-store
- js css png图片等使用 expires max
- 开启了gzip
# 离线化平台
- 部署信息
- 构建时间
- 构建人
- 构建文件
- 提交信息(commitID)分支
- 等等
# 项目监控
# 错误监控
JS错误捕获 (opens new window) 如何处理 JavaScript 的异步捕获? (opens new window) 常见的错误拦截 (opens new window)
window.onerror()
可以捕获同步或者异步运行时的错误,但是不能捕获语法错误; 也不能捕获资源加载的和接口异常的错误window.onerror(message, source, lineno, colno, error){ // message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event。 // source:发生错误的脚本URL(字符串) // lineno:发生错误的行号(数字) // colno:发生错误的列号(数字) // error:Error对象(对象) }
1
2
3
4
5
6
7- 如果想通过onerror函数收集不同域的js错误,我们需要做两件事:
- 相关的js文件上加上
Access-Control-Allow-Origin:*
的response header - 引用相关的js文件时加上crossorigin属性
unhandledrejection
继承自PromiseRejectionEvent
(opens new window),而PromiseRejectionEvent
(opens new window) 又继承自Event
(opens new window)。因此unhandledrejection
含有PromiseRejectionEvent
和Event
的属性和方法。- 当
Promise
(opens new window) 被 reject 且没有 reject 处理器的时候,会触发unhandledrejection
事件;这可能发生在window
(opens new window) 下,但也可能发生在Worker
(opens new window) 中。 这对于调试回退错误处理非常有用。
window.addEventListener("unhandledrejection", event => { console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`); });
1
2
3- unhandledrejection 只能捕获未显式处理的Promise异常
// 能触发 unhandledrejection ,因为未显式处理 Promise.reject('test').then() // 能触发 unhandledrejection ,因为未显式处理 Promise.reject('test').then(console.log) // 不能触发 unhandledrejection ,因为已处理 Promise.reject('test').then(console.log, console.log) // 不能触发 unhandledrejection ,因为没处理,直接抛出异常 Promise.reject('test')
1
2
3
4
5
6
7
8
9
10
11在 ES6 的 Promise 中,对于直接 reject 的异常未处理任务,不会进行错误提示:
const rejected = Promise.reject('err') // 直接报异常
1除非手动去处理一下:
const rejected = Promise.reject('err') rejected.catch(function (err) { console.log('catch', err) })
1
2
3
4
5- 当
try catch 只能捕获同步运行的错误,对语法和异步错误无法捕获
try { } catch { }
1
2
3
4
5接口请求的错误
- ajax
window.addEventListener('error', (e)=>{ console.log('addEventListener') console.log(e) }
1
2
3
4- fetch
window.addEventListener("unhandledrejection", function(e) { console.log('unhandledrejection') console.log(e) }, true);
1
2
3
4资源加载错误
window.addEventListener('error',(e)=> {})
iframe
var frames = window.frames; for (var i = 0; i < frames.length; i++) { frames[i].addEventListener('error', (e)=>{ console.log('addEventListener') console.log(e) }, true); }
1
2
3
4
5
6
7
. vue
sentry
对window.onerror和unhandlerejection的重写
跨浏览的兼容
window.onerror
- 只能捕获运行时错误
- 无法捕获语法错误
- 无法捕获资源加载错误(可使用window.addEventListener('error') 资源加载错误的节点活冒泡到window对象, 并且没法知道错误码)
# 埋点监控
阅读tracker源码
fetch ajax image图片上报 Navigator.sendBeacon()
# 性能监控
利用window.performance
来统计
# 敏感数据反爬
- css font
- 自己生成wotff
- 不同的unicode码对应数字
- 规则给到后端
- 后端对敏感数字返回相对应的unicode
- 前端通过自定义字体解析unicode展示响应的内容
# native-sdk
# h5和nativa通信
native在webview的全局
window
对象下面挂载了一个全局对象webviewJsBridge
webviewJsBridge
中有callHandler
和registter
方法h5调用native方法使用
window.webviewJsBridge.callHandler(apiName, callback)
1三种 js 调用 native 方法
- 拦截 Url Schema(假请求)
- 拦截 prompt alert confirm
- 注入 JS 上下文
native调用js
window.webviewJsBridge.registerHandler(apiName, callback)
1
# h5调用native
# native调用h5
# 参考资料
JSBridge 实现原理解析 (opens new window)
# 其他
# 抓包
# CDN
# spa页面 和 多页面对比
优点
- 切换的流畅度比较高
- 前后端分离,利于维护
- 减轻服务端压力
- 跳转动画有优化的空间
缺点
- seo不友好
- 一次要加载所有资源(可使用路由懒加载)
- 需要额外的管理路由(不能使用浏览器天然的前进后退)
- 这里需要去复习弄清楚 vue-router的原理
mpa
- 优点
- seo友好
- 首屏加载快
- 缺点
- 每次都需要去加载公告的资源(js, css, html,图片等)
- 页面跳转没有跳转动画
- 项目维护起来比较麻烦,重复的代码较大
# 解决方案
- 可使用ssr方案解决
# 项目介绍思路
# 思路和角度
业务相关
- 业务数据 tracker
- 业务流程(业务理解)
- 项目规划
技术相关
采集业务数据 tracker
性能监控
错误捕获 sentry
错误监控
- 页面错误
- 接口错误
代码质量
- sonar
- ts
- elsint stylelint husky
- 代码review
项目优化
- 首页优化
- 打包优化 html, js, css
- 分包
- 压缩
- tree shaking
- 抽离css
- webpack 其他优化问题
- 开发(体验)阶段优化
- cdn
- ssr
- 离线化
代码重构
字体反爬
native-sdk原理
# 项目亮点难点
- typescript
- sentry
- 友商过户评分 用到zrender 绘制了评分雷达图
- 重构
重构原因
- 项目各个列表 和 订单耦合度过大,添加新功能时改动过大
- 向公司的ske-cli脚手架靠拢,统一开发模式
- 引入typescript 代码的约束
重构的方案
- 创建新到的代码到src目录下,把久的项目放入old的目录下, 分别打包,把最后的dist包合并到一起
- 新老页面跳转 replace的方式跳转(封装了跳转方法,过渡阶段需要使用)
- 抽离公用组件
- 抽离页面代码 解耦
- 新老页面跳转 白屏问题
- 加入单元测试
重构顺序和重构时机
- 制定了重构的计划: 列出步骤和方向
- 把不流量小的页面首先抽离重构
- 二级页面,或者三级页面重构
- 抽离出一些公用的模块 button,头部,没网,没数据的组件,toast,dialog组件,或者native方法封装
- 最后重构一级页面,和改动频繁的页面,建立新的测试分支
- 每个模块的完事后的自测 开会codereview代码,测试环境的部署,稳定一段时间后,合分支准上线
遇到的问题
- 新老页面白屏问题(nginx配置缓存html)
- eslint冲突
- 新旧页面的baseUrl不同,协调客户端进行强更
- 兜底方案 nginx重定向,把旧的页面重定向到新的页面
- 健康检查 url改变,导致健康检查过不去,更新部署包
重定向 保证没有强制更新的用户 能够用正常跳转到页面
location ~ /partners_app/brokerage-agency.html { rewrite /partners_app/brokerage-agency.html(.*) /partners_app/$1 permanent; }
1
2
3