GithubHelp home page GithubHelp logo

blog's Introduction

blog

没事写写博客,博文见issues

blog's People

Contributors

jschyz avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

blog's Issues

[打印技术] 驱动参数见解

[WKWebView] 适配问题

为了让 iOS WebView 打开H5快,执行响应提升。现起项目优化 WKWebView ,逐步替换掉 UIWebView。

1. WKWebView 拦截 JS 中的 Alert / Confirm / Prompt

在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的展示

2. WKWebView 中 window.open 失效 以及 标签带有 target='_blank'

_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

3. WKWebView 白屏问题

白屏划分为三类

其一,浏览器访问H5遇到网络错误、证书错误、连接超时错误等。需拦截此等错误code,并显示友好的UI展示(特殊code需过滤);

其二,进度条引起的白屏现象,如网络重定向时,进度条归为0引起白屏;WKWebView 进度是按照真实的下载数据得出的进度百分数,但是当服务器慢请求时,进度一直为0,引发不好的体验

① 进度条宽度先是100%,然后立马变为 0%

② 频繁刷新,进度条前进后,既然后退了

③ 访问网络超时网址时,进度条停在 0% 处

其三,H5渲染白屏。

4. WKWebView 与 Cookie

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 敏感信息。

修复只针对自己的域名做单独处理。

5. 自动抓取分享设置

参考类库:https://github.com/beyondabel/BAWebView/blob/master/BAWebView/BAWebView/BAWebView.m

WKWebVIew介绍:

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:

https://github.com/DoTalkLily/LYWebViewController

[破事日常] -- 千奇百怪欺骗作用域

前言

看过 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

方案 1

frames.alert(1)
// 或
var w = open()
w.opener.alert(1)
w.close

这类都是属于借助其他窗口获得alert访问权限:

frames 访问框架集上的引用,通过框架集访问到当前页面上下文

opener 是借用新窗口,通过 opener 访问到当前页面上下文

类同的方式有:top self parent,不过被 var 覆写了。

方案 2

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)')()

方案 3

var e = eval
e('alert(1)')

方案 4

(0, eval)('alert(1)')

原理同方案3,只是省了个临时变量,逗号运算符对它对每个操作对象求值(从左至右),然后返回最后一个操作对象的值。括号运算符改变了运行顺序。

方案 5

addEventListener('click', function(){ this.alert(1) })
dispathEvent(new Event('click'))

[WebView] Android 内核调研报告

内核碎片化

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 数据来源:内部 image

按照操作系统划分,占比如下

操作系统 系统版本 启动次数占比
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 新特性支持

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 image

从上表可以看出:

X5 Blink 内核固定版本内核情况下,跑分一致。
in WebView 跑分随着系统版本趋势增长。
这些 feature 具体体现在业务中:

  • input[type=date|number] 功能样式不统一;
  • 多音频播放问题;
  • 新 API 支持 fetch / promise;
  • ES6 语法支持;
  • 上传图片前压缩处理FileReader 支持;
  • CSS 新排版引擎支持。

抽象出来则是 HTML标准、JS 新API、CSS、SVG、Others ,为解决这些兼容性体验问题,开发者们嚼劲脑汁。

WebRTC 支持

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

附阅读:

X5 浏览器内核调研报告

Android碎片化与兼容性问题的元凶

Javascript能做解析器的那些事

现如今,解析已经在Javascript中有很多运用场景,比如:

也可以这样不严谨归纳认为,将一种语言转换成另一种语言的形式,需要用到解析流程。

学习实现语言,最好是从最简单,最干净的语言开始,迅速写出一个可用的解释器。之后再逐步往里面添加特性,同时保持正确。这样你才能有条不紊地构造出复杂的解释器。

程序代码映射称为一颗语法树,而通过操纵语法树,我们能够精准的获得程序代码中的某个节点。例如声明语句,赋值语句,而这是用正则表达式所不能准确体现的地方。

什么是语法树

抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,对于一种具体编程语言下的源代码,通过构建语法树的形式将源代码中的语句映射到树中的每一个节点上。

下面用一张图来描述解析过程Code → Token → AST

AST

那么,有什么用呢?

抽象语法树的作用非常的多,比如下面这些静态分析器

  • UglifyJS、eslint、JSHint

  • CoffeeScript 、Bable / Buble 转换 ES6

  • Postcss、Css Lint

    etc...

