开发方式
并行(优先):
串行:
服务端比前端提前一个版本,交付的内容包括API+文档。
分页加载、滚动加载
第几页
+每页几项
发起请求,服务端(提前)返回总量
给前端做判断一共有几页。滚动加载,用游标
作为判断下一批请求内容的依据:
分页的游标管理
普通情况,游标由前端(或客户端)管理
前端用游标id
发起请求,服务端返回新的游标id
给前端作为下一次请求。
若快速变动的数据(如:推荐信息)、或要根据用户操作而快速改变的数据(如已推送给某用户的不再推送给ta、用户标记不喜欢的相关类型不再推送给ta),则游标由服务端管理。
服务端用Redis等内存管理方式记录用户的ID,前端只需要每次请求相同的无参数接口就可从服务端返回分页数据。
若用分页加载的服务端接口实现滚动加载
- 则可能出现请求到重复数据或略过数据的情况。(游标的,若没有管理好数据流,则也会出现重复数据或略过数据情况)。
前端(或客户端)也可以模拟游标管理方式:暴露一个加载更多的无参数接口,在接口内部实现类似服务端的游标管理。
e.g.
```javascript let arr = [] // 数据 const size = 10 // 每页数量 const total = 111 // 总量 function loadMore () { if (arr.length < total) { console.log('页数:', Math.ceil(arr.length / size) + 1) // 页码:Math.ceil(arr.length / size) + 1;每页数量:size // 用发起异步请求获取数据,数据插入arr arr = arr.concat(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) } else { // 已经加载所有内容 } return arr } loadMore() // 加载更多直接调用,不用管理状态 ```
服务端文档要求
API文档确定的字段,就算为空,也必须按照文档要求返回
或[]
或{}
,不允许返回内容丢失字段。
扁平化的需要
不同接口、但类别相同的数据,都按照相同的结构约定数据格式(如:normalizr)。
前端可以进行数据扁平化,把不同接口返回的数据都根据类别按照hash的方式存放在各自类别的store,并再保存一份数组记录展示顺序(把数据库的hash保存的方式移植到前端也用hash保存)
e.g. 一个接口返回的数据包括articles、users数据,进行扁平化 ```javascript // articles的store(内聚) const articles = {} // articles的store articles.all = {} // 存放articles的元数据(元数据:完整的单项数据,用唯一的id进行hash索引) articles.hot = { // 存放articles的hot的展示顺序 sequence: [], // 元数据的id顺序 hasMore: true // 是否继续请求 } articles.new = { // 存放articles的new的展示顺序 sequence: [], // 元数据的id顺序 hasMore: true // 是否继续请求 } articles.flattenData = (data) => { // 扁平化数据:把单项数据全部保存在同一个地方 articles.all[data.id] = Object.assign({}, articles.all[data.id], data) } articles.changeSequence = (data) => { // 写入某业务的展示顺序 const list = articles[data.category] if (data.refresh) { list.sequence = data.sequence } else { list.sequence = list.sequence.concat(data.sequence) } } // 相同省略:users的store // 请求articles.hot的数据。返回的数据包含多种类别数据(articles、users) function handleData (arr, category) { // 处理数据 articles.changeSequence({ // 写入hot的展示顺序 category: category, refresh: false, sequence: arr.map((data) => { articles.flattenData(data.articles) // 把元数据合并至articles // users.flattenData(data.users) // 把元数据合并至users return data.articles.id // 返回articles的id用于保存顺序 }) }) console.log(category, JSON.parse(JSON.stringify(articles))) // 打印 } // 针对articles.hot的第一次请求 const data1 = [ { articles: { id: '1', data: 'articles第一个数据' }, users: { id: 'a', data: 'users第I个数据' } }, { articles: { id: '20', data: 'articles第二个数据' }, users: { id: 'b', data: 'users第II个数据' } }, { articles: { id: '300', data: 'articles第三个数据' }, users: { id: 'c', data: 'users第III个数据' } }, { articles: { id: '4000', data: 'articles第四个数据' }, users: { id: 'd', data: 'users第IV个数据' } } ] handleData(data1, 'hot') // 针对articles.hot的第二次请求 const data2 = [ { articles: { id: '5000', data: 'articles第五个数据' }, users: { id: 'E', data: 'users第V个数据' } }, { articles: { id: '600', data: 'articles第六个数据' }, users: { id: 'F', data: 'users第VI个数据' } }, { articles: { id: '70', data: 'articles第七个数据' }, users: { id: 'G', data: 'users第VII个数据' } }, { articles: { id: '8', data: 'articles第八个数据' }, users: { id: 'H', data: 'users第VIII个数据' } }, { articles: { id: '1', data: 'articles第一的覆盖内容' }, users: { id: 'a', data: 'users第I个的覆盖内容' } } ] handleData(data2, 'hot') // 针对articles.new的第一次请求 const data3 = [ { articles: { id: '5000', data: 'articles第五的覆盖内容' }, users: { id: 'E', data: 'users第V的覆盖内容' } }, { articles: { id: '8', data: 'articles第八的覆盖内容' }, users: { id: 'H', data: 'users第VIII的覆盖内容' } }, { articles: { id: '300', data: 'articles第三的覆盖内容' }, users: { id: 'c', data: 'users第III的覆盖内容' } }, { articles: { id: '20', data: 'articles第二的覆盖内容' }, users: { id: 'b', data: 'users第II的覆盖内容' } } ] handleData(data3, 'new') console.log('接口获得的数据都进行扁平化处理;在对应类别的store按照id存取数据,再保存一份存放顺序的数组') ```
接口请求失败,不能帮用户静默再次请求
提示用户(针对必要展示的信息)
让用户认知网络错误(或其他错误)并且给用户操作重新加载的功能(如:跳转到网络出错或404页面)。
需要请求数据的应用都需要设计404和网络错误等容错页面。
静默失败(针对增量加载的信息,如:滚动加载)
不提示用户失败,当用户再次触发时再次请求(减少用户挫败感)。
不能把服务端返回的(错误)信息直接发送给用户看见,需要转换成用户能看得懂的语言。
也不能把前端错误信息发送给用户。若必须发送给用户错误信息,也需要转换成用户能看得懂的语言。
e.g. ```javascript try { asd } catch (e) { alert(e) // 不可以把不经过翻译的错误信息发送给用户 } ```
可能在物理机、vps、容器(如:Docker、Kubernetes)中查看日志。
HTTP Server日志
nginx
nginx -t
获得配置路径;access_log
、error_log
);服务端应用程序日志
pm2
pm2 list
获得进程列表;pm2 info 「进程id或name」
获取进程信息;pm2 log 「进程id或name」
查看进程日志。Docker
docker ps
获取容器信息;docker logs -f --tail 「数字」 「容器ID」
查看Docker日志。Kubernetes
kubectl get namespace
查看所有命名空间;kubectl get pod -n 「namespace名字」 | grep 「筛选关键字」
查看所有某命名空间的pod;kubectl logs 「pod名字」 -n 「namespace名字」 -c 「container名字」 -f --tail 「数字」
查看pod日志。顺着请求链路排查:域名 -(DNS -> 服务器地址) -> HTTP Server(如:nginx、Apache Tomcat) -> 服务端应用程序(逻辑、IO)。
出错维度、链路:
判断是否发送到指定服务器地址
如:域名解析、网络问题、本机的hosts/DNS(缓存)。
已发送到指定服务器地址,查看出错位置的日志
HTTP server错误
如:nginx日志。
服务端应用程序错误
如:Node.js、Golang跑的服务端程序日志。
运维相关问题
如:磁盘满了、硬盘坏了、数据库出问题、其他各种问题的信息。
前端查错
根据HTTP Response查询
Body
注意那些nginx标记的错误,说明在nginx层被阻止。
e.g.
```html413 Request Entity Too Large 413 Request Entity Too Large
nginx/1.19.0 ```
服务端查错
若有错误信息,则方便直接定位出错点;若没有错误信息,则需要顺着链路查询。
进入服务器或容器内:
curl
容器监听端口,确认是否能从宿主机进入容器。curl
依赖的API,确认是否宿主机内能正常访问API。在宿主机或容器内通用查询: