Giter Site home page Giter Site logo

blog's People

Contributors

leoyoung07 avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

blog's Issues

TCP Notes

一、关于连接状态与心跳包

主动断开连接

如果一方主动断开连接,另一方尝试接收数据时会抛出异常,例如C#下会抛出SocketException:

ErrorCode: 10054
Message: "远程主机强迫关闭了一个现有的连接。"
SocketErrorCode: ConnectionReset

捕获该异常,可以判断对方是否主动断开连接。

网络异常断开连接

如果是网络异常导致连接断开,接收数据的一方无法得知网络连接状态,这时就需要用心跳机制来解决这个问题。TCP 本身有 Keepalive 选项,但默认关闭,而且检测周期过长(2个小时),因此一般都在应用层自己实现心跳包。心跳包即像心跳一样每隔一段时间向对方发送一个数据包,告诉对方自己连接正常。根据业务场景需要,心跳包可以两边都发送或者由一边主动发送,发送周期可设置在30s左右。如果由客户端主动发心跳包,服务端被动检测连接的周期可
设置为客户端发送周期的两到三倍。

二、关于消息格式

TLV格式

消息格式可基于TLV(Type Length Value)进行设计:

[int32_t]Type(4bytes) | [int32_t]Length(4bytes) | [bytes array]Value |

这种格式较为常见,接收方获得 Type 和 Length 后即可动态创建buffer接收、处理数据。

文本格式

消息格式也可以使用类似http协议的文本格式进行设计:

Header \r\n\r\n Body

文本格式便于扩展和阅读,更加灵活。这也是 UNIX编程艺术 一书中推荐的协议格式。

三、关于字节序

大端序与小端序

不同计算机对多字节整型的存储方式不同:

Some computers put the most significant byte first (known as big-endian order) and others put the least-significant byte first (known as little-endian order).

定义:

如果最低有效位在最高有效位的前面,则称小端序;反之则称大端序

不同计算机可能采用不一样的字节序(主机字节序),而网络传输一般采用大端序(网络字节序)。

主机字节序与网络字节序的转换

C#中的IPAddress类提供了两个方法对主机字节序与网络字节序进行转换:

public static int NetworkToHostOrder(int network)
public static int HostToNetworkOrder(int host)

参考

JavaScript Notes

关于 document.write()

document.write() 可以向文档流注入内容。值得注意的是,对于已关闭的文档流(closed/loaded),调用document.write() 将会自动调用 document.open() , 从而清除文档中已有的内容。因此,在页面内嵌的 <script> 标签中调用document.write() 并不会清除文档内容,因为此时文档流还没有关闭(not loaded)。而在文档加载完成后或者手动调用document.close() 后再调用document.write(),之前文档的内容将被清除。

相关API:document.open() document.close()

参考


关于 jQuery 中的 .attr().prop() 方法

参考


关于 jQuery 中的 .serialize().serializeArray() 方法

注:这两个方法不能获取 disable 的字段值

参考

关于 node.jsWeb Socket 报错 Error: read ECONNRESET

错误信息:

Error: read ECONNRESET
WebSocketServer.js:20
	at exports._errnoException (util.js:1050:11)
	at TCP.onread (net.js:581:26)

错误原因其实是因为没有为 Web Socket 实例添加错误处理。

解决方法:

ws.on('error', (e) => {
  console.log(e);
  //handle error here
});

参考

jQuery 操作 iframe 中元素的方法

参考

计算页面停留时长的方法

;(function () {
  var startTime = Math.ceil(new Date().getTime() / 1000), //单位秒
    getDuration = function () {
      var time = '',
        hours = 0,
        minutes = 0,
        seconds = 0,
        endTime = Math.ceil(new Date().getTime() / 1000),
        duration = endTime - startTime;

      hours = Math.floor(duration / 3600); //停留小时数
      minutes = Math.floor(duration % 3600 / 60); //停留分钟数
      seconds = Math.floor(duration % 3600 % 60); //停留秒数

      time = (hours < 10
        ? '0' + hours
        : hours) + ':' + (minutes < 10
        ? '0' + minutes
        : minutes) + ':' + (seconds < 10
        ? '0' + seconds
        : seconds);

      return time;
    };

  window.onbeforeunload = function (e) {
    var duration = 'time: ' + getDuration();
  };
})();

关于C语言中的getch和getchar函数

最近在阅读K&R的经典之作The C Programming Language(C程序设计语言)时遇到了一个看似简单的函数 getchar() ,书中对其的描述是:

getchar() 函数从文本流中读出下一个输入字符,并将其作为结果值返回。

并给出一段用于文件复制的代码:

#include <stdio.h>

main()
{
    int c;
    while ((c = getchar()) != EOF)
    {
        putchar(c);
    }
}

书中说该例子就是把输入一次一个字符地复制到输出。而我在Ubuntu下和Windows下运行程序时发现每次输入完回车后,程序才会将输入的内容整行输出,即:

abcdef
abcdef

在网上查到, getchar()有缓冲区、有回显的,用户输入的字符(包括回车)被放在键盘缓冲区中,当用户输入回车后 getchar() 函数从缓冲区中每次读入一个字符,后续的 getchar() 不会等待用户按键,而是直接从缓冲区读取字符,直到读完缓冲区中的字符。