实际这背后就是在对抽象语法树进行操作。

那么,AST该如何生成呢?

解析器

在编译语言流程中,程序中的一段代码在执行之前会经过解析环节转换成有意义的结构,这个环节通常分为二个步骤实现

词法分析

词法分析也称为分词过程Tokenizing/Lexing),这个过程将有意义的字符组成的字符串分解成有意义的代码块,这些代码块被称为词法单元(token)

拿大家熟悉的Javascript程序来讲解,例如:var AST = "is Tree"; 。这段程序会被分解成下面这些词法单元: varAST=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; 这段程序会被分解成下面这些词法单元: vara=2;

事实上在浏览器引擎里解析HTML、CSS、JS是以单个字符(token)进行分词,然后在语法分析中使用类似递归的方式遍历这些token,组装成有意义的。比如,var a = 2; 这段程序会被分解成下面这些词法单元: vara=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 模版引擎解析就少了完整的词法分析过程,故而无法准确确定错误源所在的位置,给调试者带来诸多不便

参考

Parsing wiki

how browsers work HTML解析

You-Dont-Know-JS Javascript 解析

how to be a compiler

building javascript tools

[JavaScriptCore] Objective-C调用JavaScript

例如有一个"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来调用,参数是一个数组,如果没有参数则传入空数组@[]。

[JavaScriptCore] JavaScript调用Objective-C

还是上面的例子,将"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形参。

[打印技术] IPP -- 状态检测 (NodeJs)

先来一段代码

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

[打印技术] IPP -- 互联网打印协议

IPP

