leoyoung07 / blog Goto Github PK
View Code? Open in Web Editor NEWLeo Young Blog
Home Page: https://leoyoung07.github.io/blog
Leo Young Blog
Home Page: https://leoyoung07.github.io/blog
如果一方主动断开连接,另一方尝试接收数据时会抛出异常,例如C#下会抛出SocketException
:
ErrorCode: 10054
Message: "远程主机强迫关闭了一个现有的连接。"
SocketErrorCode: ConnectionReset
捕获该异常,可以判断对方是否主动断开连接。
如果是网络异常导致连接断开,接收数据的一方无法得知网络连接状态,这时就需要用心跳机制来解决这个问题。TCP 本身有 Keepalive 选项,但默认关闭,而且检测周期过长(2个小时),因此一般都在应用层自己实现心跳包。心跳包即像心跳一样每隔一段时间向对方发送一个数据包,告诉对方自己连接正常。根据业务场景需要,心跳包可以两边都发送或者由一边主动发送,发送周期可设置在30s左右。如果由客户端主动发心跳包,服务端被动检测连接的周期可
设置为客户端发送周期的两到三倍。
消息格式可基于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)
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.js
中 Web 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();
};
})();
最近在阅读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()
返回的的是 \n
而 getch()
接收到 \r
就返回了。对此也有人这样解释:
因为getchar()是标准的C库函数,在UNIX系统里回车一律只用\n。
Windows里回车输入的其实是\r\n,但是C库会对其进行处理(读写文本文件时也会),统一转换为规范的\n。而getch()则是比较低级的,会越过这个处理,貌似没有经过输入流缓冲,而是直接调用键盘服务中断获得的,就是原始按键。
另外,程序中使用 int c
而不是 char c
的原因是
EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不正确的。
求模和求余其实是两种不同的运算。当两个操作数同号时,它们的运算结果相同;而当两个操作数异号时,它们的运算结果就不同了。
求模与求余运算都分两步进行:
求模和求余的差别在第一步:
JavaScript中的 %
运算符其实是求余运算,即:
console.log((-13) % 64);
结果是-13。
JavaScript中的求模运算可以采用以下方法实现:
Number.prototype.mod = function (n)
{
return ((this % n) + n) % n;
}
此时,用:
console.log((-13).mod(64));
结果是51。
记录微信(公众号/网页/小程序)开发中的一些坑和填坑方法
微信中允许用户调整网页字体大小,而当页面使用rem作为单位进行布局时,用户调整字体大小就会导致页面布局变乱。
/*
* 页面加入这段代码可使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();
}
})();
body {
-webkit-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
}
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#中使用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版本 |
---|---|---|---|
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 |
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: public
和 Cache-Control: private
两个指令用于表明缓存可以保存的位置。
private: Private browser caches, 即只有客户端(浏览器)可以缓存
public: Shared proxy caches, 即客户端(浏览器)和代理服务器都可以缓存
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-Since
和 If-Unmodified-Since
字段与它配合使用。
If-Modified-Since: 如果资源在该字段给定的时间后没有修改过,服务器返回 304
状态,并且不带有数据;否则返回 200
状态,并且带上请求的资源。当与 If-None-Match
字段一起使用并且服务器支持 If-None-Match
字段时,它将被忽略。
ETag: 用于标定资源版本的标识。与 If-Match
和 If-None-Match
字段配合使用。
If-None-Match: 如果服务器资源的版本号与该字段指定的ETag一致,则返回 304
状态;否则返回 200
状态,并且带上请求的资源。
参照物 | 属性 |
---|---|
父盒子对应属性 | 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 系列函数等 |
PX
、 EM
与 REM
添加Activities页面,主要内容:
- [ ] 展示个人基本信息
cURL 是Linux下的命令行工具,windows 下可通过MinGW安装(安装Git for windows即可)。
基本用法:
curl "http://www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK"
# application/x-www-form-urlencoded
curl --data "birthyear=1905&press=%20OK%20" http://www.example.com/when.cgi
curl --head "http://www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK"
HTTPie 是使用python开发的命令行工具,可用pip安装。
基本用法:
http [flags] [METHOD] URL [ITEM [ITEM]]
Postman 和 Restlet Client都是Chrome下的扩展(应用),具有图形化界面,很容易使用。
使用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数组时,Content-type为multipart/form-data
;而post query字符串时Content-type为application/x-www-form-urlencoded
。
PHP手册中对CURLOPT_POSTFIELDS的说明如下:
全部数据使用HTTP协议中的"POST"操作来发送。要发送文件,在文件名前面加上@前缀并使用完整路径。这个参数可以通过urlencoded后的字符串类似'para1=val1¶2=val2&...'或使用一个以字段名为键值,字段数据为值的数组。如果value是一个数组,Content-Type头将会被设置成multipart/form-data。
当元素可见的样式改变而不影响其布局时,会引起浏览器的重新绘制(重绘)。
当元素的尺寸、结构等发生改变时,浏览器会重新渲染部分或全部文档(回流)。
回流对页面性能的影响比重绘要大
回流会引起重绘,重绘不一定会引起回流
clientWidth
、clientHeight
、clientTop
、clientLeft
offsetWidth
、offsetHeight
、offsetTop
、offsetLeft
scrollWidth
、scrollHeight
、scrollTop
、scrollLeft
scrollIntoView()
、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
- 避免使用table布局。
- 尽可能在DOM树的最末端改变class。
- 避免设置多层内联样式。
- 将动画效果应用到position属性为absolute或fixed的元素上。
- 避免使用CSS表达式(例如:calc())。
- 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
- 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
- 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
为 Blog 首页添加搜索功能
直接利用 github issue 页面的搜索功能: https://github.com/leoyoung07/blog/issues?utf8=✓&q=
利用 github 的 search api: https://developer.github.com/v3/search/#search-issues
前端在已有的issue列表中搜索
Node
和 HTMLElement
innerText()
和 textContent()
https://developer.mozilla.org/en-US/docs/Web/API/Node/innerText
https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
Node.contains()
https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object
https://stackoverflow.com/questions/9158238/why-js-function-name-conflicts-with-element-id
https://stackoverflow.com/questions/3434278/do-dom-tree-elements-with-ids-become-global-variables
document.designMode
和 document.execCommand()
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content
https://developer.mozilla.org/en-US/docs/Web/API/Document/designMode
https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
https://stackoverflow.com/questions/6240139/highlight-text-range-using-javascript/6242538#6242538
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) {
});
CommonJS规范,在node.js中使用,一个文件就是一个模块。通过 exports
导出模块内容:
// filename: foo.js
var bar = 'bar'; // private
// public
function foo () {
}
exports.foo = foo;
通过 require
引入模块内容:
var foo = require('./foo').foo;
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
}
}));
ES6
中支持的模块定义方法,一个模块就是一个独立的文件。通过 export
导出模块内容;
// filename: foo.js
const bar = 'bar'; // private
// public
function foo () {
}
export {foo};
通过 import
引入模块内容:
import {foo} from './foo';
The Protocol-relative URL
)在弹窗等组件中,通常需要实现点击组件外部隐藏该组件的功能,下面整理一下常用的实现方式。
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)
}
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"
处理外部点击事件,就可以实现关闭/隐藏目标元素。
伪类:添加到某些选择器,用于向元素的特定状态设置样式。
伪元素:添加到某些选择器,用于向元素的特定部分设置样式。
伪类是针对元素的特定状态,伪元素是针对元素的特定部分
伪元素在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.
内容区域(content area)
包含元素真实内容的区域,位于内容边界的内部。它的宽度:content-box width,它的高度:content-box height。
内边距区域(padding area)
延伸到包围padding的边框,内容区域的背景、颜色或者图片会延伸到padding上,它位于内边距边界内部。它的宽度:padding-box width,它的高度:padding-box height。
边框区域(border area)
是包含边框的区域,它位于边框边界内部。它的宽度:border-box width,它的高度:border-box height,由属性 border-width 及border控制。
外边距区域(margin area)
用空白区域扩展边框区域,以分开相邻的元素。它的宽度:margin-box width,它的高度:margin-box height。
现代浏览器支持4种Observer API:
Intersection Observer
可以监视DOM元素之间的交集。可用于监控元素进入、离开视区。可用来实现图片的懒加载、确定广告可见度、实现无限滚动的页面、实现元素进入视区时自动播放视频或动画。
Mutation Observer
可以监视DOM元素的改变,包括元素的添加、删除,元素值或属性的改变。主要用在JS框架中,用于响应DOM元素的变化。
Resize Observer
可以监视DOM元素内容矩形(content box)的尺寸(宽度、高度)变化。类似于 document.onresize()
和 window.resize()
方法。 当元素被添加到DOM或从DOM移除、display
被设置为 none
、被设置为显示且尺寸不为0时都会触发该监听事件。
Performance Observer
可监视调用 performance
API记录性能指标的情况。用于监听mark
、measure
、paint
等类型的性能记录。
登录微信后台,在浏览器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();
}
服务器启用Session后,约定一个Cookie的键值作为Session的口令(例如 session_id
)。如果用户请求的Cookie中没有改键值,服务器就生成一个唯一的值,添加到Cookie中。
将Session的键值存储在URL的查询字符串中,如果用户请求的URL中不带有约定的Session参数(session_id
),服务器就生成一个唯一的值添加到URL的查询字符串中,并将请求重定向(302)到修改后的URL。这种实现的问题在于,如果别人用同样的URL进行请求,就会带上相同的session_id,造成用户信息泄露的风险。
这种方式利用HTTP头部的ETag字段存储session_id,实现Session降级。在页面中加入一个动态生成的js文件(例如etag_session.js
),用户向服务器请求该文件时,服务器检查请求中是否带有if-none-match
字段,如果没有则生成一个唯一的值,添加到响应头部的ETag字段中。
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)
设定一个调用周期n,当距离上次执行该动作的时间大于等于调用周期n,才执行该动作。类比咖啡机按钮,按一次出咖啡,但在出咖啡的期间再次按则无效。
函数去抖(debounce)
设定一个调用周期n,当调用动作的时间超过周期n,才会执行该动作,若在周期n内又调用此动作则将重新计算执行时间。类比电梯按钮,门关上前,每次按下按钮,电梯门都延迟一段时间才关闭。
/**
*
* @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)
}
}
}
/**
*
* @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)
}
}
映射网络驱动器
在资源管理器中添加网络驱动器,例如:\\RemoteServerName\ShareName
。
在 SQL Server 中启用 xp_cmdshell
EXEC sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
EXEC sp_configure 'xp_cmdshell',1
GO
RECONFIGURE
GO
在 SQL Server 中添加网络驱动器
EXEC XP_CMDSHELL 'net use H: \\RemoteServerName\ShareName'
[可选]查看添加的网络驱动器
EXEC XP_CMDSHELL 'Dir H:'
[可选]附加完成后在SQL Server中删除网络驱动器
EXEC XP_CMDSHELL 'net use H: /delete'
-T1807
的启动参数,以允许从网络驱动器附加数据库。添加参数后需重启SQL Server服务。添加方法:
- In SQL Server Configuration Manager, click SQL Server Services.
- In the right pane, right-click SQL Server (<instance_name>), and then click Properties.
- On the Advanced tab, in the Startup Parameters box, type the parameters separated by semicolons (;).
对首页的scroll事件监听 导致在手机上滚动页面时出现卡顿现象。
通过在scroll事件中增加 去抖函数(debounce) 节流函数(throttle),优化页面滚动。
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) |
init进程是不可杀的。init进程是由内核启动的用户级进程,它始终是第一个进程(其进程编号始终为1)。 其它所有进程都是init进程的子孙。
使用 kill 0
来终止所有由当前shell启动的进程,包括后台进程。
kill -0 pid
用于检测是否有权限给进程发送信号。例如:$ if ! kill -0 $(pgrep sleep); then echo "You're weak!"; fi
组合键 | 说明 |
---|---|
Ctrl + C | 终止目前的命令 |
Ctrl + D | 输入结束 (EOF) |
Ctrl + M | 相当于 Enter |
Ctrl + S | 暂停屏幕的输出 |
Ctrl + Q | 恢复屏幕的输出 |
Ctrl + Z | 暂停目前的命令 |
跨域问题是由于浏览器的同源策略而产生的,同源是指同协议、同域名、同端口,出于安全的考虑,浏览器禁止脚本的跨域(不同源)请求。
虽然浏览器禁止跨域请求,但是却允许引用和执行来自其他域的脚本文件,这也就是下面将要提到的 JSONP 的原理。
JSONP(JSON with Padding) 是一种动态加载js的技术,即使用javascript脚本动态加载<script>
标签,从而使得网页可以跨域获取数据。该方法需要服务器端进行配合,动态生成javascript脚本,javascript脚本中包含网页所需的数据。
假设 www.bbbbb.com.cn
域名下有一网页需要请求 www.aaaaa.com.cn
域名下的数据,利用JSONP实现跨域请求的步骤如下:
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);
}
服务器端收到请求后,根据 callback
参数动态生成一段javascript脚本,该脚本调用 callback
函数并向其传递网页所需的数据:
<?php
if(isset($_GET["callback"]))
{
$json_data = json_encode(["data"=> "Hello World"]);
echo $_GET["callback"]."({$json_data});";
}
?>
网页端定义名为 callback
的函数,用于接收、处理服务器端传来的数据:
function testCallback(jsonData)
{
alert(jsonData["data"]);
}
getDataFromRemote('testCallback');
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>
服务器端的代码与上例的相同。
首页增加 回到顶部 按钮,首屏不显示,当页面滚动时(300px左右)显示
使用Vuetify的 v-scroll
监听页面滚动事件,通过 documentElement.offsetTop
判断是否显示 回到顶部 按钮
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.