有人这样解释书中的描述:

在大师编写C的时候,当时并没有所谓终端输入的概念,所有的输入实际上都是按照文件进行读取的,文件中一般都是以行为单位的。因此,只有遇到换行符,那么程序会认为输入结束,然后采取执行程序的其他部分。同时,输入是按照文件的方式存取的,那么要结束一个文件的输入就需用到EOF (Enf Of File). 这也就是为什么getchar结束输入退出时要用EOF的原因。

而C语言中的 getch() 函数是无缓冲区、无回显的。同时,在Windows下(换行符为 \r\n )调试的过程中发现 getchar()getch() 对于换行符的处理不同: getchar() 返回的的是 \ngetch() 接收到 \r 就返回了。对此也有人这样解释:

因为getchar()是标准的C库函数,在UNIX系统里回车一律只用\n。
Windows里回车输入的其实是\r\n,但是C库会对其进行处理(读写文本文件时也会),统一转换为规范的\n。而getch()则是比较低级的,会越过这个处理,貌似没有经过输入流缓冲,而是直接调用键盘服务中断获得的,就是原始按键。

另外,程序中使用 int c 而不是 char c 的原因是

EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不正确的。

JavaScript中的求模和求余

求模与求余

求模和求余其实是两种不同的运算。当两个操作数同号时,它们的运算结果相同;而当两个操作数异号时,它们的运算结果就不同了。

求模与求余运算都分两步进行:

  1. 求整数商:c = a/b;
  2. 求值: r = a - c*b;

求模和求余的差别在第一步:

  • 求模是向负无穷的方向舍弃小数位((-1)/3 = -1)
  • 求余是向0的方向舍弃小数位((-1)/3 = 0)

JavaScript中的求余

JavaScript中的 % 运算符其实是求余运算,即:

console.log((-13) % 64);

结果是-13。

JavaScript中的求模

JavaScript中的求模运算可以采用以下方法实现:

Number.prototype.mod = function (n)
{
    return ((this % n) + n) % n;
}

此时,用:

console.log((-13).mod(64));

结果是51。

Wechat Dev Notes

记录微信(公众号/网页/小程序)开发中的一些坑和填坑方法

关于网页字体缩放

微信中允许用户调整网页字体大小,而当页面使用rem作为单位进行布局时,用户调整字体大小就会导致页面布局变乱。

  • Android解决方法
/*
  * 页面加入这段代码可使Android机器页面不再受到用户字体缩放强制改变大小
  * 但是会有一个1秒左右的延迟,期间可以考虑通过loading展示
  * 仅供参考
  */
(function(){
  function resetFontSize () {
    setTimeout(function(){
      // 设置网页字体为默认大小
      WeixinJSBridge.invoke('setFontSizeCallback', {'fontSize': 0});
    },0);
    // 重写设置网页字体大小的事件
    WeixinJSBridge.on('menu:setfont', function () {
      WeixinJSBridge.invoke('setFontSizeCallback', {'fontSize': 0});
    });
  }
  if (typeof WeixinJSBridge === 'undefined') {
    document.addEventListener('WeixinJSBridgeReady', function (e) {
      resetFontSize();
    });
  } else {
    resetFontSize();
  }
})();
  • iOS解决方法
body {
  -webkit-text-size-adjust: 100% !important;
  text-size-adjust: 100% !important;
  -moz-text-size-adjust: 100% !important;
}

参考

关于UTF-8 BOM

什么是 BOM

wikipedia上的解释:

The byte order mark (BOM) is a Unicode character, U+FEFF byte order mark (BOM), whose appearance as a magic number at the start of a text stream can signal several things to a program consuming the text:

  • What byte order, or endianness, the text stream is stored in;
  • The fact that the text stream is Unicode, to a high level of confidence;
  • Which of several Unicode encodings that text stream is encoded as.

BOM use is optional, and, if used, should appear at the start of the text stream.

简单的说就是放在文件开头,用于标明字节序的标记。

C#中的UTF-8 BOM

C#中使用Encoding.UTF8作为参数创建文件是带有BOM的。

//UTF-8 with BOM
new StreamWriter("text.txt", Encoding.UTF8);

创建不带BOM的UTF-8文件的方法:

//UTF-8 without BOM
new StreamWriter("text.txt", new UTF8Encoding(false));

参考

添加页面路由

需求说明

添加页面路由,实现在不同的页面间进行导航。

实现方案

利用vue-router实现页面路由

关于HTTP缓存

1. HTTP头部与缓存相关的字段

字段 字段说明 字段类型 HTTP版本
Cache-control Specifies directives for caching mechanisms in both requests and responses General header 1.1
ETag a unique string identifying the version of the resource Response header 1.1
If-Match Makes the request conditional and applies the method only if the stored resource matches one of the given ETags Request header 1.1
If-None-Match Makes the request conditional and applies the method only if the stored resource doesn't match any of the given ETags Request header 1.1
If-Modified-Since Makes the request conditional and expects the entity to be transmitted only if it has been modified after the given date Request header 1.0+
If-Unmodified-Since Makes the request conditional and expects the entity to be transmitted only if it has not been modified after the given date Request header 1.1
Last-Modified the last modification date of the resource, used to compare several versions of the same resource Entity header 1.0+
Vary Determines how to match future request headers to decide whether a cached response can be used rather than requesting a fresh one from the origin serve Response header 1.1
Age The time in seconds the object has been in a proxy cache Response header 1.1
Date contains the date and time at which the message was originated General header 1.0+
Expires The date/time after which the response is considered stale Entity header 1.0+
Pragma Implementation-specific header that may have various effects anywhere along the request-response chain General header 1.0

