docs: Hybrid、领域驱动设计与 BFF、项目复盘

This commit is contained in:
LiuBinPeng
2021-08-24 09:51:28 +08:00
parent d819a88652
commit 55a89dc4a5
13 changed files with 237 additions and 66 deletions

BIN
.DS_Store vendored

Binary file not shown.

14
.vscode/1.96.md vendored Normal file
View File

@@ -0,0 +1,14 @@
# 一个提高 App 运算性能的想法
想到了一个场景大概是客户端、H5 网页端、Electron 桌面端都需要大量的 DB 操作、JS 执行大量数据的计算。这些常见的共同特点是计算特别大,耗费 CPU 算力。想到了 H5 网页、Electron 等都会使用 GPU 去渲染,那么这些复杂计算,能不能也利用 GPU 强大的算力去实现。
考虑到某些机器上可能没有 GPU 能力感觉可以做一个封装内部根据当前内存情况、GPU 情况,派发任务到 GPU 还是普通的 JS 单线程的异步任务上,没有 GPU 则直接使用异步任务
由于我们的场景会存在很多的离线能力,所以这个时候很多的计算都需要本地完成,后续的 PC 收银参考客户端,做离线 DB 的操作、营销 JS 计算、很多运算都可以放 GPU
比如,营销 JS 的计算代码目前就在客户端iOS、Android存在H5 JS 也存在,那么 PC 收银桌面端,也可以享用这个能力。
利用 CommonJS 规范Webpack 打包出一个 distJS多个端服用。内部判断当前设备支不支持 GPU支持则在 GPU 上运算,不支持则在 CPU 上异步计算。
前端开源了 [gpu.js](https://github.com/gpujs/gpu.js],同样客户端也可以实现类似的设计。做到一个高性能计算的效果。

View File

@@ -1,15 +1,15 @@
# 一个 Hybrid SDK 设计与实现 # 一个 Hybrid SDK 设计与实现
随着移动浪潮的兴起,各种 App 层出不穷,极速发展的业务拓展提升了团队对开发效率的要求,这个时候纯粹使用 Native 开发技术成本难免会更高一点。而 H5 的低成本、高效率、跨平台等特性马上被利用起来了,形成一种新的开发模式: Hybrid App > 随着移动浪潮的兴起,各种 App 层出不穷,极速发展的业务拓展提升了团队对开发效率的要求,这个时候纯粹使用 Native 开发技术成本难免会更高一点。而 H5 的低成本、高效率、跨平台等特性马上被利用起来了,形成一种新的开发模式: Hybrid App
>
作为一种混合开发的模式Hybrid App 底层依赖于 Native 提供的容器Webview上层使用各种前端技术完成业务开发现在三足鼎立的 Vue、React、Angular底层透明化、上层多样化。这种场景非常有利于前端介入非常适合业务的快速迭代。于是 Hybrid 火了。 > 作为一种混合开发的模式Hybrid App 底层依赖于 Native 提供的容器Webview上层使用各种前端技术完成业务开发现在三足鼎立的 Vue、React、Angular底层透明化、上层多样化。这种场景非常有利于前端介入非常适合业务的快速迭代。于是 Hybrid 火了。
>
大道理谁都懂,但是按照我知道的情况,还是有非常多的人和公司在 Hybrid 这一块并没有做的很好,所以我将我的经验做一个总结,希望可以帮助广大开发者的技术选型有所帮助 > 大道理谁都懂,但是按照我知道的情况,还是有非常多的人和公司在 Hybrid 这一块并没有做的很好,所以我将我的经验做一个总结,希望可以帮助广大开发者的技术选型有所帮助
## Hybrid 的一个现状 ## 一、Hybrid 现状
可能早期都是 PC 端的网页开发随着移动互联网的发展iOS、Android 智能手机的普及,非常多的业务和场景都从 PC 端转移到移动端。开始有前端开发者为移动端开发网页。这样子早期资源打包到 Native App 中会造成应用包体积的增大。越来越多的业务开始用 H5 尝试,这样子难免会需要一个需要访问 Native 功能的地方,这样子可能早期就是懂点前端技术的 Native 开发者自己封装或者暴露 Native 能力给 JS 端,等业务较多的时候者样子很明显不现实,就需要专门的 Hybrid 团队做这个事情;量大了,就需要规矩,就需要规范。 可能早期都是 PC 端的网页开发随着移动互联网的发展iOS、Android 智能手机的普及,非常多的业务和场景都从 PC 端转移到移动端。开始有前端开发者为移动端开发网页。这样子早期资源打包到 Native App 中会造成应用包体积的增大。越来越多的业务开始用 H5 尝试,这样子难免会需要一个需要访问 Native 功能的地方,这样子可能早期就是懂点前端技术的 Native 开发者自己封装或者暴露 Native 能力给 JS 端,等业务较多的时候者样子很明显不现实,就需要专门的 Hybrid 团队做这个事情;量大了,就需要规矩,就需要规范。
@@ -27,12 +27,12 @@ Hybrid 在大量应用的时候就需要一定的规范,那么本文将讨论
## Native 与前端分工 ## 二、Native 与前端分工
在做 Hybird 架构设计之前我们需要分清 Native 与前端的界限。首先 Native 提供的是宿主环境,要合理利用 Native 提供的能力,要实现通用的 Hybrid 架构,站在大前端的视觉,我觉得需要考虑以下核心设计问题。 在做 Hybird 架构设计之前我们需要分清 Native 与前端的界限。首先 Native 提供的是宿主环境,要合理利用 Native 提供的能力,要实现通用的 Hybrid 架构,站在大前端的视觉,我觉得需要考虑以下核心设计问题。
### 交互设计 ### 1. 交互设计
Hybrid 架构设计的第一要考虑的问题就是如何设计前端与 Native 的交互,如果这块设计不好会对后续的开发、前端框架的维护造成深远影响。并且这种影响是不可逆、积重难返。所以前期需要前端与 Native 好好配合、提供通用的接口。比如 Hybrid 架构设计的第一要考虑的问题就是如何设计前端与 Native 的交互,如果这块设计不好会对后续的开发、前端框架的维护造成深远影响。并且这种影响是不可逆、积重难返。所以前期需要前端与 Native 好好配合、提供通用的接口。比如
@@ -43,14 +43,14 @@ Hybrid 架构设计的第一要考虑的问题就是如何设计前端与 Native
### 账号信息设计 ### 2. 账号信息设计
账号系统是重要且无法避免的Native 需要设计良好安全的身份验证机制,保证这块对业务开发者足够透明,打通账户体系 账号系统是重要且无法避免的Native 需要设计良好安全的身份验证机制,保证这块对业务开发者足够透明,打通账户体系
### Hybrid 开发调试 ### 3. Hybrid 开发调试
功能设计、编码完并不是真正结束Native 与前端需要商量出一套可开发调试的模型,不然很多业务开发的工作难以继续。 功能设计、编码完并不是真正结束Native 与前端需要商量出一套可开发调试的模型,不然很多业务开发的工作难以继续。
@@ -67,7 +67,7 @@ Android 调试技巧:
## Hybrid 交互设计 ## 三、Hybrid 交互设计
Hybrid 交互无非是 Native 调用 H5 页面JS 方法,或者 H5 页面通过 JS 调 Native 提供的接口。2者通信的桥梁是 Webview。 Hybrid 交互无非是 Native 调用 H5 页面JS 方法,或者 H5 页面通过 JS 调 Native 提供的接口。2者通信的桥梁是 Webview。
业界主流的通信方法1.桥接对象时机问题不太主张这种方式2.自定义 Url scheme 业界主流的通信方法1.桥接对象时机问题不太主张这种方式2.自定义 Url scheme
@@ -82,7 +82,7 @@ weixin:// 可以打开微信。
### JS to Native ### 1. JS to Native
Native 在每个版本都会提供一些 Api前端会有一个对应的框架团队对其封装释放业务接口。举例 Native 在每个版本都会提供一些 Api前端会有一个对应的框架团队对其封装释放业务接口。举例
@@ -116,7 +116,7 @@ SDGHybridReady(function(arg){
### Api 交互 ### 2. Api 交互
调用 Native Api 接口的方式和使用传统的 Ajax 调用服务器,或者 Native 的网络请求提供的接口相似 调用 Native Api 接口的方式和使用传统的 Ajax 调用服务器,或者 Native 的网络请求提供的接口相似
![Api交互](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/HybridApi.jpg) ![Api交互](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/HybridApi.jpg)
@@ -128,7 +128,7 @@ SDGHybridReady(function(arg){
### 格式约定 ### 3. 格式约定
交互的第一步是设计数据格式。这里分为请求数据格式与响应数据格式,参考 Ajax 模型: 交互的第一步是设计数据格式。这里分为请求数据格式与响应数据格式,参考 Ajax 模型:
``` ```
@@ -295,13 +295,13 @@ typedef NS_ENUM(NSInteger){
## 常用交互 Api ## 四、常用交互 Api
良好的交互设计是第一步,在真实业务开发中有一些 Api 一定会由应用场景。 良好的交互设计是第一步,在真实业务开发中有一些 Api 一定会由应用场景。
### 跳转 ### 1. 跳转
跳转是 Hybrid 必用的 Api 之一,对前端来说有以下情况: 跳转是 Hybrid 必用的 Api 之一,对前端来说有以下情况:
- 页面内跳转,与 Hybrid 无关 - 页面内跳转,与 Hybrid 无关
- H5 跳转 Native 界面 - H5 跳转 Native 界面
@@ -361,8 +361,7 @@ back 与 forward 一致,可能会有 animatetype 参数决定页面切换的
### 2. Header 组件的设计
## Header 组件的设计
Native 每次改动都比较“慢”,所以类似 Header 就很需要。 Native 每次改动都比较“慢”,所以类似 Header 就很需要。
1. 主流容器都是这么做的,比如微信、手机百度、携程 1. 主流容器都是这么做的,比如微信、手机百度、携程
@@ -590,8 +589,7 @@ define([], function () {
### 3. 请求类
## 请求类
虽然 get 类请求可以用 jsonp 方式绕过跨域问题,但是 post 请求是一个拦路虎。为了安全性问题服务器会设置 cors 仅仅针对几个域名Hybrid 内嵌静态资源可能是通过本地 file 的方式读取,所以 cors 就行不通了。另外一个问题是防止爬虫获取数据,由于 Native 针对网络做了安全性设置(鉴权、防抓包等),所以 H5 的网络请求由 Native 完成。可能有些人说 H5 的网络请求让 Native 走就安全了吗?我可以继续爬取你的 Dom 节点啊。这个是针对反爬虫的手段一。想知道更多的反爬虫策略可以看看我这篇文章 [Web反爬虫方案](https://github.com/FantasticLBP/Anti-WebSpider) 虽然 get 类请求可以用 jsonp 方式绕过跨域问题,但是 post 请求是一个拦路虎。为了安全性问题服务器会设置 cors 仅仅针对几个域名Hybrid 内嵌静态资源可能是通过本地 file 的方式读取,所以 cors 就行不通了。另外一个问题是防止爬虫获取数据,由于 Native 针对网络做了安全性设置(鉴权、防抓包等),所以 H5 的网络请求由 Native 完成。可能有些人说 H5 的网络请求让 Native 走就安全了吗?我可以继续爬取你的 Dom 节点啊。这个是针对反爬虫的手段一。想知道更多的反爬虫策略可以看看我这篇文章 [Web反爬虫方案](https://github.com/FantasticLBP/Anti-WebSpider)
@@ -628,7 +626,7 @@ requestHybrid({
## 常用 NativeUI 组件 ## 五、常用 NativeUI 组件
一般情况 Native 通常会提供常用的 UI比如 加载层loading、消息框toast 一般情况 Native 通常会提供常用的 UI比如 加载层loading、消息框toast
@@ -663,7 +661,7 @@ Native UI与前端UI不容易打通所以在真实业务开发过程中
## 账号系统的设计 ## 六、账号系统的设计
Webview 中跑的网页,账号登录与否由是否携带密钥 cookie 决定(不能保证密钥的有效性)。因为 Native 不关注业务实现,所以每次载入都有可能是登录成功跳转回来的结果,所以每次载入都需要关注密钥 cookie 变化,以做到登录态数据的一致性。 Webview 中跑的网页,账号登录与否由是否携带密钥 cookie 决定(不能保证密钥的有效性)。因为 Native 不关注业务实现,所以每次载入都有可能是登录成功跳转回来的结果,所以每次载入都需要关注密钥 cookie 变化,以做到登录态数据的一致性。
@@ -704,7 +702,7 @@ HybridUI.logout = function () {
## Hybrid 资源管理 ## 七、Hybrid 资源管理
Hybrid 的资源需要 `增量更新` 需要拆分方便,所以一个 Hybrid 资源结构类似于下面的样子 Hybrid 的资源需要 `增量更新` 需要拆分方便,所以一个 Hybrid 资源结构类似于下面的样子
@@ -738,7 +736,7 @@ WebApp
## 增量更新 ## 八、增量更新
每次业务开发完毕后都需要在打包分发平台进行部署上线,之后会生成一个版本号。 每次业务开发完毕后都需要在打包分发平台进行部署上线,之后会生成一个版本号。
@@ -760,9 +758,9 @@ WebApp
## 一些零散的解决方案 ## 九、体验优化
1. 静态直出 ### 1. 静态直出
“直出”这个概念对前端同学来说,并不陌生。为了优化首屏体验,大部分主流的页面都会在服务器端拉取首屏数据后通过 NodeJs 进行渲染,然后生成一个包含了首屏数据的 Html 文件,这样子展示首屏的时候,就可以解决内容转菊花的问题了。 “直出”这个概念对前端同学来说,并不陌生。为了优化首屏体验,大部分主流的页面都会在服务器端拉取首屏数据后通过 NodeJs 进行渲染,然后生成一个包含了首屏数据的 Html 文件,这样子展示首屏的时候,就可以解决内容转菊花的问题了。
当然这种页面“直出”的方式也会带来一个问题,服务器需要拉取首屏数据,意味着服务端处理耗时增加。 当然这种页面“直出”的方式也会带来一个问题,服务器需要拉取首屏数据,意味着服务端处理耗时增加。
@@ -771,18 +769,16 @@ WebApp
我们可以做一个类似的事情,自动同步最新的代码和数据,然后生成新的含首屏 Html并发布到 CDN 上面去 我们可以做一个类似的事情,自动同步最新的代码和数据,然后生成新的含首屏 Html并发布到 CDN 上面去
### 2. 离线预推
2. 离线预推
页面发布到 CDN 上面去后,那么 WebView 需要发起网络请求去拉取。当用户在弱网络或者网速比较差的环境下,这个加载时间会很长。于是我们通过离线预推的方式,把页面的资源提前拉取到本地,当用户加载资源的时候,相当于从本地加载,即使没有网络,也能展示首屏页面。这个也就是大家熟悉的离线包。 页面发布到 CDN 上面去后,那么 WebView 需要发起网络请求去拉取。当用户在弱网络或者网速比较差的环境下,这个加载时间会很长。于是我们通过离线预推的方式,把页面的资源提前拉取到本地,当用户加载资源的时候,相当于从本地加载,即使没有网络,也能展示首屏页面。这个也就是大家熟悉的离线包。
手 Q 使用 7Z 生成离线包, 同时离线包服务器将新的离线包跟业务对应的历史离线包进行 BsDiff 做二进制差分生成增量包进一步降低下载离线包时的带宽成本下载所消耗的流量从一个完整的离线包253KB降低为一个增量包3KB 手 Q 使用 7Z 生成离线包, 同时离线包服务器将新的离线包跟业务对应的历史离线包进行 BsDiff 做二进制差分生成增量包进一步降低下载离线包时的带宽成本下载所消耗的流量从一个完整的离线包253KB降低为一个增量包3KB
[手Q开源Hybrid框架VasSonic介绍极致的页面加载速度优化](https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247488218&idx=1&sn=21afe07eb642162111ee210e4a040db2&chksm=f951a799ce262e8f6c1f5bb85e84c2db49ae4ca0acb6df40d9c172fc0baaba58937cf9f0afe4&scene=27#wechat_redirect)
https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247488218&idx=1&sn=21afe07eb642162111ee210e4a040db2&chksm=f951a799ce262e8f6c1f5bb85e84c2db49ae4ca0acb6df40d9c172fc0baaba58937cf9f0afe4&scene=27#wechat_redirect
3. 拦截加载 ### 3. 拦截加载
事实上,在高度定制的 wap 页面场景下,我们对于 webview 中可能出现的页面类型会进行严格控制。可以通过内容的控制,避免 wap 页中出现外部页面的跳转,也可以通过 webview 的对应代理方法,禁掉我们不希望出现的跳转类型,或者同时使用,双重保护来确保当前 webview 容器中只会出现我们定制过的内容。既然 wap 页的类型是有限的,自然想到,同类型页面大都由前端采用模板生成,页面所使用的 html、css、js 的资源很可能是同一份,或者是有限的几份,把它们直接随客户端打包在本地也就变得可行。加载对应的 url 时,直接 load 本地的资源。 事实上,在高度定制的 wap 页面场景下,我们对于 webview 中可能出现的页面类型会进行严格控制。可以通过内容的控制,避免 wap 页中出现外部页面的跳转,也可以通过 webview 的对应代理方法,禁掉我们不希望出现的跳转类型,或者同时使用,双重保护来确保当前 webview 容器中只会出现我们定制过的内容。既然 wap 页的类型是有限的,自然想到,同类型页面大都由前端采用模板生成,页面所使用的 html、css、js 的资源很可能是同一份,或者是有限的几份,把它们直接随客户端打包在本地也就变得可行。加载对应的 url 时,直接 load 本地的资源。
对于 webview 中的网络请求,其实也可以交由客户端接管,比如在你所采用的 Hybrid 框架中为前端注册一个发起网络请求的接口。wap 页中的所有网络请求都通过这个接口来发送。这样客户端可以做的事情就非常多了举个例子NSURLProtocol 无法拦截 WKWebview 发起的网络请求,采用 Hybrid 方式交由客户端来发送,便可以实现对应的拦截。 对于 webview 中的网络请求,其实也可以交由客户端接管,比如在你所采用的 Hybrid 框架中为前端注册一个发起网络请求的接口。wap 页中的所有网络请求都通过这个接口来发送。这样客户端可以做的事情就非常多了举个例子NSURLProtocol 无法拦截 WKWebview 发起的网络请求,采用 Hybrid 方式交由客户端来发送,便可以实现对应的拦截。
@@ -792,25 +788,46 @@ https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247488218&idx=1&sn=21afe0
NSURLProtocol能够让你去重新定义苹果的URL加载系统(URL Loading System)的行为URL Loading System里有许多类用于处理URL请求比如NSURLNSURLRequestNSURLConnection和NSURLSession等。当URL Loading System使用NSURLRequest去获取资源的时候它会创建一个NSURLProtocol子类的实例你不应该直接实例化一个NSURLProtocolNSURLProtocol看起来像是一个协议但其实这是一个类而且必须使用该类的子类并且需要被注册。                                        NSURLProtocol能够让你去重新定义苹果的URL加载系统(URL Loading System)的行为URL Loading System里有许多类用于处理URL请求比如NSURLNSURLRequestNSURLConnection和NSURLSession等。当URL Loading System使用NSURLRequest去获取资源的时候它会创建一个NSURLProtocol子类的实例你不应该直接实例化一个NSURLProtocolNSURLProtocol看起来像是一个协议但其实这是一个类而且必须使用该类的子类并且需要被注册。                                       
4. WKWebView 网络请求拦截
方法一Native 侧):
原生 WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此在 WKWebView 上直接使用 NSURLProtocol 是无法拦截请求的。
但是由于 mPaas 的离线包机制强依赖网络拦截所以基于此mPaaS 利用了 WKWebview 的隐藏 api去注册拦截网络请求去满足离线包的业务场景需求参考代码如下 ### 4. WKWebView 网络请求拦截
```Objective-c - 方法一Native 侧):
[WKBrowsingContextController registerSchemeForCustomProtocol:@"https"] 原生 WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此在 WKWebView 上直接使用 NSURLProtocol 是无法拦截请求的。
```
但是因为出于性能的原因WKWebView 的网络请求在给主进程传递数据的时候会把请求的 body 去掉,导致拦截后请求的 body 参数丢失。
在离线包场景,由于页面的资源不需要 body 数据,所以离线包可以正常使用不受影响。但是在 H5 页面内的其他 post 请求会丢失 data 参数。 但是由于 mPaas 的离线包机制强依赖网络拦截所以基于此mPaaS 利用了 WKWebview 的隐藏 api去注册拦截网络请求去满足离线包的业务场景需求参考代码如下
为了解决 post 参数丢失的问题mPaas 通过在 js 注入代码hook 了 js 上下文里的 XMLHTTPRequest 对象解决。 ```Objective-c
[WKBrowsingContextController registerSchemeForCustomProtocol:@"https"]
```
通过在 JS 层把方法内容组装好,然后通过 WKWebView 的 messageHandler 机制把内容传到主进程,把对应 HTTPBody 然后存起来,随后通知 JS 端继续这个请求,网络请求到主进程后,在将 post 请求对应的 HttpBody 添加上,这样就完成了一次 post 请求的处理。整体流程可以参考如下: 但是因为出于性能的原因WKWebView 的网络请求在给主进程传递数据的时候会把请求的 body 去掉,导致拦截后请求的 body 参数丢失。
![ajax-时序图](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/2021-05-28-WKWebViewRequestHook)
通过上面的机制,既满足了离线包的资源拦截诉求,也解决了 post 请求 body 丢失的问题。但是在一些场景还是存在一些问题,需要开发者进行适配。
方法二JS 侧): 在离线包场景,由于页面的资源不需要 body 数据,所以离线包可以正常使用不受影响。但是在 H5 页面内的其他 post 请求会丢失 data 参数。
通过 AJAX 请求的 hook 方式,将网络请求的信息代理到客户端本地。能拿到 WKWebView 里面的 post 请求信息,剩下的就不是问题啦。
AJAX hook 的实现可以看这个 [Repo](https://github.com/wendux/Ajax-hook). 为了解决 post 参数丢失的问题mPaas 通过在 js 注入代码hook 了 js 上下文里的 XMLHTTPRequest 对象解决。
通过在 JS 层把方法内容组装好,然后通过 WKWebView 的 messageHandler 机制把内容传到主进程,把对应 HTTPBody 然后存起来,随后通知 JS 端继续这个请求,网络请求到主进程后,在将 post 请求对应的 HttpBody 添加上,这样就完成了一次 post 请求的处理。整体流程可以参考如下:
![ajax-时序图](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/2021-05-28-WKWebViewRequestHook)
通过上面的机制,既满足了离线包的资源拦截诉求,也解决了 post 请求 body 丢失的问题。但是在一些场景还是存在一些问题,需要开发者进行适配。
- 方法二JS 侧):
通过 AJAX 请求的 hook 方式,将网络请求的信息代理到客户端本地。能拿到 WKWebView 里面的 post 请求信息,剩下的就不是问题啦。
AJAX hook 的实现可以看这个 [Repo](https://github.com/wendux/Ajax-hook).
## 十、离线包
传统的 H5 技术容易受到网络环境影响,因而降低 H5 页面的性能。通过使用离线包,可以解决该问题,同时保留 H5 的优点。
**离线包** 是将包括 HTML、JavaScript、CSS 等页面内静态资源打包到一个压缩包内。预先下载该离线包到本地,然后通过客户端打开,直接从本地加载离线包,从而最大程度地摆脱网络环境对 H5 页面的影响。
使用 H5 离线包可以给您带来以下优势:
- **提升用户体验**:通过离线包的方式把页面内静态资源嵌入到应用中并发布,当用户第一次开启应用的时候,就无需依赖网络环境下载该资源,而是马上开始使用该应用。
- **实现动态更新**:在推出新版本或是紧急发布的时候,您可以把修改的资源放入离线包,通过更新配置让应用自动下载更新。因此,您无需通过应用商店审核,就能让用户及早接收更新。
![离线包下载流程](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/Hybrid-OfflinePackageDownload.png)
![离线包加载流程](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/Hybrid-OfflinePackageLoad.png)

View File

@@ -4,11 +4,11 @@
App 的性能问题是影响用户体验的重要因素之一。性能问题主要包含Crash、网络请求错误或者超时、UI 响应速度慢、主线程卡顿、CPU 和内存使用率高、耗电量大等等。大多数的问题原因在于开发者错误地使用了线程锁、系统函数、编程规范问题、数据结构等等。解决问题的关键在于尽早的发现和定位问题。 App 的性能问题是影响用户体验的重要因素之一。性能问题主要包含Crash、网络请求错误或者超时、UI 响应速度慢、主线程卡顿、CPU 和内存使用率高、耗电量大等等。大多数的问题原因在于开发者错误地使用了线程锁、系统函数、编程规范问题、数据结构等等。解决问题的关键在于尽早的发现和定位问题。
xf 本篇文章着重总结了 APM 的原因以及如何收集数据。APM 数据收集后结合数据上报机制,按照一定策略上传数据到服务端。服务端消费这些信息并产出报告。请结合[姊妹篇](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.80.md) 总结了如何打造一款灵活可配置、功能强大的数据上报组件。 本篇文章着重总结了 APM 的原因以及如何收集数据。APM 数据收集后结合数据上报机制,按照一定策略上传数据到服务端。服务端消费这些信息并产出报告。请结合[姊妹篇](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.80.md) 总结了如何打造一款灵活可配置、功能强大的数据上报组件。
## 一、卡顿监控 ## 一、卡顿监控
卡顿问题,就是在主线程上无法响应用户交互的问题。影响着用户的直接体验,所以针对 App 的卡顿监控是 APM 里面重要的一环。 卡顿问题,程度较低就是掉帧,比如用户在滑动某个列表的时候会有不流畅的体验,但是应用程序还是可以相应的。严重些就是 ANR就是短时间在主线程上无法响应用户交互的问题。影响着用户的直接体验,所以针对 App 的卡顿监控是 APM 里面重要的一环。
FPSframe per second每秒钟的帧刷新次数iPhone 手机以 60 为最佳iPad 某些型号是 120也是作为卡顿监控的一项参考参数为什么说是参考参数因为它不准确。先说说怎么获取到 FPS。CADisplayLink 是一个系统定时器,会以帧刷新频率一样的速率来刷新视图。 `[CADisplayLink displayLinkWithTarget:self selector:@selector(###:)]`。至于为什么不准我们来看看下面的示例代码 FPSframe per second每秒钟的帧刷新次数iPhone 手机以 60 为最佳iPad 某些型号是 120也是作为卡顿监控的一项参考参数为什么说是参考参数因为它不准确。先说说怎么获取到 FPS。CADisplayLink 是一个系统定时器,会以帧刷新频率一样的速率来刷新视图。 `[CADisplayLink displayLinkWithTarget:self selector:@selector(###:)]`。至于为什么不准我们来看看下面的示例代码
@@ -2697,6 +2697,7 @@ CFNetwork 使用 CFReadStreamRef 来传递数据,使用回调函数的形式
} }
@end @end
```
@implementation NetworkDelegateProxy @implementation NetworkDelegateProxy
@@ -2713,31 +2714,31 @@ CFNetwork 使用 CFReadStreamRef 来传递数据,使用回调函数的形式
}); });
return _sharedInstance; return _sharedInstance;
} }
#pragma mark - public Method #pragma mark - public Method
+ (instancetype)setProxyForObject:(id)originalTarget withNewDelegate:(id)newDelegate + (instancetype)setProxyForObject:(id)originalTarget withNewDelegate:(id)newDelegate
{ {
NetworkDelegateProxy *instance = [NetworkDelegateProxy sharedInstance]; NetworkDelegateProxy *instance = [NetworkDelegateProxy sharedInstance];
instance->_originalTarget = originalTarget; instance->_originalTarget = originalTarget;
instance->_NewDelegate = newDelegate; instance->_NewDelegate = newDelegate;
return instance; return instance;
} }
- (void)forwardInvocation:(NSInvocation *)invocation - (void)forwardInvocation:(NSInvocation *)invocation
{ {
if ([_originalTarget respondsToSelector:invocation.selector]) { if ([_originalTarget respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:_originalTarget]; [invocation invokeWithTarget:_originalTarget];
[((NSURLSessionAndConnectionImplementor *)_NewDelegate) invoke:invocation]; [((NSURLSessionAndConnectionImplementor *)_NewDelegate) invoke:invocation];
} }
} }
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel - (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{ {
return [_originalTarget methodSignatureForSelector:sel]; return [_originalTarget methodSignatureForSelector:sel];
} }
@end @end
``` ```
@@ -5538,12 +5539,13 @@ parseJSError(line, column);
}); });
return _sharedManager; return _sharedManager;
} }
```
#pragma mark - public Method #pragma mark - public Method
- (void)startMonitor - (void)startMonitor
{ {
APMMLog(@"crash monitor started"); APMMLog(@"crash monitor started");
#ifdef DEBUG #ifdef DEBUG
@@ -5569,14 +5571,14 @@ parseJSError(line, column);
} }
- (void)installKSCrash - (void)installKSCrash
{ {
[[APMCrashInstallation sharedInstance] install]; [[APMCrashInstallation sharedInstance] install];
[[APMCrashInstallation sharedInstance] sendAllReportsWithCompletion:nil]; [[APMCrashInstallation sharedInstance] sendAllReportsWithCompletion:nil];
[APMCrashInstallation sharedInstance].onCrash = onCrash; [APMCrashInstallation sharedInstance].onCrash = onCrash;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_isCanAddCrashCount = NO; _isCanAddCrashCount = NO;
}); });
} }
``` ```
在 `installKSCrash` 方法中调用了 `[[APMCrashInstallation sharedInstance] sendAllReportsWithCompletion: nil]`,内部实现如下 在 `installKSCrash` 方法中调用了 `[[APMCrashInstallation sharedInstance] sendAllReportsWithCompletion: nil]`,内部实现如下

View File

@@ -603,3 +603,8 @@ $ npm install
$ node app.js $ node app.js
``` ```
## 提问
1. 反爬技术方案本身会不会对盲人等特殊群体的视障模式有影响?
https://medium.com/frochu/回歸初心-一探web-accessibility-baaa4d22f4a7
alt

11
Chapter3 - Server/3.10.md Normal file
View File

@@ -0,0 +1,11 @@
# 领域驱动设计与中后台、BFF的关系
1. DDD 将业务拆分为多个领域,比如订单、商品...。每个域的服务是单独可以运行、维护的。比如订单域有10台机器1台负载均衡9台真正做事情的机器。有任务过来调度器进行任务派发。
2. 客户端或者大前端都存在模块的概念。比如商品、订单,也是以 Pods 的形式去维护,每个领域的代码都单独可以运行、维护。模块之间不存在耦合关系,通过接口的形式去能力发现、能力调用。比如订单域通过某个接口访问某个商品详情的能力
3. DDD 的最佳实践是不是微服务。比如订单服务所在的工程,可以单独运行、维护。
4. 基于微服务,肯定需要一个 BFF 层扮演业务编排、字段转换。比如订单域的模型有100个字段。但是订单的接口为客户端服务客户端通过接口找20182020年的、待付款的订单BFF 层就去扮演数据组装、字段筛选的作用。
- https://tech.meituan.com/2017/12/22/ddd-in-practice.html
- https://juejin.cn/post/6997250621627858957#heading-3

View File

@@ -11,4 +11,5 @@
* [6、YAML](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.6.md) * [6、YAML](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.6.md)
* [7、Node单元测试](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.7.md) * [7、Node单元测试](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.7.md)
* [8、数据安全反爬虫之「防重放」策略](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.8.md) * [8、数据安全反爬虫之「防重放」策略](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.8.md)
* [9、爬取疫情数据并用 Markdown 预览](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.9.md) * [9、爬取疫情数据并用 Markdown 预览](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.9.md)
* [10、领域驱动设计与中后台、BFF的关系](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.10.md)

View File

@@ -0,0 +1,22 @@
# 项目管理案例分析
## 库存扣减项目
**背景**原定于17号上车上车过程中代码合并 master 分支。iOS 和网关代码均无冲突。Android 发现订单列表接口被替换,和同学 A chechk 核实原因后是因为 App 订单管理能力补齐替换了订单列表接口而库存扣减项目测试过程中一直走的是老接口发现问题后与测试沟通对合并后的代码再进行一轮回归延期至18号上车
**影响**额外的近2天时间导致其他项目进度滞后
**复盘总结:**
因为过程中没有及时合并主分支代码,导致 QA 测试完成后,项目上车之际,合并 master 后发现很多主要逻辑以及接口被修改了。这时候不可能直接上车的,必须为新增的每一行代码回归,不然质量没法确保。
这种情况,可与举个例子,比如你的 Native 工程或者前端工程的 Package.json 中版本没有锁死,在打包平台出的 SDKA 1.0.0 版本,测试完成后在出市场包或者 Web 部署 CDN 的时候,因为没锁死,恰好 SDKA 刚好有 1.0.1 可用,这样子造成的三方库引发的“新功能”或者某某不兼容,导致发生了线上问题。
**改进点:**
- 开发过程中需要及时合并主分支,给 QA 的交付物保持最新
- 后端接口改变,尽量做到向前兼容,避免影响线上服务
- 后端接口做不到向前兼容,则新开接口,避免影响线上业务。接口改动尽量同步给相关使用方,正确评估影响面
这个问题本质上也就是多版本并行问题。属于很常见的项目问题没有版本根本上解决但是采取👆上面3个措施相信情况会好很多。

View File

@@ -21,4 +21,5 @@
* [17、一套开发规范](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.18.md) * [17、一套开发规范](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.18.md)
* [18、云服务器靠谱推荐](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.19.md) * [18、云服务器靠谱推荐](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.19.md)
* [19、规范化团队 git 提交信息](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.20.md) * [19、规范化团队 git 提交信息](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.20.md)
* [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md) * [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md)
* [21、项目管理案例分析](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.22.md)

View File

@@ -1,7 +1,106 @@
# 第九部分 # 第九部分
第九部分主要记录布偶猫饮食、吃饭、健康、玩具、洗护、社会化等方面的经验和心得,还有一部分晒猫的原因。 > 主要记录猫饮食、吃饭、健康、玩具、洗护、社会化等方面的经验和心得,还有一部分晒猫的原因。
## 基础信息
坐标 :杭州
猫咪养了4只吞金兽-布偶猫,每个猫咪都有 CFA 证书,有些猫咪小时候拿过比赛大奖,赛级布偶。
- Bella 是一只波斯系蓝双布偶,妹妹
- Simba 是一只海双布偶,弟弟
- PiPi 是一只海双布偶,弟弟
- 碎星 是一只海双布偶,妹妹
环境:家里一周一次紫外臭氧灯消毒
洗护:克里斯汀森赛级洗护
饮食:生骨肉、羊奶
## 猫咪
<h5 style="text-align:center;">PiPi</h5>
<img src="https://i.niupic.com/images/2021/06/30/9mn6.gif" width="500">
<h5 style="text-align:center;">碎星:</h5>
<img src="https://i.niupic.com/images/2021/06/30/9mn7.JPG" width="500">
<h5 style="text-align:center;">Bella</h5>
<img src="https://i.niupic.com/images/2021/06/30/9mnZ.JPG" width="500">
<img src="https://i.niupic.com/images/2021/06/30/9mqS.JPG" width="500">
<img src="https://i.niupic.com/images/2021/06/30/9mqU.JPG" width="500">
<img src="https://i.niupic.com/images/2021/06/30/9mqT.JPG" width="500">
<h5 style="text-align:center;">Simba</h5>
<img src="https://i.niupic.com/images/2021/06/30/9mo0.JPG" width="500">
<h5 style="text-align:center;">小猫:</h5>
写入2021-04周前拍的
<img src="https://i.niupic.com/images/2021/06/30/9mn8.JPG" width="500">
<img src="https://i.niupic.com/images/2021/06/30/9mn9.JPG" width="500">
<img src="https://i.niupic.com/images/2021/06/30/9mqV.JPG" width="500">
<img src="https://i.niupic.com/images/2021/06/30/9mqW.JPG" width="500">
<img src="https://i.niupic.com/images/2021/06/30/9mqX.JPG" width="500">
写入2021-07-11现在
<img src="https://i.niupic.com/images/2021/07/11/9ny4.gif" width="500">
<img src="https://i.niupic.com/images/2021/07/11/9nxU.JPG" width="500">
<img src="https://i.niupic.com/images/2021/07/11/9nxX.JPG" width="500">
<img src="https://i.niupic.com/images/2021/07/11/9nxY.JPG" width="500">
更多:
其他更多的猫咪的美照,可以访问[这里](./assets)
## 交流
如果你是一名铲屎官,想进群晒猫或者是交流猫咪养护经验
如果没有养猫,想云吸猫
如果打算养一只可爱、高颜值的布偶猫
可以联系我微信704568245
## 目录
* [1、"CFA 证书" 不再那么神秘](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.1.md) * [1、"CFA 证书" 不再那么神秘](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.1.md)
* [2、猫咪饮食](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.2.md) * [2、猫咪饮食](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.2.md)

View File

@@ -98,6 +98,7 @@
* [93、flutter 新功能引导](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.93.md) * [93、flutter 新功能引导](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.93.md)
* [94、APM-Wake Up](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.94.md) * [94、APM-Wake Up](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.94.md)
* [95、从 flutter 和前端角度出发,聊聊单线程模型下如何保证 UI 流畅性](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.95.md) * [95、从 flutter 和前端角度出发,聊聊单线程模型下如何保证 UI 流畅性](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.95.md)
* [96、一个提高 App 运算性能的想法](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.96.md)
* [Chapter2 - Web FrontEnd](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/chapter2.md) * [Chapter2 - Web FrontEnd](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/chapter2.md)
* [1、-last-child与-last-of-type你只是会用有研究过区别吗](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/2.1.md) * [1、-last-child与-last-of-type你只是会用有研究过区别吗](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/2.1.md)
@@ -152,6 +153,7 @@
* [7、Node单元测试](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.7.md) * [7、Node单元测试](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.7.md)
* [8、数据安全反爬虫之「防重放」策略](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.8.md) * [8、数据安全反爬虫之「防重放」策略](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.8.md)
* [9、爬取疫情数据并用 Markdown 预览](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.9.md) * [9、爬取疫情数据并用 Markdown 预览](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.9.md)
* [10、领域驱动设计与中后台、BFF的关系](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter3%20-%20Server/3.10.md)
@@ -190,6 +192,7 @@
* [18、云服务器靠谱推荐](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.19.md) * [18、云服务器靠谱推荐](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.19.md)
* [19、规范化团队 git 提交信息](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.20.md) * [19、规范化团队 git 提交信息](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.20.md)
* [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md) * [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md)
* [21、项目管理案例分析](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.22.md)
* [Chapter8 - Finance](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter8%20-%20Finance/chapter8.md) * [Chapter8 - Finance](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter8%20-%20Finance/chapter8.md)
@@ -216,7 +219,3 @@
* [5、猫粮对你猫咪的牙齿做了什么](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.5.md) * [5、猫粮对你猫咪的牙齿做了什么](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.5.md)
* [6、新猫到家的注意事项](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.6.md) * [6、新猫到家的注意事项](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.6.md)
* [7、猫咪健康](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.7.md) * [7、猫咪健康](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter9%20-%20Ragdoll/9.7.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB