jschyz / blog Goto Github PK
View Code? Open in Web Editor NEW没事写写博客
没事写写博客
人眼分辨的极限,应该是 30cm 距离下每 1mm 人眼可分辨线数的科学试验,一般结论是 300-400dpi 左右,这是公认的。
所以 iPhone 4 才敢把其产品命名 「Retina Display」(这个「视网膜显示器」的分辨率约为 326 ppi)。
这里的分辨率是指在用打印机打印该图像时所使用的分辨率,也就是打印该图片时每英寸多少点墨滴。列出了一些常用标准输出格式的分辨率。
网页采用72dpi
报纸采用125-170dpi
杂志/宣传品采用300dpi
高品质书籍采用350-400dpi
当像素密度超过300 dpi时,人眼就无法区分出单独的像素。
纸张尺寸 | 输入分辨率(300dpi) |
---|---|
A7 (74 x 105mm) | 874 x 1240 px |
拍照输出尺寸 | 隐藏bar | 广告尺寸 | 显示器屏幕大小 | 影响因素 |
---|---|---|---|---|
544 x 736 px | Yes | 1080 x 1920 px | 拍照输出尺寸是屏幕可视区域比例 | |
720 x 864 px | Yes | 比上一版略高 | 同上 | 同上 |
附:
喷墨打印机分辨率和照片分辨率中两个 dpi 的 dot 涵义是不同的。(图片仅示意,大家领会精神即可。来源:自维基百科)
https://zh.wikipedia.org/wiki/%E5%99%B4%E5%A2%A8%E5%8D%B0%E8%A1%A8%E6%A9%9F
墨仓式机器加长了墨管 打印机墨管进空气 晃动 打印纸蹭墨会导致图片丢色。
但搬运机器、加墨维护是不可能避免这些情况但发生。
一般轻微的丢色,人的肉眼是很难区分的,比如下面一张图。
颜色没有偏差,但是细心观察,会发现有很多细微的横线。
这就是图片质量差,间接影响用户的打印体验及口碑。
epson 墨仓式打印机的驱动都会自带一个小工具 – Nozzles Check (喷嘴检查)
运行此程序,会打印如下的样张。
对比,如果没有出现断线,说明打印机打出来的照片不会偏色。
如果出现断断续续的情况,说明喷嘴有问题,需要进行 冲墨程序 #10
escputil 工具是 gutenprint 万能驱动组成部分。里面包含 epson 打印机常用功能。
下面命令是 Nozzles Check 指令。
/system/cups/bin/escputil -n -r /dev/usb/lp0
需要注意的是:在 智能终端 程序启动时,运行将失效。
还是上面的例子,将"hello.js"的内容改为:
function printHello() {
print("Hello, World!");
}
这里的print函数用Objective-C代码来实现
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"hello" ofType:@"js"];
NSString *scriptString = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:scriptString];
context[@"print"] = ^(NSString *text) {
NSLog(@"%@", text");
};
JSValue *function = context[@"printHello"];
[function callWithArguments:@[]];
这里将一个Block以"print"为名传递给JavaScript上下文,JavaScript中调用print函数就可以执行这个Objective-C Block。
注意这里JavaScript中的字符串可以无缝的桥接为NSString,实参"Hello, World!"被传递给了NSString类型的text形参。
来记录最近做的一件事 -- 关于代码发布问题
借助工具: git / svn
创建两个仓库:
source
源代码仓库(git),用于开发release
包发布仓库(svn),用于管理包和回滚注: svn 用于存储 release
包,原因在于 git 会存储历史记录代码,clone 代码耗时。相对于 svn 而言只需要一个地址,拉取最新代码包简单而快速。
你的 source
仓库代码,大致的内容是:
source
├─ dist/
├─ dist.tar
├─ src/
└─ webpack.config.js
你的 release
仓库代码,大致的内容是:
release
└─dist.tar
开发人员要进行部署的时候,执行 npm run build
生成对于的 dist.tar 文件。这时进行 git 提交时,gitlab触发webhook,推送信息到 jenkins
注: 对于提交频繁的团队,可以在 jenkins 上设置一定时间间隔自动拉取最新 commit 记录,从而节省jenkins 资源(jenkins执行对应的job任务需要耗时,频繁提交,就会堆积任务)
jenkins 根据推送的消息执行对应的 job,将 dist.tar 文件从 git 仓库拉取到 svn 仓库,并生成最新 update 记录,此记录用语上线/回滚等运维操作
拉取完毕后,使用运维脚本从 svn 地址拉取 dist.tar 文件,然后将解压结果推送到测试/生产服务器
实现完这一套,无意间发现跟 @fouber issues 里提到的 关于代码发布 实现方式类似,故此纪录下最近的工作细节。但实现也是有差别的:
我没有用 jenkins 里做代码构建操作。
JavaScriptCore framework 是 iOS7 新引入的功能。该框架让 Objective-C 和 JavaScript 代码直接的交互变得更加的简单方便。
JavaScriptCore 可以理解为一个可运行 JavaScript 的容器,除了以往的 WebView 容器有这个能力以外。
同时,JavaScriptCore 能获取 WebView 容器执行 JavaScript 上下文环境,拥有 H5 与 Native 交互的能力,如 Hybrid 技术。
同样,JavaScriptCore 也能单独运行 JavaScript,如 React-Native 技术、JSpatch 技术。
要使用JavaScriptCore,首先我们需要引入它的头文件 #import <JavaScriptCore/JavaScriptCore.h>
里面引入了几个重要的类:
Paper FeedSequence
https://www.manualslib.com/manual/48548/Epson-Stylus-870.html?page=72
GCR Transition
https://en.wikipedia.org/wiki/Grey_component_replacement
http://the-print-guide.blogspot.com/2009/04/gcr-reseparation-for-ink-savings-and.html
Paper Menu Settings
Interleave Method
http://gimp-print.sourceforge.net/reference-html/x1731.html
Density
gamma
https://files.support.epson.com/htmldocs/per16u/per16urf/basic_7.htm
互联网打印协议(IPP;Internet Printing Protocol)是一个在互联网上打印的标准网络协议,它容许用户可以透过互联网作遥距打印及管理打印工作等工作。用户可以透过相关界面来控制打印品所使用的纸张种类、分辨率等各种参数。
与其他基于互联网的协议一样,IPP可以用于内联网及互联网等基于IP协议的网络上。不过,与一般IP协议不同的是:IPP亦同时支援安全连结。所以,用户可以透过网络进行存取控制、认证及加密,使打印过程更安全。
协议规范
https://datatracker.ietf.org/doc/search/?name=Internet+Printing+Protocol&activedrafts=on&rfcs=on
curl \
--request POST \
--header "Content-Type: application/ipp" \
--data-binary "@binary" \
http://localhost:631/printers/EPSON_L310_Series
附上二进制文件:binary.zip
在 Terminal 上输入,得出结果为
其中 offline-report 含义代表打印机离线状态
IPP 包含两种基本对象类型: 打印机和作业。每种对象类型都包含实际打印机或实际打印作业的特征。每种对象类型都被定义为这种特定对象类型可支持的一组可能属性。
为了明确引用所有打印机和作业对象,所有这些对象都用统一资源标识符 (Uniform Resource Identifier, URI) 加以标识。URI 概念以及作为标识符的实现方式非常有用,因为它所提供的手段既能够唯一标识与打印服务 (IPP) 进行通信的方法,又能够唯一标识打印机队列 (//server/printers/queue) 或作业的不同网络标识符。
创建打印请求时,生成的 IPP 协议消息必须包含将对其执行操作的打印机对象的 printer-uri。可以从打印机对象或命名服务 printer-uri-supported 属性检索 printer-uri 的可能值。
打印机对象是 IPP 模型中的主要对象。打印机对象可为 IPP 提供服务器端支持。打印机对象包含的功能通常与物理输出设备相关联。这些功能包括假脱机、调度、变换和管理多个与打印服务器关联的设备。打印机对象用 printer-uri 唯一地标识。为了搜索和查找有关打印机对象的静态信息(如名称、上下文和打印机功能),可以将这些打印机对象注册为目录条目。动态信息(例如打印机的排队作业数目、错误和警告)与打印机对象本身相关联。
注 - 只要语义与打印机对象的语义一致,就可以使用打印机对象来表示实际设备或虚拟设备。
IPP 客户机在客户端实现协议,以便为您或代表您运行的程序提供查询打印机对象的能力,目的是为了提交和管理打印作业。IPP 服务器是打印机对象的一部分,用于实现打印服务的应用程序语义。打印机对象可以嵌入输出设备中,也可以在与输出设备进行通信的网络主机上实现。
将作业提交到打印机对象时,打印机对象将验证请求中的属性,然后创建作业对象。当您查询作业状态或监视其进度时,就在与作业对象进行交互。如果您取消打印作业,则使用的是作业对象的 Cancel-job 操作。有关作业对象操作的更多信息,请参见IPP 操作关键字。
作业对象用于为打印作业建模。作业对象包含文档。如果您通过 IPP 客户机将打印请求发送给打印机对象,则创建作业对象所需的信息将以创建请求的形式发送到打印服务器。打印机对象将验证创建请求,如果接受,打印机对象随后将创建新的作业对象。IPP 作业对象用 printer-uri 和 job-id 属性或 job-uri 属性的组合唯一地标识。有关更多详细信息,请参见IPP 操作关键字。
Operation Name | CUPS | Code | Brief Description |
---|---|---|---|
Print-Job | 1.0 | 0x0002 | Print a file. |
Validate-Job | 1.0 | 0x0004 | Validate job attributes. |
Create-Job | 1.1 | 0x0005 | Create a print job. |
Send-Document | 1.1 | 0x0006 | Send a file for a print job. |
Cancel-Job | 1.0 | 0x0008 | Cancel a print job. |
Get-Job-Attributes | 1.0 | 0x0009 | Get job attributes. |
Get-Jobs | 1.0 | 0x000A | Get all jobs. |
Get-Printer-Attributes | 1.0 | 0x000B | Get printer attributes. |
Hold-Job | 1.1 | 0x000C | Hold a job for printing. |
Release-Job | 1.1 | 0x000D | Release a job for printing. |
Restart-Job | 1.1 | 0x000E | Restarts a print job. |
Pause-Printer | 1.0 | 0x0010 | Pause printing on a printer. |
Resume-Printer | 1.0 | 0x0011 | Resume printing on a printer. |
Purge-Jobs | 1.0 | 0x0012 | Purge all jobs. |
Set-Printer-Attributes | 1.4 | 0x0013 | Set attributes for a printer. |
Set-Job-Attributes | 1.1 | 0x0014 | Set attributes for a pending or held job. |
Create-Printer-Subscription | 1.2 | 0x0016 | Creates a subscription associated with a printer or the server. |
Create-Job-Subscription | 1.2 | 0x0017 | Creates a subscription associated with a job. |
Get-Subscription-Attributes | 1.2 | 0x0018 | Gets the attributes for a subscription. |
Get-Subscriptions | 1.2 | 0x0019 | Gets the attributes for zero or more subscriptions. |
Renew-Subscription | 1.2 | 0x001A | Renews a subscription. |
Cancel-Subscription | 1.2 | 0x001B | Cancels a subscription. |
Get-Notifications | 1.2 | 0x001C | Get notification events for ippget subscriptions. |
Enable-Printer | 1.2 | 0x0022 | Accepts jobs on a printer. |
Disable-Printer | 1.2 | 0x0023 | Rejects jobs on a printer. |
Hold-New-Jobs | 1.4 | 0x0025 | Hold new jobs by default. |
Release-Held-New-Jobs | 1.4 | 0x0026 | Releases all jobs that were previously held. |
Cancel-Jobs | 1.5 | 0x0038 | Cancel all jobs (administrator). |
Cancel-My-Jobs | 1.5 | 0x0039 | Cancel all jobs (user). |
Close-Job | 1.5 | 0x003b | Close a created job. |
Keyword | Description |
---|---|
connecting-to-device | Connecting to printer but not printing yet. |
cover-open | The printer's cover is open. |
input-tray-missing | The paper tray is missing. |
marker-supply-empty | The printer is out of ink. |
marker-supply-low | The printer is almost out of ink. |
marker-waste-almost-full | The printer's waste bin is almost full. |
marker-waste-full | The printer's waste bin is full. |
media-empty | The paper tray (any paper tray) is empty. |
media-jam | There is a paper jam. |
media-low | The paper tray (any paper tray) is almost empty. |
media-needed | The paper tray needs to be filled (for a job that is printing). |
paused | Stop the printer. |
timed-out | Unable to connect to printer. |
toner-empty | The printer is out of toner. |
toner-low | The printer is low on toner. |
一般来说HTML在开始接收到返回数据的时候就开始解析HTML并构建DOM树(详见: how brower work )。如果没有JS(JavaScript)阻塞的话一般会相继完成。
<link href="//res.jiuyan.info/.../base.css" rel="stylesheet">
<script>
// rem 布局脚本
</script>
</head>
通常情况下,上面代码的link部分和script部分如果单独出现,都不会阻塞页面的解析:
然而,当这两部分同时出现的时候,问题就来了。
通常情况下,CSS不会阻塞HTML的解析,但如果CSS后面有JS,则会阻塞JS的执行直到CSS加载完成(即便JS是内联的脚本),从而间接阻塞HTML的解析。
一个小小的内联JS放错位置也会让性能下降很多。
CSS的加载会在HTML解析到CSS的标签时开始,所以CSS的标签要尽量靠前。
但是,CSS链接下面不能有任何的JS标签(包括很简单的内联JS),否则会阻塞HTML的解析。
如果必须要在头部增加内联脚本,一定要放在CSS标签之前。
将DOM Processing平均值基线远远降到 1s 以下
看过 You-Dont-Know-JS 欺骗词法作用域 的人应该或多还记得:
eval
setTimeout
setInterval
Function
这些都可以接受字符串参数,字符串的内容可以被解释为一段动态生成的函数代码。换句话说,在运行时来“修改”词法作用域。
先来一段代码熟悉下:
function foo(str, a) {
eval( str ); // 欺骗!
console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 ); // 1, 3
这段代码实际上在 foo(…) 内部创建来一个变量 b,并遮蔽了外部(全局)作用域中的同名变量。
今天耗子姐在群里丢出一道题,来考验大伙们该如何实现攻击手段。
此题一出,群里炸开锅,纷纷给出答案见解。
(function(){
'use strict';
var alert, prompt, confirm, location, window, top, self,
parent, document, Function, execScript,
setTimeout, setInterval, localStorage, sessionStorage, console;
// 在这里插入代码,弹出 alert(1)
})()
细心的你会发现,这是严格模式下,重写了一些关键字变量,this
在这时会绑定到 undefined
上。
那么this.alert(1)
是行不通的。
首先大多会想到如果能直接访问 window.alert
,那么正好可以实现,可惜在立即执行匿名函数里重新声明了 window
,也就是覆写了 window
,访问时也会得到 undefined
。
frames.alert(1)
// 或
var w = open()
w.opener.alert(1)
w.close
这类都是属于借助其他窗口获得alert访问权限:
frames 访问框架集上的引用,通过框架集访问到当前页面上下文
opener 是借用新窗口,通过 opener 访问到当前页面上下文
类同的方式有:top self parent
,不过被 var 覆写了。
Object.constructor('alert(1)')()
任何Javascript值类型(除 Object.create(null)、null、undefined)的原型链最上级构造器都是 Function,而 Function 执行上下文总是包含全局对象 (global) 作用域链。
类似方式:
new Error.apply.constructor('alert(1)')()
Array.constructor('alert(1)')()
2333..constructor.constructor('alert(1)')()
var e = eval
e('alert(1)')
(0, eval)('alert(1)')
原理同方案3,只是省了个临时变量,逗号运算符对它对每个操作对象求值(从左至右),然后返回最后一个操作对象的值。括号运算符改变了运行顺序。
addEventListener('click', function(){ this.alert(1) })
dispathEvent(new Event('click'))
制图源文件:
打印规范.sketch.zip
爱普生 ppd 打印描述文件:
https://github.com/endlessm/epson-inkjet-printer/tree/master/201401w/ppds
其他附件:
为了让 iOS WebView 打开H5快,执行响应提升。现起项目优化 WKWebView ,逐步替换掉 UIWebView。
在WKWebview中,js的alert是不会出现任何内容的,必须重写WKUIDelegate委托的runJavaScriptAlertPanelWithMessage message方法,自己处理alert。类似的还有Confirm和prompt也和alert类似,这里我只以alert为例。
// 调用JS的alert()方法
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
// 调用JS的confirm()方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 调用JS的prompt()方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
示例代码:https://github.com/JYSDeveloper/WKWebViewDemo/blob/master/WKWebViewDemo/ViewController.m#L167
验证重点:① Confirm Prompt 的回掉 ② 提示框UI的展示
_blank 标签,众所周知,是让浏览器新开一个页面来打开链接,而不是在原网页上打开。
在UIWebView上,只有一个页面,所以会自动在原来的页面上打开新链接。
但是在WKWebView上就不是这样了。
x <a onclick="window.open('http://www.qq.com')">window.open</a>
√ <a onclick="window.open('http://www.qq.com', '_self')">window.open</a>
x <a href="https://www.baidu.com" target='_blank'>target _blank</a>
√ <a href="https://www.baidu.com">target _self</a>
示例页面:window.open http://ui.ptlogin2.qq.com/cgi-bin/login?appid=636026402&style=8&s_url=http%3A%2F%2Fxw.qq.com%2Findex.htm <点击注册新页面,或忘记密码>
解决方法:http://www.jianshu.com/p/3a75d7348843
白屏划分为三类
其一,浏览器访问H5遇到网络错误、证书错误、连接超时错误等。需拦截此等错误code,并显示友好的UI展示(特殊code需过滤);
其二,进度条引起的白屏现象,如网络重定向时,进度条归为0引起白屏;WKWebView 进度是按照真实的下载数据得出的进度百分数,但是当服务器慢请求时,进度一直为0,引发不好的体验
① 进度条宽度先是100%,然后立马变为 0%
② 频繁刷新,进度条前进后,既然后退了
③ 访问网络超时网址时,进度条停在 0% 处
其三,H5渲染白屏。
Cookie 问题是目前 WKWebView 的一大短板
① WKWebView loadRequest H5的时候,发现 Cookie 没有带上,而切换页面的时候,则可以。这会影响依赖 cookie 做登录校验的 H5
原因在于:WKWebView Cookie 问题在于 WKWebView 发起的请求不会自动带上存储于 NSHTTPCookieStorage 容器中的 Cookie
原理链接:http://www.10tiao.com/html/330/201701/2653578513/1.html
② WKWebView 访问任何域名的网址,都会带上默认的 token 等 cookie 敏感信息。
修复只针对自己的域名做单独处理。
参考类库:https://github.com/beyondabel/BAWebView/blob/master/BAWebView/BAWebView/BAWebView.m
https://zhihu.com/question/54564198/answer/140030597
附:
http://www.jianshu.com/p/403853b63537
http://www.jianshu.com/p/90a90bd13aac
http://www.jianshu.com/p/7bb5f15f1daa
http://liuyanwei.jumppo.com/2015/10/17/ios-webView.html
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1483682025_enmey
http://www.jianshu.com/p/9513d101e582
github:
例如有一个"hello.js"文件内容如下:
function printHello() {
}
在Objective-C中调用printHello方法:
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"hello" ofType:@"js"];
NSString *scriptString = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:scriptString];
JSValue *function = context[@"printHello"];
[function callWithArguments:@[]];
分析以上代码:
首先初始化了一个JSContext,并执行JavaScript脚本,此时printHello函数并没有被调用,只是被读取到了这个context中。
然后从context中取出对printHello函数的引用,并保存到一个JSValue中。
注意这里,从JSContext中取出一个JavaScript实体(值、函数、对象),和将一个实体保存到JSContext中,语法均与NSDictionary的取值存值类似,非常简单。
最后如果JSValue是一个JavaScript函数,可以用callWithArguments来调用,参数是一个数组,如果没有参数则传入空数组@[]。
现如今,解析已经在Javascript中有很多运用场景,比如:
handlebars模版引擎里 compiler模块 将模版语法解析HTML语法
JSON库的 parse 将Json字符串转解析Json对象
vuejs 这类新型的框架内置 html-parser
就最近在稀土上看到一篇文章 how to be a compiler ,教你一步步的将 DBN 编程语言 转换成 SVG 的,也同样运用了解析器技术
babel postcss hexo coffeeScript eslint etc...
也可以这样不严谨归纳认为,将一种语言转换成另一种语言的形式,需要用到解析流程。
学习实现语言,最好是从最简单,最干净的语言开始,迅速写出一个可用的解释器。之后再逐步往里面添加特性,同时保持正确。这样你才能有条不紊地构造出复杂的解释器。
程序代码映射称为一颗语法树,而通过操纵语法树,我们能够精准的获得程序代码中的某个节点。例如声明语句,赋值语句,而这是用正则表达式所不能准确体现的地方。
抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,对于一种具体编程语言下的源代码,通过构建语法树的形式将源代码中的语句映射到树中的每一个节点上。
下面用一张图来描述解析过程Code → Token → AST
抽象语法树的作用非常的多,比如下面这些静态分析器
UglifyJS、eslint、JSHint
CoffeeScript 、Bable / Buble 转换 ES6
Postcss、Css Lint
etc...
实际这背后就是在对抽象语法树进行操作。
那么,AST该如何生成呢?
在编译语言流程中,程序中的一段代码在执行之前会经过解析环节转换成有意义的结构,这个环节通常分为二个步骤实现
词法分析也称为分词过程
(Tokenizing/Lexing),这个过程将有意义的字符组成的字符串分解成有意义的代码块,这些代码块被称为词法单元(token)
。
拿大家熟悉的Javascript程序来讲解,例如:var AST = "is Tree";
。这段程序会被分解成下面这些词法单元: var
、AST
、 =
、 is Tree
、;
input: "var a = 2;"
output: [
{ type: 'Keyword', value: 'var' },
{ type: 'Identifier', value: 'AST' },
{ type: 'Punctuator', value: '=' },
{ type: 'String', value: 'is Tree' } ,
//{ type: 'EOF', value: ';' }
]
分词逻辑也有难易之分。
通常将每个关键词用空格分开,比如,
var a = 2;
这段程序会被分解成下面这些词法单元:var
、a
、=
、2
、;
事实上在浏览器引擎里解析HTML、CSS、JS是以单个字符(token)进行分词,然后在语法分析中使用类似递归的方式遍历这些token,组装成有意义的。比如,
var a = 2;
这段程序会被分解成下面这些词法单元:var
、a
、=
、2
、`;
(Parsing)是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构树。这个树被称为“抽象语法树”(Abstract Syntax Tree, AST)
input: [
{ type: 'Keyword', value: 'var' },
{ type: 'Identifier', value: 'AST' },
{ type: 'Punctuator', value: '=' },
{ type: 'String', value: 'is Tree' }
]
output: {
type: 'Program',
body: [
{
type: 'VariableDeclaration',
declarations: [
{
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'Identifier',
name: 'a'
},
right: {
type: 'StringLiteral',
value: 2
}
}
]
}
]
}
题外话:
artTemplate 模版引擎解析就少了完整的词法分析
过程,故而无法准确确定错误源所在的位置,给调试者带来诸多不便
how browsers work HTML解析
You-Dont-Know-JS Javascript 解析
Android WebView 内核是跟系统版本绑定的。要想获取更多的H5新特性,就得相应升级系统。
系统版本 | 启动次数占比 |
---|---|
6.0.1 | 25.43% |
5.1 | 16.52% |
5.1.1 | 14.15% |
6.0 | 12.55% |
7.0 | 10.89% |
7.1.1 | 10.23% |
4.4.4 | 4.32% |
5.0.2 | 2.87% |
4.4.2 | 1.1% |
other | 1.93% |
抽样时间:2017.9.12 ~ 2017.9.19 数据来源:内部 | ![]() |
按照操作系统划分,占比如下
操作系统 | 系统版本 | 启动次数占比 |
---|---|---|
Kitkat | 4.4.2, 4.4.4 | 5.42% |
Lollipop | 5.0.2, 5.1, 5.1.1 | 33.54% |
Marshmallow | 6.0, 6.0.1 | 37.98% |
Nougat | 7.0, 7.1.1 | 21.12% |
other | 1.93% |
HTML5 标准支持情况,此指标代表 feature 支持情况,不代表性能测试。
浏览器特性支持得全,用户才能体验到某些高级功能。
系统 | 机型 | 自带 | in WebView | X5 Blink |
---|---|---|---|---|
7.1.1 | MI6 | 434 | 504 | 504 |
7.0 | KNT-AL20 | 431 | 492 | 481 |
5.1.1 | Nexus 5 | 512 | 496 | 484 |
5.1 | OPPO R9m | 496 | 466 | 484 |
4.4.4 | MI 4LTE | 434 | 370 | 484 |
4.2.2 | MK260 | 289 | 289 | 484 |
测试方式:http://html5test.com/测试机型:大部分均为测试机 inTest | ![]() |
从上表可以看出:
X5 Blink 内核固定版本内核情况下,跑分一致。
in WebView 跑分随着系统版本趋势增长。
这些 feature 具体体现在业务中:
抽象出来则是 HTML标准、JS 新API、CSS、SVG、Others ,为解决这些兼容性体验问题,开发者们嚼劲脑汁。
Android 系统版本 5.0 以上,WebView对 WebRTC 特性进行支持。
由下表测试结果得知,国内厂商对WebView内核进行了精简或替代,导致 WebRTC 新特性支持不一。
X5 Blink在腾讯TBS团队支持下,在低版本平台也对 WebRTC 有良好的兼容性。减少了系统内核的碎片化问题。
系统 | 机型 | 默认浏览器 | in WebView | X5 Blink |
---|---|---|---|---|
7.1.1 | MI6 | NO | NO | YES |
7.0 | KNT-AL20 | NO | NO | YES |
6.0.1 | SM-G9250 | YES | NO | YES |
6.0.1 | R9s | NO | NO | YES |
5.1.1 | Nexus 5 | NO | NO | YES |
5.1 | OPPO R9m | NO | NO | YES |
4.4.4 | MI 4LTE | NO | NO | YES |
4.2.2 | MK260 | NO | NO | YES |
iOS 11.0 | YES | NO | NO | |
遗憾的是 WKWebView 还不支持 getUserMedia | ||||
测试方式:https://jeromeetienne.github.io/AR.js/three.js/examples/mobile-performance.html |
附阅读:
再说白屏之前,先来说说 NJKWebViewProgress,在iOS大概表现为 { height: 2px, color: blue } 细线。
对,这跟细细对蓝线就是 NJKWebViewProgress 实现的,既然有3.7k的star,而且 facebook 大佬也在用。
假如我说 webview 白屏原因跟 NJKWebViewProgress 有见解原因呢,你们会不会一口盐汽水喷死我啊(逃。。。
在视图中,
NavigationBar 有3种元素:返回、Title、分享
WebView 由于底色原因,显示白色底配色方案 – (白屏的由来)
NJKWebViewProgress 则依托在 NavigationBar 层最底部 { position: fixed; bottom: 0; height: 2px; width: 0 }
当加载一个神秘的 H5 时,首当其冲被 shouldStartLoadWithRequest 拦截,拦截做什么呢? 当然是我们的特色服务 –- 追加签名 及 白名单验证逻辑。注意此时进度为0,用CSS表示 { width: 0px }。
好了,上一步验证无误后,就开始跟服务器进行连接勾搭数据了,当接收到服务端的握手后,就触发 webViewDidStartLoad 委托。设置进度 0.1 ,用CSS表示 { width: 10%}
当网页加载正常时,并且内部资源加载差不多时,触发 webViewDidFinishLoad 委托。分情况设置进度条进度,具体看下图。
didFailLoadWithError 网页加载失败(断网、网络延迟..)是触发。
好了,了解上面3个基础知识、概念,接下来就来说说,白屏!!!!
@cto 在风和日丽当早晨,浏览世界页看到 雪弗兰 汽车广告 banner,(大概他想买入手一辆吧 ^_^),点进去后发现页面是白板,白板,白板啊亲!
是没有进度条的白板啊亲!我正好也在现场(鼓励下不迟到的我:手动滑稽)。见证了@冬冬 有史以来困扰他很久的白屏 (不虚此行)
分析疑点:
页面白屏没有 Progressbar,那么说明执行到 shouldStartLoadWithRequest 这步,没有向 webViewDidStartLoad 进军!
接着,小心翼翼将 http://www.in66.com/g/mp5dv 链接复制到 MAC 端 Chrome 查看(我的小心肝颤抖的不要不要:( ^ω^ ) 发现很正常浏览加载呀,只是页面 JS 、CSS很多而已。
说明网络是通畅的,当shouldStartLoadWithRequest 设置为0,会立马通知执行 webViewDidStartLoad 让进度条走向 10%的位置。
好奇怪,为什么是白板啊啊?
继续分析,神秘的 H5 链接,既然是个 短链接,然后进行跳转到真实到 页面。
由 WebView 生命周期得知,当页面发生重定向后,将重新执行 shouldStartLoadWithRequest 委托。也就是将进度条给重置为 0了!
基于此条线索,脑袋模拟出尽可能多的例子。一一排除得到最有可能的结果 – 重定向的网页超时响应(网络环境差,弱网,丢包严重)
幸不辱命,实验使用 charles 对重定向页面进行限速,复现了 @cto 早晨白屏页面之迷。
接下来,将对 NJKWebViewProgress 改造,最起码白屏页面有进度条显示(作为一个进度条对尊严)。
吐槽:
钉钉也会出现白屏现象,应该也是用对 NJKWebViewProgress 库吧。为什么这么流行对库,没人提此 issue 呢?
原来是我方 H5 经常用短链接,重定向对页面包装搞事情,所以这类事件频繁。到底是谁的锅呢?
反正不是 H5 的锅。
双向数据绑定极大简化 web 应用中 view 层编写。这里的简化也就是 数据层 跟 视图层 建立一个双向的数据通道,界面的操作能实时反应到数据,数据的变更能实时展现到界面。
回顾以往的框架:
Backbone 对 modal 包装了一层变化通知机制,这样就可以在 view 层改变 DOM 元素。
Angular 使用纯 JS 对象作为 modal,然后在 digest 阶段定期的去检查该 modal 是否有变化。
对 Vue 而言呢?官方解释是
把一个普通 Javascript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用
Object.defineProperty 把这些属性全部转为 getter/setter。
用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
Vue 实现了一个 观察者-消费者(订阅者)
模式来实现数据驱动视图。通过设定对象的 setter/getter
方法来监听数据的变化,每个属性的 setter
方法就是一个观察者,当属性变化将会向订阅者发送消息,从而驱动视图更新。
keynote 文稿
先来一段代码
const ipp = require('ipp')
/**
* @uri: print-uri 地址
* @data: 参见https://tools.ietf.org/html/rfc2911#section-3.2.5.1
*/
const uri = 'ipp://localhost:631/printers/EPSON_L310_Series'
const data = ipp.serialize({
'operation': 'Get-Printer-Attributes',
'operation-attributes-tag': {
'attributes-charset': 'utf-8',
'attributes-natural-language': 'en-us',
'printer-uri': uri,
'requested-attributes': [
'printer-state',
'printer-state-reasons'
]
}
})
ipp.request(uri, data, function(err, res){
console.log(res)
})
输出可能结果值
printer-state-reasons | explain |
---|---|
none | 打印机正常开机 |
offline-report | 打印机离线(断开USB连接 OR 关机) |
media-empty | media-empty-warning | 打印机缺纸 |
toner-empty | 打印机缺墨(智能打印机支持) |
More status | https://tools.ietf.org/html/rfc2911#section-4.4.12 |
当打印机墨水量低时候,打印出来的照片会有严重的偏色。 -- 缺墨状态
当灌满墨水后,再次打印照片还会有严重当偏色。 -- 喷嘴没墨
这是因为,墨水到喷嘴之间有一跟长长到管子。当墨水灌满后,机器不会自动压迫那块真空区域。
解决这方法,需要清洗喷嘴,把刚灌的墨水压至喷嘴处。 -- Clean Heads
方案1:
需要智能终端运营联系当地的技术人员,用window电脑上的驱动连接打印机,再运行 Clean Heads 程序。这个过程重复几次遍可
方案2:
需要人为的长按住 L310 打印机 喷墨键,此过程非常耗时,大约2小时才清洗完毕。
下面是清洗喷嘴实时打印过程的图片。
escputil 工具是 gutenprint 万能驱动组成部分。里面包含 epson 打印机常用功能。
下面命令是 Clean Heads 指令, 实测有效。
/system/cups/bin/escputil -c -r /dev/usb/lp0
注:此命令建议运行4~5次。多次挤压管道中的空气。
人在办公室坐,机器远处运行。一个字 – 舒坦。
墨仓式机器加长了墨管 打印机墨管进空气 晃动 或者卡纸之类的 打印纸蹭墨会导致断线
建议放置2小时,使墨水充分浸润打印头,再做大墨量清洗测试
如果大墨量清洗无效 说明喷嘴堵头很严重,您需要与爱普生授权的服务中心联系对打印机进行专业的清洗,您可浏览以下网址查看就近的服务中心:http://www.epson.com.cn/Apps/tech_support/website.aspx
按照上述方法,清洗结果如下,但不是最完美,需要去专业清洗。
咨询价格专业清洗 100元,喷头严重堵住更换喷头 550元
保质期内免费清洗
所以请爱惜打印机,严重堵住相当于报废了此款打印机
最近在整理内部组件文档时,使用webpack将md文档转换成SPA页面,在dev模式下,发现webpack会循环构建,在多次编辑结束后才停止。
百思不得骑姐,遂追究其原因。
翻阅webpack2文档、google查询,都未有帮助的见解。
于是从构建的生命周期入手,看是否有所帮助。
注:不带超链接事件名为webpack编译内部使用。
事件名 | 解释 |
---|---|
1. option初始化 | |
entry-option | |
after-plugins | |
after-resolvers | |
2. webpack 环境变量初始化 | |
environment | |
after-environment | |
3. unlatch / watch 模式 | |
before-run | |
run | |
watch-run | |
*4. * | |
normal-module-factory | |
context-module-factory | |
5. 编译 | |
before-compile | 编译前准备 |
compile | 开始编译 |
this-compilation | |
compilation | |
make | 分析入口文件创建模块对象 |
after-compile | 完成所有模块构建结束编译过程 |
6. 产出文件 | |
emit | Compiler开始输出生成的assets,插件可以修改产出路径 |
after-emit | 输出完成 |
在写iOS UIWebView 组件,针对这段时间搜集到的 “白屏事件” 做了分析。
讲目前遇到的问题划分为4类。
复现:
访问 s.com,此域名还未被 DNS 正常解析,也就是不存在的域名。
iOS | Android | 微信 |
---|---|---|
![]() |
![]() |
![]() |
原因:
iOS 遇到错误 DNS 解析错误,会提示 NSURLErrorCannotFindHost 错误提示,我询问@九毛后,得到他们在遇到此类Error错误直接跳过处理
附上 Error 错误列表
Android 则显示 Chrome 自带的错误提示,虽然友好,但体验较差。
复现:(以 iOS 举例)
正常访问一个h5页面时,将依次执行 shouldStartLoadWithRequest webviewDidStartLoad webviewDidFinishLoad 3个委托事件。
假如页面包含 iframe、location.href = ‘URL Scheme’,那么上面3个委托事件需要重新执行,损耗了加载性能。
原因:
shouldStartLoadWithRequest 会拦截 request 请求(不包含资源请求),如果委托返回 return YES;那么后续的委托将重新执行,导致真正需要加载的H5延后执行 webviewDidFinishLoad
问题:
某些用户手机经常性出现页面加载不完全问题。排除个别现象,那么其他真实用户也会出现此状态,严重影响后续但使用体验。
跟运维探查几次,大部分在日志都无法抓到黑羽的请求。
猜测:
App访问H5页面,请求未发送,但访问的是本地缓存?
问题:
H5页面报错等。
原因:
此类事故属于 H5 问题,比如Android低机型不支持新特性等,导致JS报错异常,从而中端后续操作
问题:
iOS Webview 进度条加载总是卡在末尾处,过个2~5s时间在跑完。
原因:
iOS 在触发委托 webviewDidFinishLoad ,会判断 document.readyState 是否等于 complete,相等,蓝颜色的进度条才会走完。
但是呢?触发 webviewDidFinishLoad 事件那个时间点,未必 document.readyState 的状态是 complete,也有可能是 interactive 状态,所以判断不是很准确,造成加载没完成的假象(其实是已加载完毕状态)
------ 扩展阅读 (刚发现一遍算比较好的 UIWebview 总结)
iOS H5 容器的一些探究(一):UIWebView 和 WKWebView 的比较和选择
https://www.slideshare.net/lpilorz/webview-security-on-ios-en
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.