zhiwenxuan / my-web-note Goto Github PK
View Code? Open in Web Editor NEW知识体系构建
知识体系构建
- feat :新功能
- fix :修复 bug
- chore :对构建或者辅助工具的更改
- refactor :既不是修复 bug 也不是添加新功能的代码更改
- style :不影响代码含义的更改 (例如空格、格式化、少了分号)
- docs : 只是文档的更改
- perf :提高性能的代码更改
- revert :撤回提交
- test :添加或修正测试
type(scope?): subject #scope is optional
例子:
feat(login): add login page
https://github.com/conventional-changelog/commitlint
https://juejin.im/post/5ea9ad78e51d454dca70fbe3
Mac系统下的环境变量:
a. /etc/profile
b. /etc/paths
c. ~/.bash_profile
d. ~/.bash_login
e. ~/.profile
f. ~/.bashrc
编写环境变量
vim ~/.bash_profile
保存环境变量更改
source ~/.bash_profile
在这个时代,信息已经不再是稀缺资源,注意才是,有效的提高注意能力非常重要!!!注意力确实是一个有意思的话题,心理学有个被玩烂的实验,叫做斯特普效应。
似乎不太容易吧?起码需要你花点时间转个弯。
如果没有这些字的干扰,你肯定很容易就能说出颜色,但为什么这些颜色在字体上出现就变得难以驾驭了?
因为注意干扰。往大了说,注意分为两大类有意识的注意和无意识的注意。
有意识的注意:从一堆扑克牌中找出红桃A。
无意识的注意:低头走路时对面突然出现美女,你盯着看。
所以,提高注意能力的本质就是:加强有意识注意的集中程度和持续时间,尽量避免无意识注意对大脑的干扰。
现在进入正题:
注意力和意志力一样,都属于有限资源,用完了就没了,这点我想很多人都应该有体会,LOL单排20把以后,基本上你的注意力已经很难集中了,会在不经意间犯很多小错误,可能自己都意识不到。你可以尝试回放你的视频录像,对比之后几把和之前几把的失误次数。在工作中,长时间的脑力劳动你可能很难再回去看书了,就算动用意志力强迫自己看书也非常容易分神。这种现象时有科学解释的:《意志力:再度发现人类最强的力量》一书的作者罗伊鲍曼斯曾为美国《商业周刊》撰文论述该现象。他的是活跃我们大脑细胞的化学物质来源于血液中的血糖,这是一个自控的过程,包括控制我们的专注和集中注意力,这一过程会耗费能量。当我们努力思考、计算做决定的时候,我们消耗体内的血糖,同时也消耗我们的意志力。
**有句古话,叫做玩物丧志,“志”是需要意志力和注意力达成的,而“玩物”的过程浪费了我们大量的注意资源。所以,起床先做重要的事情,如果一天是从娱乐开始,那么你接下来的工作学习会很吃力。可以在工作时放一杯水,每次分神喝水,稍作休息就喝掉一点,并且告诉自己,我的注意在慢慢减少。
读一本书,开始的一段你可能需要读几遍才能搞懂,那个时间段是注意力慢慢开始集中的时间,效率较低。在2005年的一份研究报告中表示,我们需要花25分钟时间才能把已经分散了的注意力回到原有状态,并且你要记得,之前你已经花了近11分钟完成注意积累过程。网络信息时代的发展,我们的大量信息碎片化,随手拿手机翻阅都有大量不同种类的信息以不同方式干扰我们的注意,并且这样碎片化的吸收效果并不是很好。当你需要阅读,需要头脑风暴的时候,给自己设置一个时间段,一般以一个小时为周期,这个时间段,找一个安静的环境,静音你的手机,拿走桌上的小玩具,让自己真正的做到与任务与书本独处。
《别把脑袋放冰箱》一书的作者大卫罗克博士曾为《今日心理》撰文:有发现表明,无论完成什么任务,注意力偶尔转移会影响表现,而这些注意力的偶尔转移会激发**前额叶。**前额叶位于前额叶内,在前额**附近。当你考虑自己和他人的时候,这个区域就会活跃起来。大脑的这一区域也属于名为“默认设置”的网络。当你无所事事的时候,这个网络就处于活跃状态。他总结:“当你对外的注意力放松的时候,这个默认的大脑网络就会被激活,你的注意力转移为关注内部信号。”我们的大脑如同计算机,有屏幕保护设置,一定时间不操作就会自动进入屏保,而这个时间的长短可以通过锻炼调节,之后我会介绍一套训练方法的。
在上文中我引用过《意志力》一书:《意志力:再度发现人类最强的力量》一书的作者罗伊鲍曼斯曾为美国《商业周刊》撰文论述该现象。他的是活跃我们大脑细胞的化学物质来源于血液中的血糖,这是一个自控的过程,包括控制我们的专注和集中注意力,这一过程会耗费能量。当我们努力思考、计算做决定的时候,我们消耗体内的血糖,同时也消耗我们的意志力。这段话不仅说明注意与意志是有限资源,还可以看出,当我们强迫自己集中注意的时候,注意力和意志力同时运作,所消耗的能量要更多,所付出的代价更大,所达成的难度也更大。强迫自己做某一件事情的时候,效率也会变得低下,因为单位时间消耗能量石有上限的,消耗巨大能量用以强迫,那么用以专注的力量就会减少。所以,自信,相信自己读完这本书是可以的,不要给自己以不可能的预设并且试图动用意志力强迫自己,你需要告诉自己你能做到。当然,这个过程的建立需要花费一定时间练习,一些成功的经验自然会带来自信。很多人惊叹学霸学习时间之久,但可能对他们而言只不过是一件简单的事情,他们都没怎么动用意志力,只是做了该做的事情。
用读一遍下文,明白意思,并且数出里面有多少字母a.kan wan dian zan de dou shi hao ren a ,jin tian ken ding hui you hao yun qi de .我们可以一边走路,一边看书,但是基本没有人可以做到一边逛淘宝一边和男朋友讲爱情,一边游戏一边哄女朋友开心,因为注意很难以分裂形式出现。多任务同时进行的时候,特别容易引发焦虑,比如明天要考试了,还有很多作业要写,最近忙死,很多人都有这样的抱怨。你可以想象一下,一天之中,从穿鞋,刷牙到晚上睡觉, 你已经完成了很多任务,之所以这些任务不会引发你的焦虑,一是因为习惯,无需动用注意,二是他们是顺序进行的,不互相干扰,这点非常符合注意的特征。所以,学会列计划,在规定的时间注意应该的事情,其他时间不要去想,你会发现其实很容易,你的焦虑来自大脑同时面对多项任务需要注意的时候的一种不适应。
这事一项很玄奥的能力,并且很多人认为不必要,我这里所指遗忘为暂时遗忘,暂时的遗忘能够帮助你减轻大脑运行负担,但是我们的大脑似乎不敢我们这样随意的遗忘,那你就需要有一样载体暂时帮你记住。坚持做笔记、写日记,让自己能够心安理得的遗忘,能够有效提高你的效率。那很多人疑问了,那不是看书都没意义了吗?看了就忘。相信大家都知道记忆曲线,记忆的关键不在于短时记忆能输入多少信息,而在于如何保证不流失。笔记的温故,对长时记忆是非常有效果的,当然本片文章不是为了讲记忆,就不赘述了,多一句嘴,根据斯宾塞的实验过程,每个人都可以测试出自己的记忆曲线,属于自己独享的记忆曲线哦。
a. 冥想:内心观想莲花法闭上眼睛,然后放松,慢慢的集中注意到呼吸上来,用腹部呼吸,感受气流从鼻子进入胸部,然后腹部。在把注意集中到自己的大脑中,似乎能看到自己的鼻尖,然后慢慢的想象大脑中出现了一朵莲花,他的形状,他的每一个细节.......
b. 数字练习算24点:3 8 3 8(第一个给出答案的有奖励哦)数字迷宫、九宫格等方式
c. 有氧运动
a. 模型法注意力是一种资源,我们就要学会合理运用才能起到更好的效果,读一本书,如果内心中没有一定的模型建构,那么你的大量注意力需要先非配到记忆上来,或者大量时间是重复阅读。每一本书,每一个理论都是有着自己的理论模型的,比如看一头大象,你如果每一个细节去摸索要花费多大的注意力啊,只需要简单的记忆他的模型就好,下次见到你肯定能记住。我之前总结过一些模型,找到链接贴过来。
b. 联想法注意的内容前后关联越大,我们再次进入专注的状态就越容易。试想,打完游戏之后学数学,需要多长时间把心静下来?如果打完游戏之后陪女朋友谈恋爱,可能更容易进入状态吧?我们的注意力从一件事到另一件事所需时间和他们关联度成反比。所有,有效的进行联想不仅仅减轻记忆压力(这是记忆训练都不可绕开的一个高级学习模式),也能减轻注意压力。
c. 合理顺序打完游戏之后学数学,你可能需要10分钟静下心来,再花5分钟进入状态,但是你学完数学之后打游戏,可能一分钟不要就进入状态了。合理的安排你的注意顺序,可以节省你很多时间。原则:压力大的往前排重要的往前排需要更多注意力的往前排信息时代,决定成功的是对信息的处理能力,这里面不仅仅涉及到注意,还有记忆、意志力、想象力,这些我都会系统整理出书籍遇到较为合适的问题回答。
作者:极乐
链接:https://www.zhihu.com/question/20148429/answer/50487319
来源:知乎
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 8888;
server_name localhost;
root /Users/macbook/workspace/nginx-html;
#charset koi8-r;
#access_log logs/host.access.log main;
location /admin {
try_files $uri $uri/ /admin.html;
}
location / {
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
include servers/*;
}
// Based on: https://stackoverflow.com/a/46814952/283851
/**
* Create a Base64 Image URL, with rotation applied to compensate for EXIF orientation, if needed.
*
* Optionally resize to a smaller maximum width - to improve performance for larger image thumbnails.
*/
export async function getImageUrl(file: File, maxWidth: number|undefined) {
return readOrientation(file).then(orientation => applyRotation(file, orientation || 1, maxWidth || 999999));
}
/**
* @returns EXIF orientation value (or undefined)
*/
const readOrientation = (file: File) => new Promise<number|undefined>(resolve => {
const reader = new FileReader();
reader.onload = () => resolve((() => {
const view = new DataView(reader.result as ArrayBuffer);
if (view.getUint16(0, false) != 0xFFD8) {
return;
}
const length = view.byteLength;
let offset = 2;
while (offset < length) {
const marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1) {
offset += 2;
if (view.getUint32(offset, false) != 0x45786966) {
return;
}
offset += 6;
const little = view.getUint16(offset, false) == 0x4949;
offset += view.getUint32(offset + 4, little);
const tags = view.getUint16(offset, little);
offset += 2;
for (var i = 0; i < tags; i++) {
if (view.getUint16(offset + (i * 12), little) == 0x0112) {
return view.getUint16(offset + (i * 12) + 8, little);
}
}
} else if ((marker & 0xFF00) != 0xFF00) {
break;
} else {
offset += view.getUint16(offset, false);
}
}
})());
reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
});
/**
* @returns Base64 Image URL (with rotation applied to compensate for orientation, if any)
*/
const applyRotation = (file: File, orientation: number, maxWidth: number) => new Promise<string>(resolve => {
const reader = new FileReader();
reader.onload = () => {
const url = reader.result as string;
const image = new Image();
image.onload = () => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d")!;
let { width, height } = image;
const [outputWidth, outputHeight] = orientation >= 5 && orientation <= 8
? [height, width]
: [width, height];
const scale = outputWidth > maxWidth ? maxWidth / outputWidth : 1;
width = width * scale;
height = height * scale;
// set proper canvas dimensions before transform & export
canvas.width = outputWidth * scale;
canvas.height = outputHeight * scale;
// transform context before drawing image
switch (orientation) {
case 2: context.transform(-1, 0, 0, 1, width, 0); break;
case 3: context.transform(-1, 0, 0, -1, width, height); break;
case 4: context.transform(1, 0, 0, -1, 0, height); break;
case 5: context.transform(0, 1, 1, 0, 0, 0); break;
case 6: context.transform(0, 1, -1, 0, height, 0); break;
case 7: context.transform(0, -1, -1, 0, height, width); break;
case 8: context.transform(0, -1, 1, 0, 0, width); break;
default: break;
}
// draw image
context.drawImage(image, 0, 0, width, height);
// export base64
resolve(canvas.toDataURL("image/jpeg"));
};
image.src = url;
}
reader.readAsDataURL(file);
});
参考:https://gist.github.com/mindplay-dk/72f47c1a570e870a375bd3dbcb9328fb
const fs = require('fs')
const https = require('https')
function downloadFileAsync(uri, dest) {
return new Promise(resolve => {
const file = fs.createWriteStream(dest);
https.get(uri, (res) => {
if (res.statusCode !== 200) {
resolve(res.statusCode);
return;
}
res.on('end', () => {
});
file.on('finish', () => {
file.close(resolve);
}).on('error', (err) => {
// delete failed file
fs.unlink(dest);
resolve(err.message);
})
res.pipe(file);
});
});
}
使用对象的属性时,一般要判断对象是否存在。当属性嵌套过深时,判断起来比较繁琐。
如下:
let nestedProp = obj && obj.first && obj.first.second;
优化:使用可选链获取嵌套属性
let nestedProp = obj?.first?.second;
通常不建议桶的名字带点符号 “.” ,除非用作静态托管
https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html
清除 Cypress 的缓存
cypress cache list
cypress cache clear
// index.js
'use strict';
const S3Zip = require('./s3-zip')
const params = {
files: [
{
fileName: '1.jpg',
key: 'key1.JPG'
},
{
fileName: '2.jpg',
key: 'key2.JPG'
}
],
zippedFileKey: 'zipped-file-key.zip'
}
exports.handler = async event => {
const s3Zip = new S3Zip(params);
await s3Zip.process();
return {
statusCode: 200,
body: JSON.stringify(
{
message: 'Zip file successfully!'
}
)
};
}
// s3-zip.js
'use strict';
const fs = require('fs');
const AWS = require("aws-sdk");
const Archiver = require('archiver');
const Stream = require('stream');
const https = require('https');
const sslAgent = new https.Agent({
KeepAlive: true,
rejectUnauthorized: true
});
sslAgent.setMaxListeners(0);
AWS.config.update({
httpOptions: {
agent: sslAgent,
},
region: 'us-east-1'
});
module.exports = class S3Zip {
constructor(params, bucketName = 'default-bucket') {
this.params = params;
this.BucketName = bucketName;
}
async process() {
const { params, BucketName } = this;
const s3 = new AWS.S3({ apiVersion: '2006-03-01', params: { Bucket: BucketName } });
// create readstreams for all the output files and store them
const createReadStream = fs.createReadStream;
const s3FileDwnldStreams = params.files.map(item => {
const stream = s3.getObject({ Key: item.key }).createReadStream();
return {
stream,
fileName: item.fileName
}
});
const streamPassThrough = new Stream.PassThrough();
// Create a zip archive using streamPassThrough style for the linking request in s3bucket
const uploadParams = {
ACL: 'private',
Body: streamPassThrough,
ContentType: 'application/zip',
Key: params.zippedFileKey
};
const s3Upload = s3.upload(uploadParams, (err, data) => {
if (err) {
console.error('upload err', err)
} else {
console.log('upload data', data);
}
});
s3Upload.on('httpUploadProgress', progress => {
// console.log(progress); // { loaded: 4915, total: 192915, part: 1, key: 'foo.jpg' }
});
// create the archiver
const archive = Archiver('zip', {
zlib: { level: 0 }
});
archive.on('error', (error) => {
throw new Error(`${error.name} ${error.code} ${error.message} ${error.path} ${error.stack}`);
});
// connect the archiver to upload streamPassThrough and pipe all the download streams to it
await new Promise((resolve, reject) => {
console.log("Starting upload of the output Files Zip Archive");
s3Upload.on('close', resolve());
s3Upload.on('end', resolve());
s3Upload.on('error', reject());
archive.pipe(streamPassThrough);
s3FileDwnldStreams.forEach((s3FileDwnldStream) => {
archive.append(s3FileDwnldStream.stream, { name: s3FileDwnldStream.fileName })
});
archive.finalize();
}).catch((error) => {
throw new Error(`${error.code} ${error.message} ${error.data}`);
});
// Finally wait for the uploader to finish
await s3Upload.promise();
}
}
在 pages
目录下,定义一个文件夹和文件,且名字一样,如下:
pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue
然后在 users.vue
文件中,使用 <nuxt-child />
匹配子路由,如下
// users.vue
<template>
<nuxt-child />
</template>
之后 Nuxt
会自动生成如下配置:
router: {
routes: [
{
path: '/users',
component: 'pages/users.vue',
children: [
{
path: '',
component: 'pages/users/index.vue',
name: 'users'
},
{
path: ':id',
component: 'pages/users/_id.vue',
name: 'users-id'
}
]
}
]
}
// 返回距离,单位 m
getGPSDistance(start, end) {
let lat1 = (Math.PI / 180) * start.lat;
let lat2 = (Math.PI / 180) * end.lat;
let lon1 = (Math.PI / 180) * start.lng;
let lon2 = (Math.PI / 180) * end.lng;
let R = 6371000; //地球半径
let d = Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) *
Math.cos(lon2 - lon1)) * R;
return d;
}
const crypto = require('crypto');
const algorithm = 'aes-256-ctr';
const secretKey = 'vOVH6sdmpNWjRRIqCc7rdxs01lwHzfr3';
const iv = crypto.randomBytes(16);
const encrypt = (text) => {
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
return {
iv: iv.toString('hex'),
content: encrypted.toString('hex')
};
};
const decrypt = (hash) => {
const decipher = crypto.createDecipheriv(algorithm, secretKey, Buffer.from(hash.iv, 'hex'));
const decrpyted = Buffer.concat([decipher.update(Buffer.from(hash.content, 'hex')), decipher.final()]);
return decrpyted.toString();
};
module.exports = {
encrypt,
decrypt
};
参考: https://attacomsian.com/blog/nodejs-encrypt-decrypt-data
Chrome 81 版本后支持 image-orientation
,且默认属性是 from-image
相关资料:
https://chromestatus.com/feature/6313474512650240
https://paul.kinlan.me/correct-image-orientation-for-images-chrome-81/
const base64ToUint8Array = base64 => {
binary_string = window.atob(base64);
const len = binary_string.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes;
}
const uint8Array = base64ToUint8Array(base64);
// 获取 buffer
const buffer = uint8Array.buffer;
(menu) -> Settings -> Show Advanced Settings -> Web Content -> Customize Fonts -> Minimum Font Size.
这个函数的参数各自代表如下:
a:水平方向的缩放
b:水平方向的倾斜偏移
c:竖直方向的倾斜偏移
d:竖直方向的缩放
e:水平方向的移动
f:竖直方向的移动
3个方法都是基于transform的,也就是transform的快捷方式
缩放:scale(a, d) 等同于 matrix(a, 0, 0, d, 0, 0);
平移:translate(e, f) 等同于 matrix(1, 0, 0, 1, e, f);
旋转:rotate(deg) 等同于 matrix(cos(deg), sin(deg), -sin(deg), cos(deg), 0, 0);
一个图形同时需要缩放平移和旋转,就是3个单独的矩阵相乘
举例 transform:translate(10px,20px) rotate(37deg) scale(1.5,2)
即 matrix(1.2,0.9,-1.2,1.6,10,20)
当 Lambda Function
需要用某些库时,可以通过添加 layer
达到目的。下面以添加 image-magic
为例进行说明:
点击 deploy
按钮即可安装。详细可见 文档
Layer version ARN
在 Layers
列表中可以找到
在 nexus Repositories 建立 raw proxy
远程库使用阿里的 https://npm.taobao.org/mirrors/node-sass/
在 .npmrc 文件中配置 sass_binary_site
_auth=xxxx
sass_binary_site=http://nexus.xxx.com/repository/npm-node-sass-mirror-proxy
今天给大家分享一个 Web 知识点。如果你有过一段时间的 Web 开发经验,可能已经知道了。不过对于刚接触的新手来说,还是有必要了解一下的。
我们知道,网页里的a标签默认在当前窗口跳转链接地址,如果需要在新窗口打开,需要给 a 标签添加一个target="_blank"属性。
<a href="http://kaysonli.com/" target="_blank">1024译站</a>
顺便提下一个有意思的现象,很早之前我就发现,国外网站倾向于在当前页跳转,而国内网站喜欢打开新窗口。不信你们可以去验证下。我不知道这是交互设计上的文化差异,还是技术上的开发习惯。
当然,这两种方式各有优缺点。当前页跳转显得操作比较有连贯性,不会贸然打断用户的注意力,也会减少浏览器的窗口(tab 页)数量。但是对于需要反复回到初始页面的场景来说,就很麻烦了。比如搜索结果页面,通常需要查看对比几个目标地址,保留在多个窗口还是比较方便。
今天要说的不只是用户体验上的差别,而是涉及安全和性能。
如果只是加上target="_blank",打开新窗口后,新页面能通过window.opener获取到来源页面的window对象,即使跨域也一样。虽然跨域的页面对于这个对象的属性访问有所限制,但还是有漏网之鱼。
这是某网页打开新窗口的页面控制台输出结果。可以看到window.opener的一些属性,某些属性的访问被拦截,是因为跨域安全策略的限制。
即便如此,还是给一些操作留下可乘之机。比如修改window.opener.location的值,指向另外一个地址。你想想看,刚刚还是在某个网站浏览,随后打开了新窗口,结果这个新窗口神不知鬼不觉地把原来的网页地址改了。这个可以用来做什么?钓鱼啊!等你回到那个钓鱼页面,已经伪装成登录页,你可能就稀里糊涂把账号密码输进去了。
还有一种玩法,如果你处于登录状态,有些操作可能只是发送一个GET请求就完事了。通过修改地址,就执行了非你本意的操作,其实就是 CSRF 攻击。
除了安全隐患外,还有可能造成性能问题。通过target="_blank"打开的新窗口,跟原来的页面窗口共用一个进程。如果这个新页面执行了一大堆性能不好的 JavaScript 代码,占用了大量系统资源,那你原来的页面也会受到池鱼之殃。
尽量不使用target="_blank",如果一定要用,需要加上rel="noopener"或者rel="noreferrer"。这样新窗口的window.openner就是null了,而且会让新窗口运行在独立的进程里,不会拖累原来页面的进程。不过,有些浏览器对性能做了优化,即使不加这个属性,新窗口也会在独立进程打开。不过为了安全考虑,还是加上吧。
我特意用自己的博客网站 http://www.kaysonli.com/ 试了一下,点击里面的外链打开新页面,window.openner都是null。查看页面元素发现,a标签都加上了 rel="noreferrer"。博客是用 Hexo 生成的,看来这种设置已经成了基本常识了。
另外,对于通过window.open的方式打开的新页面,可以这样做:
var yourWindow = window.open();
yourWindow.opener = null;
yourWindow.location = "http://someurl.here";
yourWindow.target = "_blank";
1. sudo vim /etc/hosts
// 增加如下选项
127.0.0.1 dev.bees360.com
0.0.0.0 dev.bees360.com
// 刷新系统缓存
2. sudo dscacheutil -flushcache
// 3. 刷新浏览器缓存
// 4. 如果翻墙,要改为PAC 自动模式
解析 JWT, 兼容 UIcode 编码
parseJwt(token) {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
}
更多关于 Base64 解码编码见:
https://developer.mozilla.org/zh-CN/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
excelDate
要减一由于 Excel 时间和 JavaScript 开始时间不一致,可以统一以Excel 中1900为起点,再加上 excelDate - 1
new Date(1900, 0, excelDate-1)
$ curl -O https://bootstrap.pypa.io/get-pip.py
$ python3 get-pip.py --user
$ pip3 install awscli --upgrade --user
$ vim ~/.bash_profile
// 添加内容
export PATH=/Users/username/Library/Python/3.7/bin:$PATH
$ source ~/.bash_profile
$ aws --version
$ aws configure
~/.aws
参考:
选取物体或拖拽物体时,无法选中
<canvas id="cvs" width="400" style="width: 200px"></canvas>
const canvas = document.getElementById('cvs');
const { width, clientWidth, height, clientHeight } = canvas;
当 Canvas 上的 width 和 clientWidth 不等,或者高度层面不等时,事件的位置要做对应的转化,才能得到真实的值。
const scale = width / clientWidth;
getMousePosition(evt) {
const rect = canvas.getBoundingClientRect();
const clientX = evt.clientX;
const clientY = evt.clientY;
return {
x: (clientX - rect.left) * scale,
y: (clientY - rect.top) * scale
};
},
使用技术栈:jest + supertest
环境: node
beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));
test('', () => console.log('1 - test'));
describe('Scoped / Nested block', () => {
beforeAll(() => console.log('2 - beforeAll'));
afterAll(() => console.log('2 - afterAll'));
beforeEach(() => console.log('2 - beforeEach'));
afterEach(() => console.log('2 - afterEach'));
test('', () => console.log('2 - test'));
});
// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll
// Truthiness
toBeNull matches only null
toBeUndefined matches only undefined
toBeDefined is the opposite of toBeUndefined
toBeTruthy matches anything that an if statement treats as true
toBeFalsy matches anything that an if statement treats as false
// Number
test('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);
// toBe and toEqual are equivalent for numbers
expect(value).toBe(4);
expect(value).toEqual(4);
});
// String
test('there is no I in team', () => {
expect('team').not.toMatch(/I/);
});
test('but there is a "stop" in Christoph', () => {
expect('Christoph').toMatch(/stop/);
});
// Arrays and iterables
const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'beer',
];
test('the shopping list has beer on it', () => {
expect(shoppingList).toContain('beer');
expect(new Set(shoppingList)).toContain('beer');
});
初始化
const supertest = require('supertest');
const request = supertest(BaseURL);
请求
let auth = ['Authorization', 'Token']
request.post('/oauth/token')
.set(...auth)
.set('Accept', 'application/json') // 设置头部
.send('password=' + Password) // form data
.send('username=' + env.Username)
.send('scope=' + env.Scope)
.send('grant_type=password');
request.post('/project')
.set(...auth)
.set('Accept', 'application/json')
.send(data); // json data
request.get(`/project/${projectId}`)
.set(...auth)
.timeout(10*60*1000)
.set('Accept', 'application/json');
在官网 注册账号,并验证邮箱
在终端登录
$ npm login
$ npm init package-name -y
必须带有的字段
name :包名(全部小写,没有空格,可以使用下划线或者横线)
version: 版本
其他内容
author:作者信息
main:程序入口文件,一般都是 index.js
description:描述信息,有助于搜索
keywords:[] 关键字,有助于在人们使用 npm search 搜索时发现你的项目
scripts:支持的脚本,默认是一个空的 test
license:默认是 MIT
bugs:当前项目的一些错误信息,如果有的话
dependencies:在生产环境中需要用到的依赖
devDependencies:在开发、测试环境中用到的依赖
repository:代码仓库
完成工程开发
发布工程
$ npm publish
https://gitlab.bees360.com/help/user/project/pipelines/schedules
1 new schedule
2 在.gitlab-ci.yml
文件添加
job:on-schedule:
only:
- schedules
script:
- make world
job:
except:
- schedules
script:
- make build
不想跑定时任务的job,使用 except schedules
http_proxy
和 https_proxy
环境变量。端口要看翻墙软件的配置。Mac 系统 shadowsocks 一般是 1087, windows 系统下一般是 1080export http_proxy=http://127.0.0.1:1087;export https_proxy=http://127.0.0.1:1087;export NO_PROXY=localhost,127.0.0.1
firebase login:ci
获取 tokenfirebase deploy --only hosting --project ${PROJECT_NAME} --token ${TOKEN}
new Date(timestamp).toLocaleString('en-US', {
year:"numeric",
month:"2-digit",
day:"2-digit",
hour:"2-digit",
minute:"2-digit",
timeZoneName: "short"
})
// 01/08/2020, 04:55 PM GMT+8
详细见 MDN
承担的跳转的标签尽可能是 <a>
,要不浏览器一些默认行为将丢失,比如 按住 Control
按键跳转新标签页面。
// 发布订阅者模式
const Event = {
taskList: [],
subscribe: function () {
const params = {};
const args = Array.prototype.slice.call(arguments);
params.msg = args[0];
params.args = args.slice(1);
this.taskList.push(params)
},
publish: function () {
if (this.taskList.length) {
const { msg, args } = this.taskList.shift();
Task.run(msg, args);
}
}
}
// 执行完一个任务后,再发布,保证任务队列顺序执行。
const Task = {
run: function (msg, args) {
this[msg].apply(this, args);
},
lazyMan: function () {
console.log(`lazyMan start`)
Event.publish();
},
eat: function (str) {
console.log(`Eat: ${str}`)
Event.publish();
},
play: function (str) {
console.log(`Play: ${str}`)
Event.publish();
},
sleep: function (time) {
setTimeout(function () {
console.log(`Sleep: ${time}s`)
Event.publish();
}, time * 1000)
},
}
// 返回 对象本身,保证链式调用
const _LazyMan = function () { };
_LazyMan.prototype.eat = function (str) {
Event.subscribe('eat', str);
return this;
}
_LazyMan.prototype.play = function (str) {
Event.subscribe('play', str);
return this;
}
_LazyMan.prototype.sleep = function (time) {
Event.subscribe('sleep', time);
return this;
}
// 启动辅助
const LazyMan = function () {
Event.subscribe('lazyMan');
// 事件循环:订阅完后,下个宏任务启动发布。
setTimeout(function () {
Event.publish();
})
return new _LazyMan();
}
const lazyMan = new LazyMan();
lazyMan.eat('apple').play('ball').sleep(2).eat('rice');
有些场景需要容器具有初始值,比如 element table。如果父容器是flex 布局,要注意设置默认值。设置默认值的方法:
// 宽度初始值
min-width: 0;
// 高度初始值
min-height: 0;
如果你想从别的 Git 托管服务那里复制一份源代码到新的 Git 托管服务器上的话,可以通过以下步骤来操作。
1). 从原地址克隆一份裸版本库,比如原本托管于 GitHub。
git clone --bare git://github.com/username/project.git
2). 然后到新的 Git 服务器上创建一个新项目,比如 GitCafe。
3). 以镜像推送的方式上传代码到 GitCafe 服务器上。
cd project.git
git push --mirror [email protected]/username/newproject.git
4). 删除本地代码
cd ..
rm -rf project.git
5). 到新服务器 GitCafe 上找到 Clone 地址,直接 Clone 到本地就可以了。
git clone [email protected]/username/newproject.git
这种方式可以保留原版本库中的所有内容。
异步I/O、事件与回调函数、单线程、跨平台
I/O密集型、是否不擅长CPU密集型业务、与遗留系统和平共处、分布式应用
弥补当前 JavaScript 没有标准的缺陷,以达到像 Python、Ruby和Java具备开发大型应用的基础能力。
规范涵盖了模块、二进制、Buffer、字符集编码、I/O流、进程环境、文件系统、套接字、单元测试、Web服务器网关接口、包管理等。
模块规范分为:模块引用、模块定义和模块标识三个部分。
var math = require('math');
// math.js
exports.add = function() {
// something
}
require()
方法的参数,它必须符合小驼峰命名的字符串,或者以.、..开头的相对路径,或者绝对路径。核心模块(Node提供的模块)、文件模块(用户编写的模块)
Node 对引入的模块都会进行缓存,以减少二次引入时的开销。
模块路径:
模块路径是Node在定位文件模块的具体文件时制定的查找策略,具体表现为一个路径组成的数组。如下:
[
'/Users/macbook/workspace/web_workspace/daily-test/node_modules',
'/Users/macbook/workspace/web_workspace/node_modules',
'/Users/macbook/workspace/node_modules',
'/Users/macbook/node_modules',
'/Users/node_modules',
'/node_modules'
]
查找方式如下:以当前文件为起点,首先在当前文件目录下找,找不到就去父级目录下找,不断逐级往上。所以当前文件离node_modules
越远时,查找越费时。
文件扩展名分析
在标识符不包含扩展名的情况下,按照 .js、.json、.node 的次序补足扩展名
目录分析和包
通过分析文件扩展名之后,可能没有查找到对应文件,但却得到一个目录,此时Node会净目录当做包处理。通过JSON.parse()
解析包描述对象,从中取出 main 属性指定的文件名进行定位。如果main指定的文件查找错误,会将index当做默认文件名,依次查找 index.js index.json index.node
。
在Node 中,每个模块都是一个对象,定义如下:
function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
if (parent && parent.children) {
parent.children.push(this);
}
this.filename = null;
this.loaded = false;
this.children = [];
}
(function (exports, require, module, __filename, __dirname) {
var math = require('math');
exports.area = function (radius) {
return Math.PI * radius * radius;
};
});
这样每个模块进行作用域隔离,避免污染。
C/C++模块的编译
调用 process.dlopen()
JSON文件编译
利用 fs 模块获取内容,调用 JSON.parse() 方法得到对象。
核心模块分为 C/C++编写的和JavaScript编写的两部分,其中C/C++文件存放在Node项目的src目录下,JavaScript文件存在lib目录下。
由纯C/C++编写的部分统一称为内建模块
文件模块可能依赖核心模块,核心模块(JavaScript)可能依赖内建模块
文件模块、核心模块、内建模块、C/C++扩展模块
JavaScript核心模块主要职责:1. 作为C/C++内建模块的封装层和桥接层,供文件模块调用;2. 纯粹的功能模块,不需要跟底层打交道,但是十分重要。
文件模块通常由第三方编写,包括普通JavaScript模块和C/C++扩展模块。
CommonJS的包规范定义,由包结构和包描述文件两个部分组成。
包实际上是一个存档文件,即一个目录直接打包为.zip或tar.gz格式的文件,安装后解压为还原为目录。完全符合CommonJS规范的包目录应该包含以下文件:
包描述文件:JSON 格式的文件 - package.json
字段:
name, description, version, keywords, maintainers, contributors, bugs, licenses,
repositories, dependencies, homepage, os, cpu, engine, bultin, directories, implements, scripts.
NPM 基于包规范实现,并且多了 author, bin, main, devDependencies 4个字段。
第三是工具 Nexus
规范: CommonJS、AMD、CMD
;(function (name, definition) {
// 检测上下文环境是否为AMDईCMD
var hasDefine = typeof define === 'function',
// 检查上下文环境是否为Node
hasExports = typeof module !== 'undefined' && module.exports;
if (hasDefine) {
// AMD环境或CMD环境
define(definition);
} else if (hasExports) {
// 定义为普通Node模块
module.exports = definition();
} else {
// 将模块的执行结果挂在window变量中,在浏览器中this指向window对象
this[name] = definition();
}
})('hello', function () {
var hello = function () {};
return hello;
});
单线程同步编程模型会因阻塞I/O导致硬件资源得不到更优的使用。多线程编程模型也因为编程中的死锁、状态同步等问题让开发头疼。
Node处理方案:利用单线程,远离多线程死锁、状态同步问题;利用异步I/O,让单线程远离阻塞,以便更好使用CPU。
在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们称为Tick。
每个Tick的过程就是查看是否有事件待处理,如果有,就取出事件及其相关的回调函数。如果存在关联的回调函数,就执行它们。
(见图3-11)
每个事件循环中有一个或者多个观察者,而判断是否有事件要处理的过程就是向这些观察者询问是否又要处理的事件。
从JavaScript发起调用到内核执行完I/O操作的过渡过程中,存在一种中间产物,它叫做请求对象。所有的状态都保存在这个对象中,包括送入线程池等待执行以及I/O操作完毕后的回调处理。
从JavaScript调用Node的核心模块,核心模块调用C++内建模块,内建模块通过libuv进行系统调用,这是Node里经典的调用方式。
组装好请求对象、送入I/O线程池等待执行,实际上完成了异步I/O的第一部分,回调通知是第二部分。
回调过程中,动用了事件循环的I/O观察者,每次Tick执行中,它会调用IOCP相关的GetQueuedCompletionStatus() 方法检查线程池中是否有执行完的请求,
如果存在,会将请求对象加入到I/O观察者的队列中,然后将其当做事件处理。
见图 3-13
事件循环、观察者、请求对象、I/O线程池这四者共同构成了Node异步I/O模型的基本要素。
在Node中,除了JavaScript是单线程外,Node自身其实是多线程的,只是I/O线程使用的CPU较少。另外一个需要重视的观点是,除了用户代码无法并行执行外,所有I/O(网络I/O、磁盘I/O等)则是可以并行起来的。
与I/O无关的API:setTimeout(), setInterval(), setImmediate(), process.nextTick()
setTimeout() 和 setInterval() 与浏览器的API一致。调用setTimeout() 或者 setInterval() 创建的定时器会被插入到定时器观察者内部的一个红黑树中。
每次Tick执行时,会从该红黑树中迭代取出定时器对象,检查是否超过定时时间,如果超过,就形成一个事件,它的回调函数将立即执行。
见图3-14
每次调用process.nextTick()方法,只会将回调函数放入队列中,在下一轮Tick时取出执行。
定时器中采用红黑树的操作时间复杂度为O(lg(n)), nextTick的时间复杂度为O(1)。相比较之下,process.nextTick() 更高效。
另外,采用定时器需要动用红黑树,创建定时器对象和迭代等操作,因此 process.nextTick() 也更轻量。
setImmediate()方法与process.nextTick()方法十分相似,都是将回调函数延迟执行。但是 process.nextTick() 中的回调函数比 setImmediate() 优先。
在具体实现上,process.nextTick()的回调函数保存在一个数组中,setImmediate()的结果则是保存在链表中。
在行为上,process.nextTick() 在每轮循环中将数组中的回调函数全部执行完,而 setImmediate() 只执行链表中的一个回调函数。如下:
// 加入两个nextTick()的回调函数
process.nextTick(function () {
console.log('nextTick延迟执行1');
});
process.nextTick(function () {
console.log('nextTick延迟执行2');
});
// 加入两个setImmediate()的回调函数
setImmediate(function () {
console.log('setImmediate延迟执行1');
// 进入ူْ循环
process.nextTick(function () {
console.log('强势插入');
});
});
setImmediate(function () {
console.log('setImmediate延迟执行2');
});
console.log('正常执行');
执行结果:
正常执行
nextTick延迟执行1
nextTick延迟执行2
setImmediate延迟执行1
强势插入
setImmediate延迟执行2
事件驱动的实质:通过主循环加事件触发的方式来运行程序
Node 通过事件驱动的方式处理请求,无须为每一个请求创建额外的对应线程,可以省掉创建线程和销毁线程的开销,同时操作系统在调度任务时因为线程较少,上下文切换的代价很低。这是Node高性能的一个原因。
事件循环是异步实现的核心,它与浏览器中的执行模型基本保持一致。
64位系统约为 1.4GB,32位系统约为0.7GB
$ node
> process.memoryUsage();
{ rss: 14958592,
heapTotal: 7195904,
heapUsed: 2821496 }
node --max-old-space-size=1700 test.js // 单位为MB
node --max-new-space-size=1024 test.js // 单位为KB
sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist
如果哪天你想让它开机启动了,则将unload 改为 load:
sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist
重启apache:sudo /usr/sbin/apachectl restart
关闭apache:sudo /usr/sbin/apachectl stop
开启apache:sudo /usr/sbin/apachectl start
一个关于图片的跨域场景:
如果平台里存在对同一张跨域图片的 <img> 标签请求
和 XHR 请求
,且先进行 <img> 标签请求
,之后再进行 XHR 请求
。浏览器会取 <img> 标签请求
过的缓存,但是这个时候不会返回头部信息,既不会存在 Access-Control-Allow-Origin
头部信息。最后则会报跨域错误。
通过设置 <img>
标签的 crossorigin
属性的值为 anonymous
,强制图片每次请求都使用 XHR 的 CORS 请求。
参考:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-crossorigin
关于跨域的相关属性:referrerpolicy
需要通过 Web Service (即手动发送 HTTP 请求)来编码,而不能通过 JavaScript SDK 来。这样才可以每秒发送 50 次请求。另外一个注意点,只有绑定信用卡的 API Key 才支持 Web Service。
部分代码:
import Axios from 'axios';
Axios.get('https://maps.googleapis.com/maps/api/geocode/json', {
params: {
address,
key: 'API_KEY'
}
}).then(res => {
if (res.data.status === 'OK') {
console.log(res.data.results[0])
} else {
console.log(res.data.status)
}
}).catch(error => {
console.log(error)
})
如果域名已经在 Cloundfront Alternate Domain Names (CNAMEs)
中配置过,需要移除
通过 Date.parse()
方法可以判断格式是否正确,如果格式正确,返回时间戳,否则返回 NaN
。
参考: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
主要利用Expires 和 Cache-Control两个字段来控制,如果命中强缓存,直接从缓存中拉取内容,不再与服务器通信。
用法
使用一个到期时间来表示资源是否到期。设置时,以服务器时间为依据;判断时,以客户端时间为依据。
expires: Wed, 11 Sep 2019 16:12:18 GMT
缺陷
依赖客户端时间:如果服务器时间和客户端不一致,容易误判;用户改动客户端时间时,也会出差错。
用法
HTTP1.1新增Cache-Control来解决Expires的缺陷或者是替代。通过max-age来设置资源的有效期,避免依赖客户端时间。如下:
cache-control: max-age=31536000
cache-control相对expires更准确,如果同时存在两者,以cache-control为准。
几个相关的字段
s-maxage
只在缓存服务器有效,优先级比max-age高
public 和 private
public:设置资源既可以被缓存服务器缓存,也可以被浏览器缓存
private: 设置资源只可以被浏览器缓存,private为默认值
no-store 和 no-cache
no-store: 设置资源不被缓存,每次都重新获取
no-cache: 设置资源绕过浏览器缓存,直接询问服务器资源是否过期。走协商缓存路线。
浏览器向服务器询问缓存的相关信息,进而判断是重新发起请求获取资源,还是从本地获取缓存资源。
如果服务器提示资源未改动(Not Modified),资源重定向到浏览器缓存,这时响应状态码是304。如下:
Request Mehtod: GET
Status Code: 304 Not Modified
协商缓存的实现: 从Last Modified 到 Etag
用法
Last-Modified是一个时间戳,如果启用协商缓存,它会在首次请求时随着Response Headers返回:
Last-Modified: Fri, 27 Ocy 2017 06:35:57 GMT
随后每次请求,会带上If-Modified-Since的时间戳字段,它的值正是上次response返回给它的
last-modified值:
If-Modified-Since: Fri, 27 Ocy 2017 06:35:57 GMT
服务器接收到这个时间戳后,会对比该时间戳和资源在服务器上的最后的修改时间是否一致,从而判断资源是否变化。
变化: 返回完整响应内容,并在Response Headers中添加新的Last-Modified值。
没有变化:返回304响应,如下:
Status Code: 304 Not Modified
Last-Modified缺陷
我们编辑文件,内容没改,但是修改时间改了,服务器会误判,重新返回新的内容
修改文件速度很快,在1秒内完成(比如100ms),由于If-Modified-Since最小计量单位是秒,这时服务器也会误判,需要返回新的内容,但却没有返回。
Etag为解决Last-Modified的缺陷而出现。作为Last-Modified的补充,优先级比Last-Modified高。
Etag是由服务器为每一个资源生成的唯一标识字符串,这个标识字符串基于文件内容编码。不同的文件,对应的Etag不同,所以能精准感知文件的变化。
用法
和Last-Modified使用类似。首次请求时,响应头有一个最初的标识字符串,如下:
ETag: W/"2a3b-1602480f459"
下次请求时,请求头有If-None-Match的字符串供服务器比较,如下:
If-None-Match: W/"2a3b-1602480f459"
缺陷
Etag的生成需要服务器额外的开销,影响服务端性能。视情况使用。
当我们的资源内容不可复用时,直接为 Cache-Control 设置 no-store,拒绝一切形式的缓存;否则考虑是否每次都需要向服务器进行缓存有效确认,如果需要,那么设 Cache-Control 的值为 no-cache;否则考虑该资源是否可以被代理服务器缓存,根据其结果决定是设置为 private 还是 public;然后考虑该资源的过期时间,设置对应的 max-age 和 s-maxage 值;最后,配置协商缓存需要用到的 Etag、Last-Modified 等参数。
内存缓存:优先级最高,最快,生命周期比较短。
使用规则: 考虑内存大小,一些Base64 图片、小体积的css,js会被缓存在内存中。
Service Worker 是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,因此无法直接访问 DOM。这样独立的个性使得 Service Worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助 Service worker 实现的离线缓存就称为 Service Worker Cache。
Service Worker 的生命周期包括 install、active、working 三个阶段。一旦 Service Worker 被 install,它将始终存在,只会在 active 与 working 之间切换,除非我们主动终止它。这是它可以用来实现离线存储的重要先决条件。
大家注意 Server Worker 对协议是有要求的,必须以 https 协议为前提。
Push Cache 是指 HTTP2 在 server push 阶段存在的缓存。这块的知识比较新,应用也还处于萌芽阶段,我找了好几个网站也没找到一个合适的案例来给大家做具体的介绍。但应用范围有限不代表不重要——HTTP2 是趋势、是未来。在它还未被推而广之的此时此刻,我仍希望大家能对 Push Cache 的关键特性有所了解:
Push Cache 是缓存的最后一道防线。浏览器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache。
Push Cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放。
不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache。
更多的特性和应用,可以在日后的开发过程中去挖掘和实践。
缓存作用更多是减少网络请求,适当应用以上四种请求,可以极大提升我们的性能。
虽然已经将node-sass
缓存到内部组件,但是安装 node-sass
时,还会源码执行下载操作,原因是要根据平台(Windows,Linux,Mac...)来不同的文件。下载的文件是在GitHub上的,没有翻墙,容易下载失败或者慢。
比如下面的报错:
npm install
> [email protected] install /builds/root/upstream/bees360ai/frontend/node_modules/node-sass
> node scripts/install.js
Downloading binary from https://github.com/sass/node-sass/releases/download/v4.12.0/linux-x64-64_binding.node
Cannot download "https://github.com/sass/node-sass/releases/download/v4.12.0/linux-x64-64_binding.node":
ESOCKETTIMEDOUT
Hint: If github.com is not accessible in your location
try setting a proxy via HTTP_PROXY, e.g.
export HTTP_PROXY=http://example.com:1234
or configure npm proxy via
npm config set proxy http://example.com:8080
> [email protected] postinstall /builds/root/upstream/bees360ai/frontend/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"
> [email protected] postinstall /builds/root/upstream/bees360ai/frontend/node_modules/ejs
> node ./postinstall.js
这时可以下载的源头来自淘宝。如下操作:
方法一:
在 .npmrc 文件中设置:
// .npmrc
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
方法二:
npm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
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.