0%

点击按钮的时候通常会为按钮提供一个按压高亮的处理,这时候我们就会用到 :active 伪类

1
2
3
4
button:active {
background-color: black;
color: white;
}

在安卓手机上当我们按下按钮的时候,:active 的表现和我们的预期是一致的,按钮会呈现按压态。但是在 iOS 设备上却没有效果。

查阅了一下文档,发现上面写着这句话:

[1] Safari Mobile 默认不使用:active 状态,除非元素上或上有一个touchstart 事件处理器。

也就是说在 iOS 设备上只有我们监听了 body 的 touchstart 事件,:active 才会有效果。所以我们这样做:

1
document.body.addEventListener('touchstart', () => {});

添加上述代码后,:active 就可以被正常触发了。

参考资料:
:active on MDN web docs

原理就是创建一个<a> 标签,创建一个 blob 对象,然后用 js 触发点击事件来下载 blob 对象即可。

1
2
3
4
5
6
const content = 'file content'
const aLink = document.createElement('a');
const blob = new Blob([content]);
aLink.download = 'title of file.ext';
aLink.href = URL.createObjectURL(blob);
aLink.click();

关于 blob

最近发现通过我自己搭的 ss 访问 google 也会出现要求人机验证的页面,怀疑是有人在 vps 所在的网段上面搞爬虫什么的,导致 google 将网段加入了黑名单。不过我发现验证页面的 url 都是以 ipv4 开头的,说明可能是 ss 服务器所在的 ipv4 地址被 google 加入了黑名单,但是 ipv6 可能是没有被限制的。我向 ss 服务器的 /etc/hosts 追加如下的配置:

1
2
3
4
5
6
2607:f8b0:4005:801::200e google.com
2607:f8b0:4005:801::200e www.google.com
2404:6800:4005:807::2003 google.com.hk
2404:6800:4005:807::2003 www.google.com.hk
2404:6800:4005:807::2003 google.com.tw
2404:6800:4005:807::2003 www.google.com.tw

这样可以强制使用 ipv6 地址来访问 google。验证码就会消失了。

众所周知,RN 提供了一个远程调试的功能,在调试的过程中可以自动刷新资源,这极大地方便了开发的过程。但这个方便的功能是如何实现的呢?开发机是如何得知需要从哪里拉取资源的呢?这两天正在搞 RN 方面的研究,正好研究了一下这个问题,感觉这是一个很好的调试思路,以后可能会有用,先在这做个记录。

在 react-native-xcode.sh 脚本中使用下面的代码获取了作为 debug server 的电脑的 IP 地址
IP=$(ifconfig | grep 'inet ' | grep -v 127.0.0.1 | cut -d\ -f2 | awk 'NR==1{print $1}')

然后使用如下语句将 IP 地址写入 ip.txt 文件中,然后将这个文件放到 app 的资源目录下

echo "$IP" > "$DEST/ip.txt"

然后使用 Xcode 构建 APP 的时候,这个 ip.txt 文件会作为资源被打包进 app 安装包中。当 ReactNative 程序开始运行的时候在 RCTBundleURLProviderguessPackagerHost 方法中读取这个文件以获取 IP 地址,然后从这个 IP 地址的 debug server 上获取 jsbundle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (NSString *)guessPackagerHost
{
static NSString *ipGuess;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *ipPath = [[NSBundle mainBundle] pathForResource:@"ip" ofType:@"txt"];
ipGuess = [[NSString stringWithContentsOfFile:ipPath encoding:NSUTF8StringEncoding error:nil]
stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
});

NSString *host = ipGuess ?: @"localhost";
if ([self isPackagerRunning:host]) {
return host;
}
return nil;
}

相比 Angular1 而言 Angular2 的变更检查机制更加透明,更加可预测。但是在使用 Angular2 开发的过程中依然存在着一些场景(比如性能优化)需要我们更深入的了解在框架的底层究竟发生了什么。

本文包含如下内容:

  • 变化产生于异步操作
  • 为浏览器打补丁
    • Zone.js
    • 补丁是如何工作的
    • Angular2 中 Zone 的应用
      • 自动检查数据变化
      • 可选的变更检查
  • Angular2.x 变更检查工作原理
    • Angular 的默认变更检查如何工作
    • OnPush 模式
  • 总结
  • 参考资料