2. 缓存控制

HTTP 头部的 cache-control 字段主要用于指定缓存控制相关的指令。

请求中可用的指令

Cache-Control: max-age=<seconds>

Cache-Control: max-stale[=<seconds>]

Cache-Control: min-fresh=<seconds>

Cache-Control: no-cache

Cache-Control: no-store

Cache-Control: no-transform

Cache-Control: only-if-cached

响应中可用的指令

Cache-Control: must-revalidate

Cache-Control: no-cache

Cache-Control: no-store

Cache-Control: no-transform

Cache-Control: public

Cache-Control: private

Cache-Control: proxy-revalidate

Cache-Control: max-age=<seconds>

Cache-Control: s-maxage=<seconds>

是否可以缓存

  • no-cache: 在使用缓存前,强制要求向源服务器发送验证请求。

  • no-store: 所有内容都不会被缓存。

  • only-if-cached: 表明客户端只想要获取缓存的数据,不向源服务器发送验证请求。

Cache-Control: publicCache-Control: private 两个指令用于表明缓存可以保存的位置。

  • private: Private browser caches, 即只有客户端(浏览器)可以缓存

  • public: Shared proxy caches, 即客户端(浏览器)和代理服务器都可以缓存

3. 缓存过期

  • Expires: HTTP 1.0版本的字段,表明资源过期的服务器时间。如果头部包含Cache-Control: max-age=<seconds> 指令,Expires将被忽略。

  • Cache-Control: max-age=<seconds>: 资源将会过期的时间长度,与Expires不同,是相对时间(长度)。

  • Cache-Control: must-revalidate: 缓存的资源在使用前必须向服务器验证其是否过期,而且过期的资源将不会被使用。

  • Last-Modified: 资源上次修改的服务器时间。用于确认缓存的资源与当前的资源是否相同,它没有ETag字段精确。If-Modified-SinceIf-Unmodified-Since 字段与它配合使用。

  • If-Modified-Since: 如果资源在该字段给定的时间后没有修改过,服务器返回 304 状态,并且不带有数据;否则返回 200 状态,并且带上请求的资源。当与 If-None-Match 字段一起使用并且服务器支持 If-None-Match 字段时,它将被忽略。

4. 缓存对比

  • ETag: 用于标定资源版本的标识。与 If-MatchIf-None-Match 字段配合使用。

  • If-None-Match: 如果服务器资源的版本号与该字段指定的ETag一致,则返回 304 状态;否则返回 200 状态,并且带上请求的资源。

5. 参考

CSS Notes

关于CSS中的百分比参照

参照物 属性
父盒子对应属性 width, height, font-size 等
父盒子宽度 text-indent, padding, margin 等
盒子自身宽高 border-radius, background-size, border-image-width, transform: translate(), scale(), transform-origin, zoom, clip-path 等
盒子自身字号 line-height 等
定位元素 top, right, bottom, left 等
特殊算法 background-position (原盒子的宽高值减去背景图片的宽高值所得到的剩余值), border-image-slice (相对于图片尺寸), filter 系列函数等

参考

关于 PXEMREM

参考

添加Activities页面

需求说明

添加Activities页面,主要内容:

- [ ] 展示个人基本信息

  • Contributions/Punch Card 图表/Timeline

一些好用的HTTP调试工具

一、发送请求

cURL

cURL 是Linux下的命令行工具,windows 下可通过MinGW安装(安装Git for windows即可)。

基本用法:

  • GET
curl "http://www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK"
  • POST
# application/x-www-form-urlencoded
curl --data "birthyear=1905&press=%20OK%20"  http://www.example.com/when.cgi
  • HEAD
curl --head "http://www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK"

HTTPie

HTTPie 是使用python开发的命令行工具,可用pip安装。

基本用法:

http [flags] [METHOD] URL [ITEM [ITEM]]

Postman/Restlet Client

Postman 和 Restlet Client都是Chrome下的扩展(应用),具有图形化界面,很容易使用。

二、抓包(未完待续。。。)

Fiddler/Charles

Wireshark

Chrome dev-tools

TcpDump

参考

PHP curl post 数组与 post query字符串的区别

使用PHP的curl方法post数据时通常使用以下方法:

$post_fields = ["aaa"=>"aaaa", "bbb"=>"bbbb"];
$url = "http://localhost/sandbox/PHPTest/output.php";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
echo $response;

$url为php页面时,该方法没有遇到问题,但$url为jsp页面时,出现post数据为空的情况,即在jsp页面用request.getParameter("aaa")得到的结果为null。尝试将$post_fields拼接为query字符串:

$post_fields = http_build_query($post_fields);

结果正确。
抓包对比直接post数组与post query字符串两种方法结果如下:

  • 直接post数组

    • header

      array header

    • body

      array body

  • post query字符串

    • header

      query string header

    • body

      query string body