互联网打印协议(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 上输入,得出结果为

image

其中 offline-report 含义代表打印机离线状态

IPP 对象模型

IPP 包含两种基本对象类型: 打印机和作业。每种对象类型都包含实际打印机或实际打印作业的特征。每种对象类型都被定义为这种特定对象类型可支持的一组可能属性。

为了明确引用所有打印机和作业对象,所有这些对象都用统一资源标识符 (Uniform Resource Identifier, URI) 加以标识。URI 概念以及作为标识符的实现方式非常有用,因为它所提供的手段既能够唯一标识与打印服务 (IPP) 进行通信的方法,又能够唯一标识打印机队列 (//server/printers/queue) 或作业的不同网络标识符。

创建打印请求时,生成的 IPP 协议消息必须包含将对其执行操作的打印机对象的 printer-uri。可以从打印机对象或命名服务 printer-uri-supported 属性检索 printer-uri 的可能值。

IPP 打印机对象

打印机对象是 IPP 模型中的主要对象。打印机对象可为 IPP 提供服务器端支持。打印机对象包含的功能通常与物理输出设备相关联。这些功能包括假脱机、调度、变换和管理多个与打印服务器关联的设备。打印机对象用 printer-uri 唯一地标识。为了搜索和查找有关打印机对象的静态信息(如名称、上下文和打印机功能),可以将这些打印机对象注册为目录条目。动态信息(例如打印机的排队作业数目、错误和警告)与打印机对象本身相关联。

注 - 只要语义与打印机对象的语义一致,就可以使用打印机对象来表示实际设备或虚拟设备。

IPP 客户机在客户端实现协议,以便为您或代表您运行的程序提供查询打印机对象的能力,目的是为了提交和管理打印作业。IPP 服务器是打印机对象的一部分,用于实现打印服务的应用程序语义。打印机对象可以嵌入输出设备中,也可以在与输出设备进行通信的网络主机上实现。

将作业提交到打印机对象时,打印机对象将验证请求中的属性,然后创建作业对象。当您查询作业状态或监视其进度时,就在与作业对象进行交互。如果您取消打印作业,则使用的是作业对象的 Cancel-job 操作。有关作业对象操作的更多信息,请参见IPP 操作关键字

IPP 作业对象

作业对象用于为打印作业建模。作业对象包含文档。如果您通过 IPP 客户机将打印请求发送给打印机对象,则创建作业对象所需的信息将以创建请求的形式发送到打印服务器。打印机对象将验证创建请求,如果接受,打印机对象随后将创建新的作业对象。IPP 作业对象用 printer-uri 和 job-id 属性或 job-uri 属性的组合唯一地标识。有关更多详细信息,请参见IPP 操作关键字

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.

VueJs之如何追踪变化

双向数据绑定极大简化 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 方法就是一个观察者,当属性变化将会向订阅者发送消息,从而驱动视图更新。

前端代码发布

来记录最近做的一件事 -- 关于代码发布问题

借助工具: git / svn

  1. 创建两个仓库:

    • source 源代码仓库(git),用于开发
    • release 包发布仓库(svn),用于管理包和回滚

    注: svn 用于存储 release 包,原因在于 git 会存储历史记录代码,clone 代码耗时。相对于 svn 而言只需要一个地址,拉取最新代码包简单而快速。

你的 source 仓库代码,大致的内容是:

source
  ├─ dist/
  ├─ dist.tar
  ├─ src/
  └─ webpack.config.js

你的 release 仓库代码,大致的内容是:

release
  └─dist.tar
  1. 开发人员要进行部署的时候,执行 npm run build 生成对于的 dist.tar 文件。这时进行 git 提交时,gitlab触发webhook,推送信息到 jenkins

    注: 对于提交频繁的团队,可以在 jenkins 上设置一定时间间隔自动拉取最新 commit 记录,从而节省jenkins 资源(jenkins执行对应的job任务需要耗时,频繁提交,就会堆积任务)

  2. jenkins 根据推送的消息执行对应的 job,将 dist.tar 文件从 git 仓库拉取到 svn 仓库,并生成最新 update 记录,此记录用语上线/回滚等运维操作

  3. 拉取完毕后,使用运维脚本从 svn 地址拉取 dist.tar 文件,然后将解压结果推送到测试/生产服务器

实现完这一套,无意间发现跟 @fouber issues  里提到的 关于代码发布 实现方式类似,故此纪录下最近的工作细节。但实现也是有差别的:
我没有用 jenkins 里做代码构建操作。

  • 其一,每次npm install耗时是个问题
  • 其二,如果package.json里依赖项,如果引用未知第三方不安全包,npm install 隐患比较大。

[WebView] 白屏原因 iOS

1. 蓝色进度条 – NJKWebViewProgress

再说白屏之前,先来说说 NJKWebViewProgress,在iOS大概表现为 { height: 2px, color: blue } 细线。

image

对,这跟细细对蓝线就是 NJKWebViewProgress 实现的,既然有3.7k的star,而且 facebook 大佬也在用。
假如我说 webview 白屏原因跟 NJKWebViewProgress 有见解原因呢,你们会不会一口盐汽水喷死我啊(逃。。。

2. iOS View层布局概念

image

在视图中,

NavigationBar 有3种元素:返回、Title、分享
WebView 由于底色原因,显示白色底配色方案 – (白屏的由来)
NJKWebViewProgress 则依托在 NavigationBar 层最底部 { position: fixed; bottom: 0; height: 2px; width: 0 }

3. NJKWebViewProgress 生命周期

当加载一个神秘的 H5 时,首当其冲被 shouldStartLoadWithRequest 拦截,拦截做什么呢? 当然是我们的特色服务 –- 追加签名 及 白名单验证逻辑。注意此时进度为0,用CSS表示 { width: 0px }。

好了,上一步验证无误后,就开始跟服务器进行连接勾搭数据了,当接收到服务端的握手后,就触发 webViewDidStartLoad 委托。设置进度 0.1 ,用CSS表示 { width: 10%}

当网页加载正常时,并且内部资源加载差不多时,触发 webViewDidFinishLoad 委托。分情况设置进度条进度,具体看下图。

didFailLoadWithError 网页加载失败(断网、网络延迟..)是触发。

image

好了,了解上面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 的锅。

[打印技术] 打印质量 -- 图片分辨率(输入分辨率)

启示

人眼分辨的极限,应该是 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 比上一版略高 同上 同上
前端H5预览
图片显示比例 采用算法 照片尺寸 预览 预览丢失像素 裁图(标准是天心口述)
1 : 1.41后端传递 Cover 544 x 736 px image  灰色部分为丢失区域橘黄色部分为可视区域明显可以看出,新版Android广告区域增高,导致图片裁剪部分严重 542 x 742px明显达不到人眼无法区分最低密度 300dpi 的像素尺寸874 x 1240 px,人眼能感觉到照片打印有明显的颗粒感
720 x 864 px image

附:
喷墨打印机分辨率和照片分辨率中两个 dpi 的 dot 涵义是不同的。(图片仅示意,大家领会精神即可。来源:自维基百科)

image

https://zh.wikipedia.org/wiki/%E5%99%B4%E5%A2%A8%E5%8D%B0%E8%A1%A8%E6%A9%9F

webpack 流程分析

前言

最近在整理内部组件文档时,使用webpack将md文档转换成SPA页面,在dev模式下,发现webpack会循环构建,在多次编辑结束后才停止。
百思不得骑姐,遂追究其原因。
翻阅webpack2文档、google查询,都未有帮助的见解。
于是从构建的生命周期入手,看是否有所帮助。

compiler对象

注:不带超链接事件名为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 输出完成

[性能优化] <head> 头部块优化

一般来说HTML在开始接收到返回数据的时候就开始解析HTML并构建DOM树(详见: how brower work )。如果没有JS(JavaScript)阻塞的话一般会相继完成。

<link href="//res.jiuyan.info/.../base.css" rel="stylesheet"> 
<script> 
// rem 布局脚本 
</script> 
</head> 

通常情况下,上面代码的link部分和script部分如果单独出现,都不会阻塞页面的解析:

  • CSS不会阻止页面继续向下继续。
  • 内联的JS很快执行完成,然后继续解析文档。

然而,当这两部分同时出现的时候,问题就来了。

  • CSS加载阻塞了下面的一段内联JS的执行,而被阻塞的内联JS则阻塞了HTML的解析。

通常情况下,CSS不会阻塞HTML的解析,但如果CSS后面有JS,则会阻塞JS的执行直到CSS加载完成(即便JS是内联的脚本),从而间接阻塞HTML的解析。

优化

一个小小的内联JS放错位置也会让性能下降很多。

CSS的加载会在HTML解析到CSS的标签时开始,所以CSS的标签要尽量靠前。
但是,CSS链接下面不能有任何的JS标签(包括很简单的内联JS),否则会阻塞HTML的解析。
如果必须要在头部增加内联脚本,一定要放在CSS标签之前。

image

结果对照

image

将DOM Processing平均值基线远远降到 1s 以下

[打印技术] escputil -- 冲洗喷嘴(冲墨)

当打印机墨水量低时候,打印出来的照片会有严重的偏色。 -- 缺墨状态

当灌满墨水后,再次打印照片还会有严重当偏色。 -- 喷嘴没墨

这是因为,墨水到喷嘴之间有一跟长长到管子。当墨水灌满后,机器不会自动压迫那块真空区域。

解决这方法,需要清洗喷嘴,把刚灌的墨水压至喷嘴处。 -- Clean Heads

以往的清洗过程

方案1:

需要智能终端运营联系当地的技术人员,用window电脑上的驱动连接打印机,再运行 Clean Heads 程序。这个过程重复几次遍可

方案2:

需要人为的长按住 L310 打印机 喷墨键,此过程非常耗时,大约2小时才清洗完毕。

下面是清洗喷嘴实时打印过程的图片。

image

现在的清洗过程

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元

保质期内免费清洗

所以请爱惜打印机,严重堵住相当于报废了此款打印机

image

[打印技术] escputil -- 喷嘴检查

墨仓式机器加长了墨管 打印机墨管进空气 晃动 打印纸蹭墨会导致图片丢色。

但搬运机器、加墨维护是不可能避免这些情况但发生。

一般轻微的丢色,人的肉眼是很难区分的,比如下面一张图。

image

颜色没有偏差,但是细心观察,会发现有很多细微的横线。

这就是图片质量差,间接影响用户的打印体验及口碑。

如何检测

epson 墨仓式打印机的驱动都会自带一个小工具 – Nozzles Check (喷嘴检查)

运行此程序,会打印如下的样张。

image

对比,如果没有出现断线,说明打印机打出来的照片不会偏色。

image

如果出现断断续续的情况,说明喷嘴有问题,需要进行 冲墨程序 #10

image

Android 喷嘴检查

escputil 工具是 gutenprint 万能驱动组成部分。里面包含 epson 打印机常用功能。

下面命令是 Nozzles Check 指令。

/system/cups/bin/escputil -n -r /dev/usb/lp0

需要注意的是:在 智能终端 程序启动时,运行将失效。

[WebView] 白屏问题初分析

在写iOS UIWebView 组件,针对这段时间搜集到的 “白屏事件” 做了分析。

讲目前遇到的问题划分为4类。

1. iOS 纯白屏;Android 显示类似 Not Found 页面

复现:
访问 s.com,此域名还未被 DNS 正常解析,也就是不存在的域名。

iOS Android 微信
image image image

原因:
iOS 遇到错误 DNS 解析错误,会提示 NSURLErrorCannotFindHost 错误提示,我询问@九毛后,得到他们在遇到此类Error错误直接跳过处理

附上 Error 错误列表

Android 则显示 Chrome 自带的错误提示,虽然友好,但体验较差。

2. 在网络通畅情况下,打开H5时,白屏需要耗时很长(iOS、Android都会出现)

复现:(以 iOS 举例)
正常访问一个h5页面时,将依次执行 shouldStartLoadWithRequest webviewDidStartLoad webviewDidFinishLoad 3个委托事件。

假如页面包含 iframe、location.href = ‘URL Scheme’,那么上面3个委托事件需要重新执行,损耗了加载性能。

原因:

shouldStartLoadWithRequest 会拦截 request 请求(不包含资源请求),如果委托返回 return YES;那么后续的委托将重新执行,导致真正需要加载的H5延后执行 webviewDidFinishLoad

3. 网络“通畅”,但页面请求未发出

问题:
某些用户手机经常性出现页面加载不完全问题。排除个别现象,那么其他真实用户也会出现此状态,严重影响后续但使用体验。

跟运维探查几次,大部分在日志都无法抓到黑羽的请求。

猜测:

App访问H5页面,请求未发送,但访问的是本地缓存?

4. 网络通畅,页面请求已发出,但 ajax 请求未发出

问题:

H5页面报错等。

原因:

此类事故属于 H5 问题,比如Android低机型不支持新特性等,导致JS报错异常,从而中端后续操作

5. 不算白屏现象,属于 NSProgressbar bug。

问题:

iOS Webview 进度条加载总是卡在末尾处,过个2~5s时间在跑完。

image

原因:

iOS 在触发委托 webviewDidFinishLoad ,会判断 document.readyState 是否等于 complete,相等,蓝颜色的进度条才会走完。

但是呢?触发 webviewDidFinishLoad 事件那个时间点,未必 document.readyState 的状态是 complete,也有可能是 interactive 状态,所以判断不是很准确,造成加载没完成的假象(其实是已加载完毕状态)

------ 扩展阅读 (刚发现一遍算比较好的 UIWebview 总结)

UIWebView相关协议方法总结

UIWebView高级

吐槽 UIWebView 和 WKWebview

iOS H5 容器的一些探究(一):UIWebView 和 WKWebView 的比较和选择

https://www.slideshare.net/lpilorz/webview-security-on-ios-en

WebView·开车指南

[JavaScriptCore] 启蒙篇

JavaScriptCore framework 是 iOS7 新引入的功能。该框架让 Objective-C 和 JavaScript 代码直接的交互变得更加的简单方便。

JavaScriptCore 可以理解为一个可运行 JavaScript 的容器,除了以往的 WebView 容器有这个能力以外。

同时,JavaScriptCore 能获取 WebView 容器执行 JavaScript 上下文环境,拥有 H5 与 Native 交互的能力,如 Hybrid 技术。

同样,JavaScriptCore 也能单独运行 JavaScript,如 React-Native 技术、JSpatch 技术。

JavaScriptCore中的类

要使用JavaScriptCore,首先我们需要引入它的头文件 #import <JavaScriptCore/JavaScriptCore.h>

链到这个头文件,内容如下:
image

里面引入了几个重要的类:

  • JSContext JavaScript runtime,可以执行 js 代码和注册 native 方法接口
  • JSValue JSContext执行后的返回结果,他可以是任何js类型(比如基本数据类型和函数类型,对象类型等)
    并且都有对象的方法转换为native对象
  • JSManagedValue JSValue的封装,用它可以解决 js 和 native代码之间循环引用的问题
  • JSVirtualMachine 管理JS运行时和管理js暴露的native对象的内存
  • JSExport 是一个协议,通过实现它可以完成把一个 native对象 暴漏给js,起到安全的作用

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.