0%

什么是 Lazy Load

LazyLoad 就是懒加载,Angular2 对懒加载提供了很好的支持,只需要在根模块的路由上写一个

1
2
3
4
// app.module.ts
export const ROUTES: Routes = [
{ path: 'reports', loadChildren: '../reports/reports.module#ReportsModule' }
];

只要提供 loadChildren 字段应用初始化的时候就不会加载 report 这个模块,而是会在用到它的时候才加载它,这会缩减应用初始化时需要加载的文件大小,以此来提升应用的启动速度。

Read more »

1. Module version mismatch. Expected 48, got 51

错误原因:编译 hexoNode.js 版本和执行 hexoNode.js 版本不同。
解决办法:进入到 /usr/local/lib/node_modules/hexo/ 中执行 npm rebuild 即可解决

最近换了个新电脑,发现连手机调试 H5 页面的时候,chrome 可以发现设备,但是点击inspect 按钮之后弹出的调试窗口是一个空白页面。

这问题搞得人很懵逼,一通搜索之后发现 chrome 项目中的一个 issue 提到:

致中国同胞:如果你按照要求操作最后却得到一个空窗口,翻墙后再试试。不用谢。

无语,翻墙后搞定。致所有中国同胞,遇事先想想是不是墙挡住了你。

经试验,这个只会在第一次使用 Chrome 调试设备时候出现,应该是第一次调试的时候Chrome会从 Google 那里加载 ChromeDevTool 的页面文件到本地并且将其缓存起来。加载过之后就不会再出现这个情况了。

参见
issue#14

点击按钮的时候通常会为按钮提供一个按压高亮的处理,这时候我们就会用到 :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;
};

蛤,也有这个问题。。

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