可见,直接post数组时,Content-type为multipart/form-data;而post query字符串时Content-type为application/x-www-form-urlencoded

PHP手册中对CURLOPT_POSTFIELDS的说明如下:

全部数据使用HTTP协议中的"POST"操作来发送。要发送文件,在文件名前面加上@前缀并使用完整路径。这个参数可以通过urlencoded后的字符串类似'para1=val1&para2=val2&...'或使用一个以字段名为键值,字段数据为值的数组。如果value是一个数组,Content-Type头将会被设置成multipart/form-data。

关于浏览器的回流与重绘

重绘

当元素可见的样式改变而不影响其布局时,会引起浏览器的重新绘制(重绘)。

回流

当元素的尺寸、结构等发生改变时,浏览器会重新渲染部分或全部文档(回流)。

对页面性能的影响

  • 回流对页面性能的影响比重绘要大

  • 回流会引起重绘,重绘不一定会引起回流

引起回流的原因

  1. 页面首次渲染
  2. 浏览器窗口大小发生改变
  3. 元素尺寸或位置发生改变
  4. 元素内容变化(文字数量或图片大小等等)
  5. 元素字体大小变化
  6. 添加或者删除可见的DOM元素
  7. 激活CSS伪类(例如::hover)
  8. 查询某些属性或调用某些方法:
    clientWidthclientHeightclientTopclientLeft
    offsetWidthoffsetHeightoffsetTopoffsetLeft
    scrollWidthscrollHeightscrollTopscrollLeft
    scrollIntoView()scrollIntoViewIfNeeded()
    getComputedStyle()
    getBoundingClientRect()
    scrollTo()

优化手法

  • CSS
  1. 避免使用table布局。
  2. 尽可能在DOM树的最末端改变class。
  3. 避免设置多层内联样式。
  4. 将动画效果应用到position属性为absolute或fixed的元素上。
  5. 避免使用CSS表达式(例如:calc())。
  • JavaScript
  1. 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  2. 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  3. 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  4. 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  5. 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

参考

HTML Notes

关于HTML的表单提交

参考


关于 DOM 中的 NodeHTMLElement

参考


关于 innerText()textContent()

参考

关于 Node.contains()

参考

关于DOM元素ID成为window属性

参考

关于 document.designModedocument.execCommand()

参考

关于 HTML 文本选择

参考

关于 HTML Entities

参考

关于JavaScript模块化

AMD

Asynchronous Module Definition(异步模块定义),通过 define() 方法定义模块:

define(id?, dependencies?, factory);

通过 require() 方法引用模块:

require([module], callback);

举例:

// filename: foo.js
define('foo', ['jquery', 'underscore'], function ($, _) {
  // methods
  function a(){};    // private because it's not returned (see below)
  function b(){};    // public because it's returned
  function c(){};    // public because it's returned

  // exposed public methods
  return {
      b: b,
      c: c
  }
});
require(['foo'], function(foo) {

});

CMD

CommonJS规范,在node.js中使用,一个文件就是一个模块。通过 exports 导出模块内容:

// filename: foo.js
var bar = 'bar'; // private
// public
function foo () {
}
exports.foo = foo;

通过 require 引入模块内容:

var foo = require('./foo').foo;

UMD

Universal Module Definition(通用模块定义),支持AMD和CMD:

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(['jquery', 'underscore'], factory);
  } else if (typeof exports === 'object') {
    // Node, CommonJS-like
    module.exports = factory(require('jquery'), require('underscore'));
  } else {
    // Browser globals (root is window)
    root.returnExports = factory(root.jQuery, root._);
  }
}(this, function ($, _) {
  // methods
  function a(){}; // private because it's not returned (see below)
  function b(){}; // public because it's returned
  function c(){}; // public because it's returned

  // exposed public methods
  return {
    b: b,
    c: c
  }
}));

import/export

ES6 中支持的模块定义方法,一个模块就是一个独立的文件。通过 export 导出模块内容;

// filename: foo.js
const bar = 'bar'; // private
// public
function foo () {
}
export {foo};

通过 import 引入模块内容:

import {foo} from './foo';

参考

Vue中点击元素外部隐藏元素的实现方法

在弹窗等组件中,通常需要实现点击组件外部隐藏该组件的功能,下面整理一下常用的实现方式。

实现一: 添加遮罩层( overlay )

在弹出的目标元素外层添加透明或半透明的遮罩层,可设置样式:

position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;

监听遮罩层的 click 事件,通过 e.target 判断点击位置,从而决定是否隐藏弹出的元素。但是如果不方便为目标元素添加遮罩层,这种方式就不适用。

实现二: 监听 document 的点击事件

在Vue的hooks中监听 document 元素的 click 事件,同样通过 e.target 判断点击位置,决定是否隐藏目标元素。

methods: {
    handleBodyClick(){
        if (目标区域出来了) {
          let _con = $(目标区域)
          if (!_con.contains(e.target)) {
            // 点击目标区域外的时候关闭目标区域
          }
        }
    },
},
mounted () {
  document.addEventListener('click', this.handleBodyClick)
},
destroyed () {
  document.removeEventListener('click', this.handleBodyClick)
}