本文的描述基于 Angular 2.4.9 版本

Read more »

简介

RxJS is the Reactive-Extensions(ReactiveX) for JS

ReactiveX 由微软发起并推动,是一个为用于实现异步和基于事件编程的程序库制定的一个标准,RxJS 是对这个标准的 Javascript 实现。

RP(Reactive Programming)响应式编程的思路是把随时间不断变化的数据、状态、事件等等转成可被观察的序列(Observable Sequence),然后订阅序列中那些Observable对象的变化,一旦变化,就会执行事先安排好的各种转换和操作。


ReactiveX


Read more »

最近做需求的时候遇到一个坑,对这个问题的思考在这里记录一下

在应用中为了使得数据尽快展示出来,在网络请求的数据回来之前都会先显示内存中缓存的数据。存取缓存的方法大概是这个样子

1
2
3
4
5
6
7
var setCacheInfo = function(key, value) {
key && (cacheWebData[key] = value);
}

var getCacheInfo = function(key) {
return cacheWebData[key];
}

cacheWebData 是一个内存中的对象,读取缓存就是直接反悔了对象中 key 对应的 value。然后将取出来的值进行传递。

先说下问题的现象,在调试的时候我发现,从内存缓存中取出来的值会莫名其妙的改变。比如:在 A 页面中使用key取到的值是{a:1,b:2},但是在进入过 B 页面之后同样使用key取到的值却变成了 {a:1,b:2,c:3},这过程中没有调用setCacheInfo方法来修改缓存中的值。

难不成他还自己变了?

这显然是不可能的,查找了好半天,我发现取出这个缓存的值以后,这个值被传递了好几层,最终被 $.extend(obj,{c:1}) 了一次。这一改不要紧,由于传递的是缓存中对象的引用,这下直接将缓存里面的值也修改了,到下次再使用缓存的时候取出来的值就已经不是原来的值了。然而在 extend 的地方,使用者是完全不知道这个对象引用的是什么地方的数据,这里是一个隐患。

避免出现这个问题有三个办法;

  1. getCacheInfo 方法改为以下的实现
1
2
3
var getCacheInfo = function(key) {
return $.extend(true, {}, cacheWebData[key]);
}

也就是将传出的值进行深拷贝,切断与缓存内部对象的引用关系。
2. 模仿 localStorage 的接口,令缓存对象只接受字符串类型的 value,那么读取方法要这样实现

1
2
3
4
5
6
7
var setCacheInfo = function(key, value) {
key && (cacheWebData[key] = JSON.stringify(value));
}

var getCacheInfo = function(key) {
return JSON.parse(cacheWebData[key]);
}
  1. 直接规定不允许直接 extend 对象…,或者只允许为空对象扩展属性(并不能完全排除隐患)
1
$.extend({}, obj, {c:3})

于是我想看下 jquery,node-cache 之类的框架中是如何处理缓存读写的。
jQuery.data 的实现如下:

1
2
3
get: function (owner, key) {
return (key === undefined) ? (this.cache (owner)): (owner[this.expando] && owner[this.expando][jQuery.camelCase (key)]);
}

同样存在这个问题。

node-cache 的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
exports.get = function(key) {
var data = cache[key];
if (typeof data != "undefined") {
if (isNaN(data.expire) || data.expire >= Date.now()) {
if (debug) hitCount++;
return data.value;
} else {
// free some space
if (debug) missCount++;
size--;
delete cache[key];
}
} else if (debug) {
missCount++;
}
return null;
};

蛤,也有这个问题。。

看来主流框架告诉我们是不应该随意地对对象进行扩展了,不然一不留神就会出现这种问题…想要防患于未然最好还是使用深拷贝或者只存字符串类型值的方法吧。

前言

我们在前端开发中经常需要使用 ChromeDevTool 来进行调试,我想大家经常会遇到这种场景:

  • 这里有一个字符串写错了,改一下就好了
  • 这里加个 if 判断保护一下就好了
  • 这里变量名写错了
    等等

