GaHingZ 发布的文章

JS异步下载按序执行


前言

前端优化有个原则,叫资源懒加载。

对于某些js资源,我们在页面load前并不需要用到,加载反而会影响到首屏速度。

把这些js放到 load 后进行加载,我们称之为js异步加载。


响应式图片srcset属性学习笔记


介绍

以下参考了MDN,并添加了一些细节和自己的测试结论

<img/>的属性,设置srcset之后不同屏幕密度加载不同图片。

其值为:以逗号分隔的一个或多个字符串列表表明不同User Agent可能设置的图像资源。

每一个字符串由以下组成:

  1. 一个图像的 URL
  2. 可选的描述符,空格后跟以下的其一:
    • 一个宽度(w)描述符,这是一个正整数,后面紧跟 'w' 符号。该整数宽度=sizes属性给出的资源(source)大小*像素密度。该值需配合sizes使用。
    • 一个像素密度描述符,这是一个正浮点数,后面紧跟 'x' 符号。该值其实指的就是dpr。若像素密度描述符直接命中的话,就不考虑sizes和w描述符了,但是建议一般所有字符串仅用其中一个 如果没有指定描述符,那它会被指定为默认的 1x。


yahoo前端优化建议-个人总结


内容

1.尽量减少HTTP请求数

  1. 资源合并
  2. 小图片base64编码
  3. 多图合并为单图
  4. CSS Sprites,通过background-image和background-position属性来定位要显示的部分

2. 减少DNS查找

组件分散到2-4个主机名,这是同时减少DNS查找和允许高并发下载的折中方案

3. 避免重定向

HTTP 重定向是一项用于将客户端重定向到设备专用网址的常用技术。

但采用重定向会增加延迟。

若必须重定向的话,请使用HTTP重定向(响应体为空)并用301(永久转移)或302(暂时转移)状态码。(两者区别在于302会被url拦截,具体百度

因为系统会根据 HTTP 请求标头中的用户代理来执行重定向。 例子:

HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html

若难以实现HTTP重定向,则用js再去做。该做法多了文档下载,js解析的过程。

注意1:有一种常见的极其浪费资源的重定向,就是URL尾部缺少一个斜线的时候。

例如,跳转到http://astrology.yahoo.com/astrology会返回一个重定向到http://astrology.yahoo.com/astrology/的301响应(注意添在尾部的斜线)。

在Apache中可以用Alias,mod_rewrite或者DirectorySlash指令来取消不必要的重定向。

注意2:进行双向重定向与单向重定向

将访问桌面版网页的移动用户重定向到对应的移动版网页 -- “单向”重定向

在移动用户以及桌面设备用户分别访问桌面版网站和移动网站上的网页时,对他们进行重定向 --“双向”重定向

https://developers.google.com/search/mobile-sites/mobile-seo/separate-urls#automatic-redirection


Node子进程执行ping操作,获取统计信息


需求:采用ping -t方式不断进行ping操作,直到收到关闭信号or某个超时时间时结束操作,获取统计信息。

分析:

在cmd窗口进行ping -t操作,会一直进行ping,直到输入ctrl+C会输出ping统计信息。

kill('SIGINT') 即模拟ctrl+C终止进程

编码:

这里我自己手动进行统计信息,原因见下面分析。

var exec = require('child_process').exec;
var iconv = require('iconv-lite');
let ping = exec(`ping www.google.com.hk -t`, { encoding: 'binary'}, function (err, stdout, stderr) {
    let send = 0
    let accept = 0
    let lost = 0
    let min = Infinity
    let max = -Infinity
    let avg = 0
    let Min = (a,b)=>a<b?a:b;
    let Max = (a,b)=>a>b?a:b;

    let str = iconv.decode(new Buffer(stdout, 'binary'), 'GBK')
    console.log(str);
    console.log('=========')
    let regAccept = /来自 .*的回复: 字节=(\d+) 时间=(\d+)ms TTL=(\d+)/g
    let regAll = /\n/g
    send = str.match(regAll).length - 2
    send=send<0?0:send
    let res
    while (res = regAccept.exec(str)) {
        accept++
        let tim = Number(res[2])
        min=Min(tim,min)
        max=Max(tim,max)
        avg= (avg*(accept-1)+tim)/accept
        console.log(res[1], tim, res[3],avg)
    }
    console.log('=========')
    console.log(`发送:${send};接收:${accept};丢失:${send-accept};${(1-(accept/send))*100}%丢失`)
    console.log(`最短:${min}ms;最长:${max}ms;平均:${avg}ms`)
});
ping.on('close', (code) => { console.log('close by', code) })
setTimeout(function () {
    ping.kill('SIGINT')
}, 5 * 1000);

想通过ping.kill('SIGINT')去关闭exec子进程。

测试结果是:输出了close by null后,程序依然再运行,并且没有输出统计信息。


setInterval与settimeout模拟的区别


首先先说明下,node里面的事件循环和浏览器中的是不一致的。

这边浏览器用的是chrome 64

问题1:setInterval(fn,ms)过程,是先把fn放入timer堆,还是先执行?

问题2:setInterval 和 settimeout模拟定时 的使用场景都有哪些?

A1

看了一篇文章,里面讲到node中timers阶段的源码为

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    heap_node = heap_min((struct heap*) &loop->timer_heap);//取出timer堆上超时时间最小的元素
    if (heap_node == NULL)
      break;
    //根据上面的元素,计算出handle的地址,head_node结构体和container_of的结合非常巧妙,值得学习
    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)//如果最小的超时时间比循环运行的时间还要小,则表示没有到期的callback需要执行,此时退出timer阶段
      break;

    uv_timer_stop(handle);//将这个handle移除
    uv_timer_again(handle);//如果handle是repeat类型的,重新插入堆里
    handle->timer_cb(handle);//执行handle上的callback
  }
}

里面说到对于repeat类型的handle(setInterval设置的),是先重新插入再执行callback

( 我这边测试发现chrome 64是这样的,node相反。可能node修改过实现?

测试代码

var speed = 1000
var start = Date.now()
var icounter = 0
var tcounter = 0
//t:ms
function sleep(t) {
    let d = Date.now()
    while (Date.now() - d < t) { }
}
setInterval(function () {
    var time = (Date.now() - start) / 1000
    var avg = ++icounter / time
    console.log('<td>setInterval</td><td>' + icounter + '</td><td>' + time.toFixed(3) + '</td><td>' + avg.toFixed(6) + '</td>')
    sleep(50)
}, speed)

浏览器运行效果:

setInterval=>次数:1   所用时间:1.002
setInterval=>次数:2   所用时间:2.001
setInterval=>次数:3   所用时间:3.000
setInterval=>次数:4   所用时间:4.002
setInterval=>次数:5   所用时间:5.002

node运行效果:

setInterval=>次数:1   所用时间:1.003
setInterval=>次数:2   所用时间:2.060
setInterval=>次数:3   所用时间:3.114
setInterval=>次数:4   所用时间:4.164
setInterval=>次数:5   所用时间:5.215

结果分析:

  1. 浏览器先把fn放入timer堆,再执行

  2. node先执行,再把fn放入timer堆