实现三: Vue的自定义指令( Custom Directives )

其实这种方式也是监听 document 的点击事件,只不过是利用Vue的自定义指令( Custom Directives )来实现。这也是 iView 中的实现方式。

Vue.directive('clickoutside', {
  bind (el, binding, vnode) {
    function documentHandler (e) {
      if (el.contains(e.target)) {
        return false;
      }
      if (binding.expression) {
        binding.value(e);
      }
    }
    el.__vueClickOutside__ = documentHandler;
    document.addEventListener('click', documentHandler);
  },
  unbind (el, binding) {
    document.removeEventListener('click', el.__vueClickOutside__);
    delete el.__vueClickOutside__;
  }
});

之后在组件上用 v-clickoutside="handleClose" 处理外部点击事件,就可以实现关闭/隐藏目标元素。

参考

关于CSS中的伪类和伪元素

定义

  • 伪类:添加到某些选择器,用于向元素的特定状态设置样式。

  • 伪元素:添加到某些选择器,用于向元素的特定部分设置样式。

区别

  • 伪类是针对元素的特定状态,伪元素是针对元素的特定部分

  • 伪元素在DOM树中创建了一些抽象元素

  • 语法区别:

    伪类的语法:

    selector:pseudo-class {
      property: value;
    }

    伪元素的语法:

    selector::pseudo-element {
      property: value;
    }

属性

常用的伪类

名称 说明
:active 向被激活的元素添加样式。
:focus 向拥有键盘输入焦点的元素添加样式。
:hover 当鼠标悬浮在元素上方时,向元素添加样式。
:link 向未被访问的链接添加样式。
:visited 向已被访问的链接添加样式。
:first-child 向元素的第一个子元素添加样式。
:lang 向带有指定 lang 属性的元素添加样式。
:nth-child(an+b) 匹配同系列兄弟节点中的位于 an+b 位置的元素。
:nth-of-type(an+b) 匹配那些在它之前有 an+b-1 个相同类型兄弟节点的元素。

常用的伪元素

名称 说明
::first-letter 向文本的第一个字母添加特殊样式。
::first-line 向文本的首行添加特殊样式。
::before 在元素之前添加内容。
::after 在元素之后添加内容。
::selection 给选中的内容添加样式。
::placeholder 给 placeholder 添加样式。

备注

As a rule, double colons (::) should be used instead of a single colon (:). This distinguishes pseudo-classes from pseudo-elements. However, since this distinction was not present in older versions of the W3C spec, most browsers support both syntaxes for the original pseudo-elements.

参考

关于CSS盒模型

四个边

  1. 外边距边
  2. 边框边
  3. 内填充边
  4. 内容边

四个区域

  1. 内容区域(content area)
    包含元素真实内容的区域,位于内容边界的内部。它的宽度:content-box width,它的高度:content-box height。

  2. 内边距区域(padding area)
    延伸到包围padding的边框,内容区域的背景、颜色或者图片会延伸到padding上,它位于内边距边界内部。它的宽度:padding-box width,它的高度:padding-box height。

  3. 边框区域(border area)
    是包含边框的区域,它位于边框边界内部。它的宽度:border-box width,它的高度:border-box height,由属性 border-width 及border控制。

  4. 外边距区域(margin area)
    用空白区域扩展边框区域,以分开相邻的元素。它的宽度:margin-box width,它的高度:margin-box height。

图示

参考

关于浏览器中的Observer API

现代浏览器支持4种Observer API:

  1. Intersection Observer
  2. Mutation Observer
  3. Resize Observer
  4. Performance Observer

Intersection Observer

Intersection Observer 可以监视DOM元素之间的交集。可用于监控元素进入、离开视区。可用来实现图片的懒加载、确定广告可见度、实现无限滚动的页面、实现元素进入视区时自动播放视频或动画。

Mutation Observer

Mutation Observer 可以监视DOM元素的改变,包括元素的添加、删除,元素值或属性的改变。主要用在JS框架中,用于响应DOM元素的变化。

Resize Observer

Resize Observer 可以监视DOM元素内容矩形(content box)的尺寸(宽度、高度)变化。类似于 document.onresize()window.resize() 方法。 当元素被添加到DOM或从DOM移除、display 被设置为 none、被设置为显示且尺寸不为0时都会触发该监听事件。

Performance Observer

Performance Observer 可监视调用 performance API记录性能指标的情况。用于监听markmeasurepaint等类型的性能记录。

参考

[转]从浏览器导出微信公众号关注用户的方法

登录微信后台,在浏览器console中执行:

var sc=document.createElement("script");sc.type="text/javascript";
sc.onload=function(){export_userlist();};
sc.src="https://dn-html.qbox.me/wxmp.js?_="+Date.now();
document.getElementsByTagName('HEAD').item(0).appendChild(sc);

wxmp.js:

var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var base64DecodeChars = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
/**
 * base64编码
 * @param {Object} str
 */
