2014年5月

浏览器控制台 $ 符号

接上一篇 检查 jQuery 是否 load, 否则重新加载 , 做了一个加载 jQuery 的实验, 找了一个未加载 jQuery 的页面, 再控制台检查 window.jQuery, 显示 undefined, 但是如果直接输出 $, 却是有输出的(默认以 chrome console 为例):

>window.jQuery;
undefined
>$;
function $(selector, [startNode]) { [Command Line API] }

这里就有点迷惑了, 为什么不加载 jQuery, $ 也是 JavaScript 的对象呢? 难道加载了 extJS 等 JavaScript library? 换一个空白页面, 仍然是上面的输出. 真奇了怪了.
那么我们先看看如果加载了 jQuery, 输出又是什么样子呢?

>if(!window.jQuery)
{
   var script = document.createElement('script');
   var protocol = (("https:" == document.location.protocol) ? " https" : " http");
   script.src = protocol + "://code.jquery.com/jquery-1.9.1.min.js";
   script.type = "text/javascript";
   (document.getElementsByTagName('body')[0] || document.getElementsByTagName('head')[0]).appendChild(script);
}
<script src=​" https:​/​/​code.jquery.com/​jquery-1.9.1.min.js" type=​"text/​javascript">​</script>​
>window.jQuery;
function (e,t){return new b.fn.init(e,t,r)} 
>$;
function (e,t){return new b.fn.init(e,t,r)} 

这里 window.jQuery 和 $ 都有了, 值是一样的, 并且 $ 和上面的输出是不一样的.
为了解开迷惑, 就 google 之, 于是发现了这个非常有趣的帖子, 大致翻译过来如下:

问: 几年前, 哥也曾是 JavaScript/jQuery 开发攻城狮, 如今重操旧业. 清醒的记得, $符号是用在 jQuery 的库里面的, 如果不用 jQuery, $这逼, 啥都不是. 
今天, 我打开一个不包含任何 JavaScript 库的空页面, 然后在控制台输出 $, 竟然输出了一些东西, 不是 undefined, 是一个 function. 
1) 以前$符号是没有赋予任何函数的, 难道是我记错了?
2) 如果没有 load jQuery 等类库, 那么$符号代表神马?

答: 
1) 以前你是对的, 现在依然是对的;
2) Firefox and Chrome implement $, $$ and several others as helper commands. Both will set $$ to document.querySelectorAll(), and set $ to document.querySelector if window.$ hasn't been defined.

那么谜底解开, 在 firefox 和 chrome 的控制台, 这些符号默认被赋予了一些特定的函数, 所以即使没有 load jQuery 等类库, 你也可以用他们来操作 DOM.

  1. $() 对于给定的表达式进行评估, 如果找到对应的 element, 就返回第一个元素, 相当于 document.querySelector();
  2. $$() 评估表达式, 返回找到的所有 node 的数组, 相当于 document.querySelectorAll();
  3. $x() 评估 xPath 表达式, 返回找到 node 的数组;

还有更多, 参考官方这里: Firefox, Chrome.

检查 jQuery 是否 load, 否则重新加载

刚才看到一个很好的 javascript 用来检测 jQuery 是否已经 load, 如果没有 load, 那么重新 load. 尤其是某些嵌入式的脚本, 它可能一开始并不知道这一页面有么有 load jQuery.

if(!window.jQuery)
{
   var script = document.createElement('script');
   script.type = "text/javascript";
   script.src = "path/to/jQuery";
   (document.getElementsByTagName('body')[0] || document.getElementsByTagName('head')[0]).appendChild(script);
}

window.jQuery 用来检测当前页面有么有 load jQuery.
document.getElementsByTagName('head')[0] 查看当前 DOM 是不是有 head 元素;
document.getElementsByTagName('body')[0] 查看当前 DOM 是不是有 body 元素;

如果找到(getElementsByTagName 会返回一个数组), 就追加到第一个元素的里面的最后面.

路由器上 nginx 博客添加 fastcgi 缓存

openWRT 的路由器上架上了博客, 虽然路由器CPU, 内存都很弱, 但是假设一个博客系统还过得去. 最近发现一旦访问量大一点, 路由器CPU 就飙的很高.
为了解决这个问题, 在启动 fastcgi 的时候, 已经添加了多进程:

#运行PHP环境
PHP_FCGI_CHILDREN=2
PHP_FCGI_MAX_REQUESTS=30
export PHP_FCGI_CHILDREN
export PHP_FCGI_MAX_REQUESTS
/opt/bin/spawn-fcgi  -a 127.0.0.1 -p 9000 -f /opt/bin/php-fcgi

一开始设置PHP_FCGI_CHILDREN 为4, 但是发现还是有时候 CPU 飙升, 都是 fast_cgi 占用. nginx 的 CPU 占用率都很低. 那么瓶颈在于 php, 就要优化 php 这边.
博客系统的一个特性在于绝大部分只是看, 只有非常少的部分是修改. 那么就可以考虑设置缓存. 这里主要设置 fastcgi 的 cache, 当然也可以设置 nginx 的 cache, 或者其他的. fastcgi 的相对来说, 比较简单.
修改nginx 的http 部分, 添加如下设置:

fastcgi_cache_path /opt/var/nginx/tmp/fastcgi_cache levels=1:2 keys_zone=cache_zone:8m inactive=1d max_size=100m;
fastcgi_temp_path  /opt/var/nginx/tmp/fastcgi_temp;

fastcgi_cache cache_zone;
fastcgi_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
fastcgi_cache_key "$scheme$request_method$host$request_uri$is_args$args";
fastcgi_cache_valid 200 301 302 1d;
fastcgi_ignore_client_abort on;

其中fastcgi_cache_path, 和 fastcgi_temp_path 必须设置在 http 部分, 其他设置可以设置在 server 或者 location 部分, 以缩小范围.
网上有很多教程, 但是比较容易出错的一个点是: fastcgi_ignore_headers, 如果不设置这一项, fastcgi cache 就不起作用, 因为一般的 web http 请求都是使用后面的这些值做缓存的. 参考:这里
设置好之后, nginx -t 测试修改是否正确, 然后重启或者 reload.

那么再重新 top, 启动多线程测试, 你就会发现现在是 nginx 占用 CPU 了, 但是也非常低.
另外要注意的就是 purge, 万一你修改了博客, 或者有用户添加了评论, 那么就需要重新从数据库里拿了, nginx 的 fastcgi 模块有这个指令: fastcgi_cache_purge, 可是它是从version 1.5.7 开始生效的, 所以这里就没配置.

对于某些不想缓存的文档, 可以这么设置:

set $no_cache 0;
if ($request_uri ~* "/(administrator)")
{
    set $no_cache 1;
}
fastcgi_cache_bypass $no_cache;

关于 nginx fastcgi 指令的全部描述, 参看这里