第2章:JQuery高级技巧
[TOC]
JQuery高级技巧
1、jq –arg传递的变量select()没有硬编码值就不能工作吗?
1 | Product_Personnel_Array=$(cat ${Product_Personnel_FILE_PATH} | ${JQ_EXEC} -r '.[]') # -r 去除字符串引号 |
1 | 0、基础用法(验证通过✅) |
1 | jq '.test_group_1[] | select(.name == test_1-1).checksum = 1' $FILE_PATH |
第5章:Mac快速操作
[TOC]
Mac快速操作
参考文章:
苹果官网–在 Mac 上将“自动操作”工作流程添加到“脚本编辑器”中的“脚本”菜单
一、创建
二、使用
三、删除
如果要删除已创建的,则进入 /Users/qian/Library/Services
删除对应文件即可。
End
页面跳转相关
优雅的自定义返回
一、背景
从页面进入弹窗(全局WebView)。弹窗和页面不在同一路由栈上,从而使得当页面返回的时候,需要自己判断返回到哪。
二、场景示例
1 | 页面APage --> 页面BPage --> 弹窗CView --> 页面DPage |
三、自定义返回
1、判断的核心
- 判断的核心:在进入的时候标记下从哪来,点击返回的时候即回哪去。
2、必选处理的事及其初始主要代码
必选处理的事:
①自定义返回按钮事件。
②传递进入时候的标记。
处理事项的主要代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 进入页面 APage.swift
func goPage() {
let currentRoutePath = "xxx" // Replace with the actual value of currentRoutePath
let viewController = BPage(fromRoutePath: currentRoutePath)
self.navigationController?.pushViewController(viewController, animated: true)
}
// 返回页面 BPage.swift
func goBack() {
if fromRoutePath == "ShareWebView" {
ShareWebViewRouteUtil.hide() // 全局webView视图
} else {
if isPresent {
self.dismiss(animated: true, completion: nil)
} else {
self.navigationController?.popViewController(animated: true)
}
}
}
3、所需值fromRoutePath
的传入与使用
进入时候的标记赋值方案,主要有以下两种。
3.1、方案1(土)
进入页面的代码处标记。缺点:每个需要的地方的都需要写相应的代码。
3.2、方案2(精)
页面进行路由跳转的时候,将前一页的页面命名路由名传递给下一页。
1、添加页面跳转监听:进行对原本参数的添加,达到新值的传入目的。
2、跳转的目的页面将所有参数接收,待后续内部独立处理。
3、页面内部独立分解所有参数,并在返回时候充分使用。
1 | // 页面跳转的监听 |
End
页面跳转相关
页面跳转相关(路由)
一、随时获取上一个页面的路径
背景:埋点为避免去从所有埋点里查找进入此页面前的上一个进入页面,希望直接知道此页面的上页面路径。
路由规范
字段 | 描述 | 示例 | |
---|---|---|---|
1 | sourceFrom | 从哪个环境过来 (如h5跳转到app) |
appType/h5Type |
2 | |||
1、无整屏显示的弹窗,即从任何弹窗进入记为从页面进入
二、优雅的自定义返回
背景:从页面进入弹窗(全局WebView)。弹窗和页面不在同一路由栈上,从而使得当页面返回的时候,需要自己判断返回到哪。
详见:优雅的自定义返回
三、一个不用重加载的WebView
背景:一个基于WebView的Cocos2d游戏,因为启动加载引擎会有3-5秒的加载时长。为了更好的游戏体验,希望启动后重新进入不用重新加载。
四、游戏与app跳转交互优化调研
常见:web游戏,极小,游戏页独立存在
特殊:web游戏可以调到app页时候
关键策略:
1、调研游戏内容多网页共享方案
2、建立游戏测试页,提供后续方案验证基础
3、开发方案demo,进行游戏内容多网页实验
4、如果方案有效,后续进行含业务的完整方案梳理;则如果无效则进行其他方案调研
调研过程:
1、完成Flutter游戏测试页的建立,并通过尝试共享网页视图或控制器两种方式,进行游戏内容在多页面的共享测试。得出共享时候视图树会重绘,即网页会重新加载,从而无法达到整个跳转过程在同一路由栈下的正常跳转交互。
2、完成实现原生游戏测试页,并在原生上通过共享单例视图方式,进行游戏内容在多页面的共享测试。仍会出现在共享时候视图重绘操作。
3、后续预想方案
方案①游戏页面与其他网页一样常规的正常重新开辟。
优点:能使得整个跳转过程在同一路由栈下的正常跳转交互,享有系统的各种进出交互。
优化点:页面开辟时候,游戏加载慢。
方案②游戏使用唯一独立窗口,形如支付宝蚂蚁森林。需要从游戏内去app的其他页面时候,使用先缩小游戏窗口,到一个悬浮按钮,再弹出目标页面。
存在现象:从目标页再进游戏的时候需要从悬浮按钮进入。
路由解析详见 路由规范(含点击属性规范))
使用WebUrl解析的方法
1 | // 解析方法如下: |
解析方法详细内容如下:
1 | import 'dart:convert'; |
构造方法详细内容如下:
1 | import 'dart:convert'; |
End
路由规范(含点击属性规范)
[toc]
路由规范
一、路由地址规范
1、路由/点击操作类型
序号 | 操作类型标识 | 操作类型描述 | 场景举例 | ||
---|---|---|---|---|---|
1 | openPage | 页面跳转 | 发布内容按钮 | ||
2 | popupWindow | 弹出弹窗 | 查看物流按钮 | ||
3 | popupToast | 弹出Toast | 催一催按钮 | ||
路由操作,常与按钮点击结合在一起,所以我们顺便约定【点击按钮的属性规范】。
2、点击按钮的属性与类规范
2.1、背景/场景示例
1 | // 页面跳转 |
2.2、点击属性类规范
1 | // 基类 |
路由解析
三、WebUrl 的解析与构造
使用WebUrl解析的方法
1 | // 解析方法如下: |
解析方法详细内容如下:
1 | import 'dart:convert'; |
构造方法详细内容如下:
1 | import 'dart:convert'; |
End
MTC Client Cache Best Practice
Server/Client orcharstrated Cache Machenism
Invalidate cache and naming is two most difficult things
两种场景:浏览器请求与应用请求
浏览器控制
如 <img src="...."/>
参考 HTTP Caching 文档
- 彻底不缓存
1 | Cache-Control: no-store |
- 缓存,但每次都去服务器检查是否有新数据
1 | Cache-Control: no-cache |
- 缓存,但在一定时间内不去服务器检查是否有新数据
1 | Cache-Control: max-age=3600 |
使用 If-Modified-Since 来判断
服务器端下发:
1 | HTTP/1.1 200 OK |
浏览器端后续请求:
1 | GET /index.html HTTP/1.1 |
- 使用 If-None-Match 来判断 服务器端下发:
1 | HTTP/1.1 200 OK |
浏览器端后续请求:
1 | GET /index.html HTTP/1.1 |
MTC 实践中,使用的是 ETag/If-None-Match, 数据有变化时,只在 Redis 中生成一个新的 ETag,后续处理判断 ETag 是否一致
例一:MTC Avatar
- 服务器端:
1 | return ( |
客户端浏览器网络效果:
结果:
- 浏览器在 10 分钟内,不再重新请求,而是使用缓存中的图片
- 请求时,只有在图片变化时,服务器返回新图片,否则返回 304 头,浏览器使用已有缓存
例二:MTC Template Cover
- 服务器端:
1 | return ( |
- 客户端浏览器网络效果:
- 结果:
- 浏览器每次都请求新数据
- 请求时,只有在图片变化时,服务器返回新图片,否则返回 304 头,浏览器使用已有换
应用控制
- 如使用 Fetch, Axios, HttpClient 等请求数据,包括移动 APP 客户端从服务器获取数据
- 需要自行设计实现
- 两个问题:
- 是否有更合理的方案?
- APP 中如何实现?
业务层刷新机制思考:
- 须考虑用户本地操作后,需要刷新
- 当前用户操作,及时刷新
- 其它用户,可定时刷新
- 须考虑页面切换,使用本地缓存数据,无须访问服务器重新拉取
- 须考虑自动刷新
- 设置合理的间隔
- 设置总刷新次数,防止用户不活动
- 或检测用户鼠标动作,上次鼠标移动在多长时间内,断定为用户活跃,继续自动刷新,否则停止。
- 提供手动刷新按钮(APP 移动端为下拉)
- 或检测用户鼠标动作,上次鼠标移动在多长时间内,断定为用户活跃,继续自动刷新,否则停止。
技术层刷新机制:
- 服务端对每种业务对象设置 ETag Key, 每次返回数据,带上一个 ETag
- 这个 ETag,用于表示某种数据是否刷新,仅在有变化时,修改这个 ETag
- 客户端对每次请求的结果,进行缓存,同时记录 ETag;
- 客户端每次请求,通过 If-None-Match 带入 ETag
- 服务端相应请求时,判断服务端带来的 ETag 是否与服务端的 ETag 一致
- 如一致,返回 304
- 如不一致,返回 200,及最新数据
- 客户端根据 Header 是否是 304,决定使用已缓存数据,还是更新缓存为服务端返回数据。
具体实现方式:
服务端 CORS 设置中,允许 ETag
1 | routes: { |
客户端请求,将请求内容组装为 cacheKey,并 MD5 位 cacheID
1 | const cacheKey = { method, path, body, token }; |
请求可以带 cache 控制 cacheFlag,用户控制:
- preDelete: 在请求前,删除已有缓存
- useIfExists: 如存在缓存,直接返回缓存,不执行服务器请求
- 否则,在请求头中添加 If-None-Match
1 | if (cacheFlag === CACHE_FLAG.preDelete) { |
- 服务器端检查到 If-None-Match 后,判断业务数据是否有改变,如没有改变,直接返回 304,不用做数据库查询等操作
1 | let ifNoneMatch = req.headers["if-none-match"]; |
以上代码中,服务器端只需要按照业务逻辑,刷新相应的的 ETag Redis 记录,数据有变化,就修改 Redis 中相应 eTag key 的值。 如没有修改,则直接返回 304
- 客户端请求后接收到 304, 则直接使用缓存中的数据
1 | if (response.status === 304) { |
- 客户端从服务器端接收到的不是 304,则说明,有新数据,则写入客户端缓存(包括收到的 etag, 用于下次请求时,通过 If-None-Match 带到服务器上对比)
1 | if (responseETag) { |
缓存数据的记录方法
- 浏览器自身: 不用管它
- Cookie: 数据量非常小
- localStorage: 跨浏览器 , 跟配置相关的数据建议用 LS
- Session,但是使用 K-V
- indexedDB: 数据库,SQL 差不多,比 LS 复杂, 也是跨浏览器
- Sveltekit 的 Session 浏览器关掉,就消失。只要在 session 内部,没有问题
收益
- 用户体验提升, 快速展示,不存在刷新
- 直接使用缓存,不请求
- 请求,但得到的是 304, 数据量非常小。直接是缓存
- 网络和服务器的压力
- 直接使用缓存,不请求时, 请求变少
- 返回 304 时, 304 的判断是在 Redis 里做,后续的所有数据库操作都没有必要。大部分的请求,是可以直接 304 的。
- 整体平台的相应能力提升
- 整体的费用得以下降
Future
等待异步
有时候执行某个动作,需要等到另一个事件结束,可以用Completer类。
常见的有:
执行某个动作,需要弹窗等待用户选择某条件后再执行后续代码,
//事例
var c = Completer<bool>();
Dialogs.tipsCard(title, tip,
failMsg: '',
actions: [i18n.ok],
callback: (index) => c.complete(true),
dismissCallBack: () {
if (!c.isCompleted)
c.complete(false);
},
);
return c.future;
其他:
Completer
Completer允许你做某个异步事情的时候,调用c.complete(value)方法来传入最后要返回的值。最后通过c.future的返回值来得到结果,(注意:宣告完成的complete和completeError方法只能调用一次,不然会报错)。看下面的例子更容易理解。
1 | test() async { |
怎么将一个Callback回调转化成Future同步方法(Callback to Future),可以配套async / await去使用呢?
使用场景:dio请求使用call,但外部基本还是习惯使用future方式,为了减少接口的改动,使用callback转future方式。
参考文章:Flutter&Dart Callback转Future
compute
在一个页面中做耗时比较大的运算时,就算用了async / await异步处理, ui页面的动画还是会卡顿,因为还是在这个UI线程中做运算,异步只是说我可以先运行其他的,等我这边有结果再返回,但是,记住,我们的计算仍旧是在这个UI线程,仍会阻塞UI的刷新,异步只是在同一个线程的并发操作。
要解决这个卡顿问题,可以把运算移到另一个线程中,在dart中,这里不是称呼线程,是Isolate,直译叫做隔离,是因为隔离不共享数据,每个隔离中的变量都是不同的,不能相互共享。
Isolate的操作比较复杂,dart中封装了一层简单的实现
1 | /// package:flutter/foundation.dart |
使用方法
1 | import 'package:flutter/foundation.dart'; |
简单来讲就是运行var res = await compute( callback , val )函数。callback的传入参数是val, return的数据就是callback的res;
使用场景
- 方法执行在几毫秒或十几毫秒左右的,应使用Future
- 如果一个任务需要几百毫秒或之上的,则建议compute(只有一次返回)或Isolate(用于订阅或有多次返回的)
Flutter/Dart :如何在app启动前等待异步任务?
我在一个dart应用程序上工作,我想获取缓存(SharedPreferences)中存在的数据,然后在应用程序的UI (主屏幕)上显示它。
问题:由于SharedPreferences是一个等待调用,我的主页加载,试图读取数据,应用程序崩溃,因为还没有从SharedPreferences中获取数据,并且应用程序在此之前加载。
其他参考文章:
多任务
dart笔记13:用future实现等待多个任务完成后,再得到所有的执行结果
1 | import 'dart:async'; |