function base64encode(str) {
  var out,
    i,
    len;
  var c1,
    c2,
    c3;
  len = str.length;
  i = 0;
  out = "";
  while (i < len) {
    c1 = str.charCodeAt(i++) & 0xff;
    if (i == len) {
      out += base64EncodeChars.charAt(c1 >> 2);
      out += base64EncodeChars.charAt((c1 & 0x3) << 4);
      out += "==";
      break;
    }
    c2 = str.charCodeAt(i++);
    if (i == len) {
      out += base64EncodeChars.charAt(c1 >> 2);
      out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
      out += base64EncodeChars.charAt((c2 & 0xF) << 2);
      out += "=";
      break;
    }
    c3 = str.charCodeAt(i++);
    out += base64EncodeChars.charAt(c1 >> 2);
    out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
    out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
    out += base64EncodeChars.charAt(c3 & 0x3F);
  }
  return out;
}
/**
 * base64解码
 * @param {Object} str
 */
function base64decode(str) {
  var c1,
    c2,
    c3,
    c4;
  var i,
    len,
    out;
  len = str.length;
  i = 0;
  out = "";
  while (i < len) {
    /* c1 */
    do {
      c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
    } while (i < len && c1 == -1);
    if (c1 == -1) 
      break;
    
    /* c2 */
    do {
      c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
    } while (i < len && c2 == -1);
    if (c2 == -1) 
      break;
    out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
    /* c3 */
    do {
      c3 = str.charCodeAt(i++) & 0xff;
      if (c3 == 61) 
        return out;
      c3 = base64DecodeChars[c3];
    } while (i < len && c3 == -1);
    if (c3 == -1) 
      break;
    out += String.fromCharCode(((c2 & 0 XF) << 4) | ((c3 & 0x3C) >> 2));
    /* c4 */
    do {
      c4 = str.charCodeAt(i++) & 0xff;
      if (c4 == 61) 
        return out;
      c4 = base64DecodeChars[c4];
    } while (i < len && c4 == -1);
    if (c4 == -1) 
      break;
    out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
  }
  return out;
}
/**
 * utf16转utf8
 * @param {Object} str
 */
function utf16to8(str) {
  var out,
    i,
    len,
    c;
  out = "";
  len = str.length;
  for (i = 0; i < len; i++) {
    c = str.charCodeAt(i);
    if ((c >= 0x0001) && (c <= 0x007F)) {
      out += str.charAt(i);
    } else if (c > 0x07FF) {
      out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
      out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
    } else {
      out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
    }
  }
  return out;
}
/**
 * utf8转utf16
 * @param {Object} str
 */
function utf8to16(str) {
  var out,
    i,
    len,
    c;
  var char2,
    char3;
  out = "";
  len = str.length;
  i = 0;
  while (i < len) {
    c = str.charCodeAt(i++);
    switch (c >> 4) {
      case 0:
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
        // 0xxxxxxx
        out += str.charAt(i - 1);
        break;
      case 12:
      case 13:
        // 110x xxxx 10xx xxxx
        char2 = str.charCodeAt(i++);
        out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
        break;
      case 14:
        // 1110 xxxx10xx xxxx10xx xxxx
        char2 = str.charCodeAt(i++);
        char3 = str.charCodeAt(i++);
        out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
        break;
    }
  }
  return out;
}
// demo function doit(){    var f = document.f;    f.output.value =
// base64encode(utf16to8(f.source.value));    f.decode.value =
// utf8to16(base64decode(f.output.value)); }

function UserListExporter() {
  var exp = {
    users: []
  };
  exp.add = function (lst) {
    $
      .each(lst, function (index, val) {
        if (!val.user_openid) {
          val.user_openid = val.id;
          val.user_name = val.nick_name;
          val.user_remark = val.remark_name;
          val.user_create_time = val.create_time;
        }
        exp
          .users
          .push(val);
        exp.lastuser = val;
      });
  };
  exp.load = function (lastuser, cbk) {
    console.log("load from ", lastuser.user_openid);
    $.ajax({
      url: 'https://mp.weixin.qq.com/cgi-bin/user_tag',
      data: {
        action: 'get_user_list',
        groupid: '-2',
        begin_openid: lastuser.user_openid,
        begin_create_time: lastuser.user_create_time,
        limit: '100',
        offset: '0',
        backfoward: '1',
        token: wx.data.t,
        lang: 'zh_CN',
        f: 'json',
        ajax: '1',
        random: Date.now()
      },
      success: function (res) {
        if (cbk) {
          cbk(res.user_list && res.user_list.user_info_list
            ? res.user_list.user_info_list
            : null);
        }
      },
      error: function () {
        console.log("擦,出错了~~~");
      }
    });
  };
  exp.next = function (cbk) {
    exp.load(exp.lastuser, cbk);
  };
  exp.loadall = function () {
    exp.users = [];
    exp.add(wx.cgiData.user_list);
    exp.task = function () {
      exp
        .next(function (lst) {
          if (lst) {
            exp.add(lst);
            exp.task();
          } else {
            console.log("finish!");
            var ls = [];
            $.each(exp.users, function (ix, user) {
              ls.push(user.user_openid + "\t" + user.user_name.replace(/<span.*\/span>/g, '') + "\t" + user.user_create_time);
            });
            var txt = ls.join("\r\n");
            window.open("data:text/plain;charset=utf-8;base64," + base64encode(utf16to8(txt)));
            // window.open("data:text/plain;charset=utf-8,"+txt);
          }
        });
    };
    exp.task();
  };

  return exp;
}