在这些情况下我们往往只需要进行很少量的修改就可以修复一个 bug,但是切到编辑其中修复之后,我们又要跑一遍脚本或者在浏览器中打下一串地址(没有 hot-reload 的项目中),如果是混合移动的开发,我们还需要打包到真机上。这就是一个比较耗时的过程。何况当页面加载出来之后我们还要登陆,恢复现场才能调试一个小小的 bug。如果能在浏览器里面直接改动代码,马上恢复现场跑一下不就可以省去很多麻烦,chrome 的这个功能就会帮到你。

步骤如下

  1. 首先打开 ChromeDevTool,切换到 Sources Tab
  2. 在任意文件上单击右键 Add Folder to Folder,将包含所需要代码文件夹包含到工程中(这时 Chrome 会提示需要获得该文件夹的完全控制权限,这里点击 Allow)
  3. 在任意文件上单击右键,点击 Map to File System Resources
  4. 这时 Chrome 会提示用户重新载入开发者工具,点击 Ok 重新启动开发者工具。
  5. 然后就可以看到目录结构已经变成了我们的本地目录。command+p 搜索文件的时候也可以看到文件的路径变成本地路径了。这时在文件上进行修改,改动就会直接作用于本地的文件。而且修改后的文件可以直接用于调试而无需重新启动应用(浏览器调试和真机调试均是如此)。

yargs(官方地址) 是一个比较好用的 node 命令行参数处理工具。他可以让我们摆脱处理参数的麻烦。最近做一个项目的工作流改进用到了这个库,遇到了一个坑,在这里记录一下。

接受用户输入的参数时候需要对必要的参数进行检查,如果没有输入则需要结束流程,并提示用户传入所需参数。按照 yargs 的文档来看应该这样做

1
2
3
4
5
6
7
var argv = require('yargs')
.usage('Usage: $0 <command> [options]')
.demandOption('p', 'command -p is needed')
.demandCommand(1, 'command -p is needed')
.help('h')
.alias('h', 'help')
.argv;

但是这样做并不起作用,当我们不传参数直接执行命令的时候,依然直接跑了下去。这并不符合我们的预期,在多方查找之后,终于在 yargs 的 issue 里面发现了这个解决方案。

简单来讲,这个解决方案就是叫我们在配置的时候加一个选项

1
.global('p')

这个选项的意思是,在命令执行后继续保留这个参数(蛤?),这样就可以解决这个问题了。

这次记录一个在 Chrome Dev Tool 中直接修改本地的源代码文件的方法。

前言

我们在前端开发中经常需要使用 ChromeDevTool 来进行调试,我想大家经常会遇到这种场景:

  • 这里有一个字符串写错了,改一下就好了
  • 这里加个 if 判断保护一下就好了
  • 这里变量名写错了
    等等

在这些情况下我们往往只需要进行很少量的修改就可以修复一个 bug,但是切到编辑其中修复之后,我们又要跑一遍脚本或者在浏览器中打下一串地址(没有 hot-reload 的项目中),如果是混合移动的开发,我们还需要打包到真机上。这就是一个比较耗时的过程。何况当页面加载出来之后我们还要登陆,恢复现场才能调试一个小小的 bug。如果能在浏览器里面直接改动代码,马上恢复现场跑一下不就可以省去很多麻烦,chrome 的这个功能就会帮到你。

步骤如下

  1. 首先打开 ChromeDevTool,切换到 Sources Tab
  2. 在任意文件上单击右键 Add Folder to Folder,将包含所需代码的文件夹包含到工程中(这时 Chrome 会提示需要获得该文件夹的完全控制权限,这里点击 Allow)
  3. 在任意文件上单击右键,点击 Map to File System Resources
  4. 这时 Chrome 会提示用户重新载入开发者工具,点击 Ok 重新启动开发者工具。
  5. 然后就可以看到目录结构已经变成了我们的本地目录。command+p 搜索文件的时候也可以看到文件的路径变成本地路径了。这时在文件上进行修改,改动就会直接作用于本地的文件。而且修改后的文件可以直接用于调试而无需重新启动应用(浏览器调试和真机调试均是如此)。