移动端监听单页应用的URL变化

问题 对于Native WebView而言,Andoird/iOS都提供相应的方法/代理监听页面URL的变化。例如,Android可以重写 doUpdateVisitedHistory 方法监听URL变化,iOS可以实现 shouldStartLoadWithRequest、 decidePolicyForNavigationAction 代理监听URL变化 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { //此处检测url是否有变化 return super.shouldOverrideUrlLoading(view, url); } //UIWebView - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { //此处检测url是否有变化 return YES; } //WKWebView - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { //此处检测url是否有变化 decisionHandler(WKNavigationActionPolicyAllow); } 上述技术实现对一些前端应用能正常工作。然而,遇到单页应用(SPA)时,以上方法并不会被触发–这里涉及单页应用的实现原理,尤其是基于 hash 路由的单页应用,其 前进/后退 的问题更为突出。 解决思路 对于前端应用而言,如果开发者使用固定的框架,那么在框架的路由层面拦截监听应较为容易做到。但是实际情况是开发者使用的前端框架不确定,甚至开发者连前端框架都不用,所以我们在前端框架层面截获路由器变得不那么容易。 经过调研,要监听前端页面的URL变化,大致有两种模式: 定时器模式 观察者模式 定时器模式 定期监测URL是否变化,如100毫秒监测一次,与上次监测值对比来判断URL是否变化。定时器,前端可以使用 setInterval 来实现 var oldUrl = window.location.href; setInterval(function(){ if (oldUrl !== window.location.href) { //通知Native Url变化 oldUrl = window.location.href; } },100); 当然,Native也可以开启定时器监测 window.location.href。 定时器模式的特点是实现简单,适用范围广;缺点也显而易见,监测结果不是十分准确,性能耗用高。而其性能耗用问题在移动端将会放大,故此我们不采用此模式。 观察者模式 上面提到的 doUpdateVisitedHistory、 shouldStartLoadWithRequest、 decidePolicyForNavigationAction 的实现可以算作观察者模式。 ...

March 19, 2020 · 2 min · holdsky

emcc 将C/C++编译成没有WebAssembly支持的JavaScript

No WebAssembly support found. Build with -s WASM=0 to target JavaScript instead. 也就是 emcc -s WASM=0 c/c++文件

March 12, 2020 · 1 min · holdsky

iOS 诡异的崩溃EXC_BREAKPOINT (code=1, subcode=0x1c5691d2c)

系统 : iOS 13.3.1 机型: iPhone7 dispatch_async(_jsContextQueue, ^{ JSContext *jscontent = [[JSContext alloc] init]; [UIWebView class]; }); 看代码,按照正常思维理解, [UIWebView class]是无论如何都不应崩溃的。 起初按照惯性思维去查找僵尸变量,忙活了半天没有定位。 最终定位结果如下: 在没有调用过UIWebView的任何函数时,先在非主线程调用UIWebView方法,则会导致崩溃 本文的相关代码如下 #import "ViewController.h" #import <JavaScriptCore/JavaScriptCore.h> @interface ViewController ()<UITableViewDelegate,UITableViewDataSource> { dispatch_queue_t _jsContextQueue; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; _jsContextQueue = dispatch_queue_create("jscontext", NULL); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_async(_jsContextQueue, ^{ JSContext *jscontent = [[JSContext alloc] init]; [UIWebView class]; }); }); } @end

March 10, 2020 · 1 min · holdsky

apache 配置代理转发时遇到的错误记录

简单的配置代理 # 在 http.conf中设置 #加载 mod_proxy.so LoadModule proxy_module modules/mod_proxy.so #设置代理 ProxyPass /xxx1 https://a.b.c/xxx2 ProxyPassReverse /yyy1 https://a.b.c/yyy2 AH01144 启动后访问服务遇到错误 AH01144: No protocol handler was valid for the URL /yyy1 (scheme 'https'). If you are using a DSO version of mod_proxy, make sure the proxy submodules are included in the configuration using LoadModule. 原因没有我访问的http服务,代理时也需要加载mod_proxy_http.so LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so AH01961 & AH00961 AH01961: SSL Proxy requested for localhost:80 but not enabled [Hint: SSLProxyEngine] AH00961: HTTPS: failed to enable ssl support for 223.71.128.153:443 (yyy2) 如果是用于测试,那么需要配置SSL,以忽略证书问题。 ...

March 3, 2020 · 1 min · holdsky

从源码编译WireGuard for iOS and macOS

1、下载源码 #克隆git仓库 我在gitee上上传了一份代码https://gitee.com/gm958spanda/wireguard-apple.git git clone https://git.zx2c4.com/wireguard-apple #进入git仓库文件夹 cd wireguard-apple 2、设置 apple team ID : #将开发者模版文件复制一份 cp WireGuard/WireGuard/Config/Developer.xcconfig.template WireGuard/WireGuard/Config/Developer.xcconfig #编辑开发者文件(这步骤其实最好用Xcode打开工程文件编辑) $ vim WireGuard/WireGuard/Config/Developer.xcconfig 3、安装swiftlint 和 go #先更改文件的访问权限(安装时需要写权限) sudo chown -R $(whoami) /usr/local/lib /usr/local/lib/pkgconfig /usr/local/lib/python3.7/site-packages #安装 brew install swiftlint go 4、用Xcode编译打包 用Xcode打开工程文件 open ./WireGuard/WireGuard.xcodeproj 然后就是常规的编译打包动作了 文章参考https://git.zx2c4.com/wireguard-apple/about/ 其他平台的客户端参考https://www.wireguard.com/repositories/

February 22, 2020 · 1 min · holdsky

【转】一键安装wireguard的脚本

wireguard是一款vpn服务 wireguard.com是官方 脚本连接如下 可用一: https://github.com/angristan/wireguard-install 可用二: https://github.com/teddysun/across/blob/master/wireguard.sh 客户端安装 客户端参考https://www.wireguard.com/repositories/

February 22, 2020 · 1 min · holdsky

[转]linux上的openvpn server的管理脚本

找个地方记录,有备无患 wget https://git.io/vpn -O openvpn-install.sh && bash openvpn-install.sh 转自https://github.com/Nyr/openvpn-install

February 21, 2020 · 1 min · holdsky

在centos7上安装pritunl--基于OpenVPN协议构建的服务器

我是为了解决访问github越来越不稳定问题。 详细参考https://github.com/pritunl/pritunl 安装 使用包管理器安装 #mongodb源 sudo tee /etc/yum.repos.d/mongodb-org-4.2.repo << EOF [mongodb-org-4.2] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.2/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc EOF #pritunl源 sudo tee /etc/yum.repos.d/pritunl.repo << EOF [pritunl] name=Pritunl Repository baseurl=https://repo.pritunl.com/stable/yum/centos/7/ gpgcheck=1 enabled=1 EOF #安装 sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 7568D9BB55FF9E5287D586017AE645C0CF8E292A gpg --armor --export 7568D9BB55FF9E5287D586017AE645C0CF8E292A > key.tmp; sudo rpm --import key.tmp; rm -f key.tmp sudo yum -y install pritunl mongodb-org #启动父亲 sudo systemctl start mongod pritunl sudo systemctl enable mongod pritunl 从源码安装 如果已经安装了pip,建议先升级pip ...

February 21, 2020 · 2 min · holdsky

JS 将html的5种颜色表示,解析为RRGGBBAA

let colorNameMap = { aliceblue : '#f0f8ff', antiquewhite : '#faebd7', aqua : '#00ffff', aquamarine : '#7fffd4', azure : '#f0ffff', beige : '#f5f5dc', bisque : '#ffe4c4', black : '#000000', blanchedalmond : '#ffebcd', blue : '#0000ff', blueviolet : '#8a2be2', brown : '#a52a2a', burlywood : '#deb887', cadetblue : '#5f9ea0', chartreuse : '#7fff00', chocolate : '#d2691e', coral : '#ff7f50', cornflowerblue : '#6495ed', cornsilk : '#fff8dc', crimson : '#dc143c', cyan : '#00ffff', darkblue : '#00008b', darkcyan : '#008b8b', darkgoldenrod : '#b8860b', darkgray : '#a9a9a9', darkgrey : '#a9a9a9', darkgreen : '#006400', darkkhaki : '#bdb76b', darkmagenta : '#8b008b', darkolivegreen : '#556b2f', darkorange : '#ff8c00', darkorchid : '#9932cc', darkred : '#8b0000', darksalmon : '#e9967a', darkseagreen : '#8fbc8f', darkslateblue : '#483d8b', darkslategray : '#2f4f4f', darkslategrey : '#2f4f4f', darkturquoise : '#00ced1', darkviolet : '#9400d3', deeppink : '#ff1493', deepskyblue : '#00bfff', dimgray : '#696969', dimgrey : '#696969', dodgerblue : '#1e90ff', firebrick : '#b22222', floralwhite : '#fffaf0', forestgreen : '#228b22', fuchsia : '#ff00ff', gainsboro : '#dcdcdc', ghostwhite : '#f8f8ff', gold : '#ffd700', goldenrod : '#daa520', gray : '#808080', grey : '#808080', green : '#008000', greenyellow : '#adff2f', honeydew : '#f0fff0', hotpink : '#ff69b4', indianred : '#cd5c5c', indigo : '#4b0082', ivory : '#fffff0', khaki : '#f0e68c', lavender : '#e6e6fa', lavenderblush : '#fff0f5', lawngreen : '#7cfc00', lemonchiffon : '#fffacd', lightblue : '#add8e6', lightcoral : '#f08080', lightcyan : '#e0ffff', lightgoldenrodyellow : '#fafad2', lightgray : '#d3d3d3', lightgrey : '#d3d3d3', lightgreen : '#90ee90', lightpink : '#ffb6c1', lightsalmon : '#ffa07a', lightseagreen : '#20b2aa', lightskyblue : '#87cefa', lightslategray : '#778899', lightslategrey : '#778899', lightsteelblue : '#b0c4de', lightyellow : '#ffffe0', lime : '#00ff00', limegreen : '#32cd32', linen : '#faf0e6', magenta : '#ff00ff', maroon : '#800000', mediumaquamarine : '#66cdaa', mediumblue : '#0000cd', mediumorchid : '#ba55d3', mediumpurple : '#9370db', mediumseagreen : '#3cb371', mediumslateblue : '#7b68ee', mediumspringgreen : '#00fa9a', mediumturquoise : '#48d1cc', mediumvioletred : '#c71585', midnightblue : '#191970', mintcream : '#f5fffa', mistyrose : '#ffe4e1', moccasin : '#ffe4b5', navajowhite : '#ffdead', navy : '#000080', oldlace : '#fdf5e6', olive : '#808000', olivedrab : '#6b8e23', orange : '#ffa500', orangered : '#ff4500', orchid : '#da70d6', palegoldenrod : '#eee8aa', palegreen : '#98fb98', paleturquoise : '#afeeee', palevioletred : '#db7093', papayawhip : '#ffefd5', peachpuff : '#ffdab9', peru : '#cd853f', pink : '#ffc0cb', plum : '#dda0dd', powderblue : '#b0e0e6', purple : '#800080', rebeccapurple : '#663399', red : '#ff0000', rosybrown : '#bc8f8f', royalblue : '#4169e1', saddlebrown : '#8b4513', salmon : '#fa8072', sandybrown : '#f4a460', seagreen : '#2e8b57', seashell : '#fff5ee', sienna : '#a0522d', silver : '#c0c0c0', skyblue : '#87ceeb', slateblue : '#6a5acd', slategray : '#708090', slategrey : '#708090', snow : '#fffafa', springgreen : '#00ff7f', steelblue : '#4682b4', tan : '#d2b48c', teal : '#008080', thistle : '#d8bfd8', tomato : '#ff6347', turquoise : '#40e0d0', violet : '#ee82ee', wheat : '#f5deb3', white : '#ffffff', whitesmoke : '#f5f5f5', yellow : '#ffff00', yellowgreen : '#9acd32', } // 5种模式 全部转换程 'RRGGBBAA'的小写表示 // 1、颜色名,如'red','black' // 2、三位十六进制,如'#fba' // 3、六位十六进制, 如'#ffbbaa' // 4、rgb ,如rgb(255,220,123) // 5、rgba,如rgba(255,220,123,30) function convertStringToHexColor(colorStr) { let BLACK = '000000ff' if (typeof colorStr !== 'string' || colorStr.length <= 1) { return BLACK; //非法,返回黑色 } colorStr = colorStr.toLowerCase(); if ( colorStr.slice(0,1) === '#') //十六进制 { colorStr = colorStr.slice(1) if(colorStr.length < 4) //三位 FFF { if(colorStr.length < 3){ colorStr = ('000' + colorStr).slice(-3); } return colorStr.charAt(0).concat(colorStr.charAt(0), colorStr.charAt(1), colorStr.charAt(1), colorStr.charAt(2), colorStr.charAt(2), 'ff'); } else //六位 ffbbaa { if(colorStr.length < 3){ colorStr = ('000000' + colorStr).slice(-6); } return colorStr + 'ff'; } } else if ( colorStr.length > 4 && colorStr.slice(0,4) === 'rgba') // rgba(1,2,3,4) { let start = colorStr.indexOf('('); let end = colorStr.indexOf(')'); if (start === -1 || end === -1 || (start+1) >= end ) { return BLACK } rgb = colorStr.slice(start + 1,end).split(','); if (rgb.length !== 4) { return BLACK; } return ''.concat(parseInt(rgb[0]).toString(16).slice(-2), parseInt(rgb[1]).toString(16).slice(-2), parseInt(rgb[2]).toString(16).slice(-2), parseInt(rgb[3]).toString(16).slice(-2)); } else if ( colorStr.length > 3 && colorStr.slice(0,3) === 'rgb' ) //rgb(1,2,3) { let start = colorStr.indexOf('('); let end = colorStr.indexOf(')'); if (start === -1 || end === -1 || (start+1) >= end ) { return BLACK } rgb = colorStr.slice(start + 1,end).split(','); if (rgb.length !== 3) { return BLACK; } return ''.concat(parseInt(rgb[0]).toString(16).slice(-2), parseInt(rgb[1]).toString(16).slice(-2), parseInt(rgb[2]).toString(16).slice(-2), 'ff'); } else if (colorNameMap[colorStr] !== undefined) { colorStr = colorNameMap[colorStr].slice(1); return colorStr + 'ff'; } else{ return BLACK; //非法,返回黑色 } } console.log(convertStringToHexColor('')) console.log(convertStringToHexColor('#')) console.log(convertStringToHexColor('red')) console.log(convertStringToHexColor('#FBA')) console.log(convertStringToHexColor('#FFBBAA')) console.log(convertStringToHexColor('rgb(255,254,253)')) console.log(convertStringToHexColor('rgba(255,254,253,100)'))

February 15, 2020 · 4 min · holdsky

WKWebView : html的input控件的onfocus事件被触发了两次

环境: iOS 13.2 + WKWebView 现象一: 当不添加 viewport 时,onfocus被触发一次 <!DOCTYPE html> <html> <head> <script> function onfocus123(e){ console.log(456); } </script> </head> <body> <span>test_input</span> <input id="test_input" type="text" onfocus="onfocus123(event)"> </body> </html> 第一次点击input输入框第二次点击input输入框输出:456输出: 456 现象一: 当添加 viewport 时,onfocus被触发两次 <!DOCTYPE html> <html> <head> <script> function onfocus123(e){ console.log(456); } </script> <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover,user-scalable=no"> </head> <body> <span>test_input</span> <input id="test_input" type="text" onfocus="onfocus123(event)"> </body> </html> 第一次点击input输入框第二次点击input输入框输出:456 456输出: 456 ...

February 11, 2020 · 1 min · holdsky