function export_userlist() {
  window.exp = UserListExporter();
  exp.loadall();
}

参考

关于HTTP Session的实现方式

基于Cookie的实现

服务器启用Session后,约定一个Cookie的键值作为Session的口令(例如 session_id)。如果用户请求的Cookie中没有改键值,服务器就生成一个唯一的值,添加到Cookie中。

基于URL的实现

将Session的键值存储在URL的查询字符串中,如果用户请求的URL中不带有约定的Session参数(session_id),服务器就生成一个唯一的值添加到URL的查询字符串中,并将请求重定向(302)到修改后的URL。这种实现的问题在于,如果别人用同样的URL进行请求,就会带上相同的session_id,造成用户信息泄露的风险。

基于ETag字段的实现

这种方式利用HTTP头部的ETag字段存储session_id,实现Session降级。在页面中加入一个动态生成的js文件(例如etag_session.js),用户向服务器请求该文件时,服务器检查请求中是否带有if-none-match字段,如果没有则生成一个唯一的值,添加到响应头部的ETag字段中。

参考

首页Navigation bar移动设备显示优化

需求说明

Title和Navigation bar 在移动设备上显示过大。

改为响应式布局,在移动设备上显示为可收起的Navigation drawer。

实现方案

利用Vuetify的Visibility属性实现

hidden-sm-and-down - hidden on small viewports and down
hidden-md-and-up - hidden on medium viewports and up

关于函数节流(throttle)与函数去抖(debounce)

概念

  • 函数节流(throttle)

    设定一个调用周期n,当距离上次执行该动作的时间大于等于调用周期n,才执行该动作。类比咖啡机按钮,按一次出咖啡,但在出咖啡的期间再次按则无效。

  • 函数去抖(debounce)

    设定一个调用周期n,当调用动作的时间超过周期n,才会执行该动作,若在周期n内又调用此动作则将重新计算执行时间。类比电梯按钮,门关上前,每次按下按钮,电梯门都延迟一段时间才关闭。

简单实现

  • throttle
/**
*
* @param fn {Function}   实际要执行的函数
* @param delay {Number}  执行间隔,单位是毫秒(ms)
*
* @return {Function}     返回一个“节流”函数
*/

function throttle(fn, threshhold) {

  // 记录上次执行的时间
  var last

  // 定时器
  var timer

  // 默认间隔为 250ms
  threshhold || (threshhold = 250)

  // 返回的函数,每过 threshhold 毫秒就执行一次 fn 函数
  return function () {

    // 保存函数调用时的上下文和参数,传递给 fn
    var context = this
    var args = arguments

    var now = +new Date()

    // 如果距离上次执行 fn 函数的时间小于 threshhold,那么就放弃
    // 执行 fn,并重新计时
    if (last && now < last + threshhold) {
      clearTimeout(timer)

      // 保证在当前时间区间结束后,再执行一次 fn
      timer = setTimeout(function () {
        last = now
        fn.apply(context, args)
      }, threshhold)

    // 在时间区间的最开始和到达指定间隔的时候执行一次 fn
    } else {
      last = now
      fn.apply(context, args)
    }
  }
}
  • debounce
/**
 *
 * @param fn {Function}   实际要执行的函数
 * @param delay {Number}  延迟时间,单位是毫秒(ms)
 *
 * @return {Function}     返回一个“防反跳”了的函数
 */

function debounce(fn, delay) {

  // 定时器,用来 setTimeout
  var timer

  // 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 fn 函数
  return function () {

    // 保存函数调用时的上下文和参数,传递给 fn
    var context = this
    var args = arguments

    // 每次这个返回的函数被调用,就清除定时器,以保证不执行 fn
    clearTimeout(timer)

    // 当返回的函数被最后一次调用后(也就是用户停止了某个连续的操作),
    // 再过 delay 毫秒就执行 fn
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}

参考

SQL Server 从网络驱动器附加数据库的方法

  1. 映射网络驱动器

    在资源管理器中添加网络驱动器,例如:\\RemoteServerName\ShareName

  2. 在 SQL Server 中启用 xp_cmdshell

    EXEC sp_configure 'show advanced options', 1;
    GO
    RECONFIGURE;
    GO
    
    EXEC sp_configure 'xp_cmdshell',1
    GO
    RECONFIGURE
    GO
  3. 在 SQL Server 中添加网络驱动器

    EXEC XP_CMDSHELL 'net use H: \\RemoteServerName\ShareName'
  4. [可选]查看添加的网络驱动器

    EXEC XP_CMDSHELL 'Dir H:'
  5. [可选]附加完成后在SQL Server中删除网络驱动器

    EXEC XP_CMDSHELL 'net use H: /delete' 
  • 注:对于 SQL Server2008 及更低的版本,需要为SQL Server服务添加 -T1807 的启动参数,以允许从网络驱动器附加数据库。添加参数后需重启SQL Server服务。添加方法:
  1. In SQL Server Configuration Manager, click SQL Server Services.
  2. In the right pane, right-click SQL Server (<instance_name>), and then click Properties.
  3. On the Advanced tab, in the Startup Parameters box, type the parameters separated by semicolons (;).

参考

Blog迁移到GitHub issues啦

迁移步骤

  • 把之前的blog内容放到issues里
  • 为blog创建labels
  • 用milestone按月分类和记录进度
  • 用GitHub API读取issues数据,创建blog首页
  • 用GitHub API读取issues数据,创建本地blog备份

Blog 首页滚动性能优化

需求说明

对首页的scroll事件监听 导致在手机上滚动页面时出现卡顿现象。

解决方案

通过在scroll事件中增加 去抖函数(debounce) 节流函数(throttle),优化页面滚动。

Linux Shell Notes

关于 kill 命令与常用的信号列表

kill 的用法

kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]

常用信号

信号 信号数值 信号说明
SIGHUP 1 终端断线
SIGINT 2 中断(同 Ctrl + C),与 SIGTERM 类似
SIGQUIT 3 退出(同 Ctrl + \),进程退出时会产生 core 文件(dump core)
SIGKILL 9 强制终止,本信号不能被阻塞、处理和忽略
SIGTERM 15 终止,该信号可以被阻塞和处理
SIGCONT 18 继续(与 STOP 相反)
SIGSTOP 19 暂停(同 Ctrl + Z)

Tips

  • init进程是不可杀的。init进程是由内核启动的用户级进程,它始终是第一个进程(其进程编号始终为1)。 其它所有进程都是init进程的子孙。

  • 使用 kill 0 来终止所有由当前shell启动的进程,包括后台进程。

  • kill -0 pid 用于检测是否有权限给进程发送信号。例如:$ if ! kill -0 $(pgrep sleep); then echo "You're weak!"; fi

参考

bash 下常用的组合键

组合键 说明
Ctrl + C 终止目前的命令
Ctrl + D 输入结束 (EOF)
Ctrl + M 相当于 Enter
Ctrl + S 暂停屏幕的输出
Ctrl + Q 恢复屏幕的输出
Ctrl + Z 暂停目前的命令

参考

让进程在后台可靠运行的几种方法

参考

JSONP与跨域问题

跨域问题

跨域问题是由于浏览器的同源策略而产生的,同源是指同协议同域名同端口,出于安全的考虑,浏览器禁止脚本的跨域(不同源)请求。

虽然浏览器禁止跨域请求,但是却允许引用和执行来自其他域的脚本文件,这也就是下面将要提到的 JSONP 的原理。

JSONP

JSONP(JSON with Padding) 是一种动态加载js的技术,即使用javascript脚本动态加载<script>标签,从而使得网页可以跨域获取数据。该方法需要服务器端进行配合,动态生成javascript脚本,javascript脚本中包含网页所需的数据。

假设 www.bbbbb.com.cn 域名下有一网页需要请求 www.aaaaa.com.cn 域名下的数据,利用JSONP实现跨域请求的步骤如下:

  1. www.bbbbb.com.cn 域名下的网页中定义一个加载跨域脚本的javascript函数,即一个能够动态生成 <script> 标签的函数,该 <script> 标签的 src 指向服务器端程序( www.aaaaa.com.cn )所在的URL,并且URL后应附加一个 callback 参数,以便服务器端能够动态生成以 callback 为名的javascript函数:

    var remoteUrl = "http://www.aaaaa.com.cn/test/jsonpTest.php?callback=";
    function getDataFromRemote(callback) 
    {
        var remoteDataUrl = remoteUrl + callback;
        var script = document.createElement("script");
        script.setAttribute("src", remoteDataUrl);
        document.getElementsByTagName("head")[0].appendChild(script);
    } 
  2. 服务器端收到请求后,根据 callback 参数动态生成一段javascript脚本,该脚本调用 callback 函数并向其传递网页所需的数据:

    <?php
    if(isset($_GET["callback"]))
    {
        $json_data = json_encode(["data"=> "Hello World"]);
        echo $_GET["callback"]."({$json_data});";
    }
    ?>
  3. 网页端定义名为 callback 的函数,用于接收、处理服务器端传来的数据:

    function testCallback(jsonData) 
    {
        alert(jsonData["data"]);
    }
    getDataFromRemote('testCallback');

jQuery示例

jQuery 中的 ajax 方法支持 JSONP 类型的请求,只需将 dataType 设置为 jsonp 即可,示例如下:

<script type="text/javascript" src="//cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script>
<script type="text/javascript">
    function getDataFromRemoteAjax()
    {
        $.ajax({
            type: "get",
            async: true,
            url: "http://www.aaaaa.com.cn/test/jsonpTest.php",
            dataType: "jsonp",
            jsonpCallback: "testCallbackAjax",
            success: function (data) {
                alert(data["data"]);
            },
            error: function (err) {
                alert("error");
            }
        });
    }
</script>

服务器端的代码与上例的相同。

Blog 首页增加 回到顶部 按钮

需求说明

首页增加 回到顶部 按钮,首屏不显示,当页面滚动时(300px左右)显示

实现方案

使用Vuetify的 v-scroll 监听页面滚动事件,通过 documentElement.offsetTop 判断是否显示 回到顶部 按钮

Web Security Notes

关于跨站脚本攻击(XSS)

关于跨站请求伪造(CSRF)

关于点击劫持

关于HTTPS

加密算法

握手协议

中间人攻击

参考

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.