加密货币预测记录

虽然有在公众号「随朱波流」写有关理财的文章,但介于这篇文章很可能会被禁掉,所以在个人博客上做备份

最近LUNA暴跌,从200美元到归零,大概花了3天时间,一个市值高达400亿美元的币种,一个曾今进入前五,最近一年来稳坐前十的币种,就这样崩盘了,伴随而来的是加密货币的崩盘,总市值跌没了5000千万美元(一个礼拜时间)

具体事情自招财大牛猫描述的很到位:惊天动地

有一个韩国团队做的项目叫**UST,这是一个算法稳定币,1:1挂钩美元。**怎么挂钩呢,它采用预言机,通过燃烧另一个叫做LUNA的虚拟币来铸造。

比如LUNA现在价格5美元,那么要铸造1美元的UST,就要燃烧0.2个LUNA。如果LUNA涨到10美元,那就燃烧0.1个。

在这个机制下,如果想买UST的人越多,就会燃烧越多的LUNA,从而推高LUNA的价格,这个大家都能理解吧。LUNA过去一年涨了几十倍。

那怎么刺激大家来买UST呢?韩国团队搞了一个存款项目,只要你存UST进来,**每年固定给你19.8%的利息。**这个收益非常高,因此一经推出就遭到哄抢,UST的锁仓资金一路飙升,最高峰超过了300亿美元。

看到这里你一定会感到疑问,这19.8%的利息是从哪来的,谁给出?确实,由于贷款的利息不够高(12%左右),贷款资金也远远跟不上存款增长的速度,整个项目每天都净亏损几百万。去年底的时候资金池就告急了,韩国团队临时追加了4.5亿进去续命,于是才又拖了半年。

他们之所以花这个钱,是希望趁着快速扩张的阶段,尽可能的给这个UST创造更多线下应用的场景。比如韩国以及东南亚的一些自动售货机都有支持UST支付的网点,只要需求上去了,盘子就能稳住。

但无论怎么样19.8%的利息都很难维持,终于到了今年5月初,韩国团队宣布把利息下调到18%,之后每个月动态调整。他们也不敢一口气把利息降的太多,因为一旦降太多,别人不存UST了,选择出售或者兑换等值的LUNA退出,那汹涌的抛盘有可能引发负面螺旋,直接崩盘。

利息下调后的刚开始几天没啥事,但最近美联储加息,整个加密货币都跌的惨惨的,**终于在前天晚上突然有大资金集中抛售UST,把与美元的钩子打脱掉了。**恐慌逐渐发酵,韩国团队立刻调集资金护盘,想要保住挂钩。

但雪崩效应逐渐被激发,想逃命的人越来越多,抛盘一波比一波猛,韩国团队库存的20多亿资金很快就打光了,然后就是UST的汇率开始一泻千里。

截止最新情况,**UST=0.4美元,脱钩60%,LUNA更惨,2天时间从80元跌到2元。**因为这个盘子的规模很大,UST+LUNA有接近400亿美元的市值,国外很多人和机构都进行了投资,所以这次爆炸可以说是震惊了整个币圈和金融圈。

我这两天看戏看的惊心动魄,有很多韩文的帖子,我用软件翻译,大意都是我不想活了,我这辈子的积蓄都完蛋了,我的杠杆爆仓了之类的。

财富它不会凭空增长,也不会无故消失,它只是不停的在流动。

以及后续文章保大弃小

UST和美元的汇率最低到过0.2,之后触底反弹,一度涨到0.8,之后回落目前在0.6附近。韩国团队围绕UST苦心建设了一整套公链生态,自然不愿意轻易放弃,**他们想出来的办法就是超发LUNA,用这部分LUNA去兑换和烧毁市场上过剩的UST,以挽回汇率。**但LUNA就被砸的很难看,已经从80跌到2毛钱了。

其实就是保一个,弃一个。但这也是没办法的事,因为UST不恢复挂钩LUNA也是必死无疑,等到UST勾回去了,没准LUNA到时候也有机会捞上来。

以及最后的结局——湮灭

UST最新汇率是0.07,已经脱钩美元高达93%,为了拯救UST而疯狂增发的LUNA更惨,跌的只剩1分钱。目前看起来是大的没保住,小的也完蛋,死亡螺旋正在毁灭这两个一周前市值还有400亿美金的项目。

不跌还好,一跌我就来劲了,这几天没怎么写技术文章,一是因为这段时间业务多了,二是加密市场跌出了翔,我,想抄底了。

于是乎,这几天研究了一轮,再次做一下笔记更新

有人刻舟求剑的画了这样的图

加密货币未来预测

来源自推特,出自谁未知

周末看了ahr999的微博合集,很多人估计对他的囤币指标有所了解,看完之后,加深了一点我对“比特币四年一个周期”的认可度,我扒拉下最近几次加密货币的高点和低点,也来刻舟求剑预测下

历年高点时机历年低点时机时间跨度跌幅
2013年11月:¥80002015年1月:¥90014个月88.75%
2017年12月:$200002018年11月:$320011个月84%
2021年11月:$68900?

而更之前的是从32美元跌到2美元,时间不详,来源ahr999

按照时间跨度来看,下一次的低点节点在2022年的10月——2023年1月;而低点价位在7750——11024;低点的时机和跌幅应该有一定的关系。我个人觉得本轮跌幅不会像以往那么多,就像这次BTC没有如愿涨到10万美元一样

此外,我还有一个指标,就是ahr999指标,虽然他现在微博被封,但在github上能找到此前微博的记录,我看完了2020年到2021年的微博,他说的有一点我很赞同——“我们第一个周期能够囤的币,大概会等于我们这辈子能囤的币”

所以这次熊市我会看两个指标,一个是ahr999,如果跌倒0.45以下,我会进场。另一个是跌破80%,也就是13780点,我大概会在一个星期内打完所有的可投资加密货币的钱

而我会买什么币

2022年5月份的想法是

  • BTC
  • ETH
  • ADA
  • XHR

为什么要买这些币,怎么个买入策略?等有空再写吧

我希望低点时机出现在2023年的1月份,因为这个时候会发年终奖,正好可以入场

附加阅读

芝麻开门,显示全文!

Vercel 部署 Node 服务

引子

之前在写面试常客:HTTP 缓存时,曾经就强缓存和协商缓存写过两个 demo,但缓存要在服务端做,只能贴上代码,不能在网页上感受(虽然我贴了 gif)

笔者的所有 demo 例子都放在 github page 上,其特点是不需要服务器即可部署静态资源,但它不具备部署服务端应用能力

最近笔者在了解 CI/CD 方面的知识点,想起了 Vercel,就想着能否将服务端应用架在 vercel 上呢?

Vercel 是什么

Vercel 是一个开箱即用的网站托管平台,方便开发者快速部署自己的网站。它在全球都拥有 CND 节点,因此比 Github 官方自带的 github pages 更加稳定,访问速度更快

Koala 聊开源 曾经对其有过介绍:Vercel 与 Next.js:开源全明星团队背后的商业逻辑

文字版:Vercel 与 Next.js:开源全明星团队背后的商业逻辑

简单的说,它能极简部署应用到服务端,且是免费不用买服务器

官网

Vercel 官网

Vercel 工作流官网(网页效果炫酷)

常见命令行

将 Vercel 下载到全局(npm i vercel -g),不知道有什么命令就-h

vercel帮助

笔者对其了解有限,这里罗列下笔者知道的命令

  • vercel login:登录 Vercel 账号
  • vercel dev:本地开启服务
  • vercel dev --bug:本地开启服务并打印日志
  • vercel:部署本地资源到 Vercel 上
  • vercel --prod:更新本地网页

vercel 可以用 vc 来代替,vc 是 Vercel 的缩写

部署静态服务

我们现在已经对 vercel 有所了解,前文中说到 Vercel 能简化开发者部署服务,那它能简化到什么程度呢?

这里我们从零部署一个简易静态服务

本地安装 Vercel

npm i vercel -g

登录 Vercel

vercel login

vercel login

选择好连接的方式后,会在网站弹出

vercel 登录成功

哟哟,man。what’s your name?

创建一个 HTML 文件,后续我们要将其上传至 Vercel 服务器上

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vercel Demo</title>
  </head>

  <body>
    <h1>Vercel Demo</h1>
  </body>
</html>

本地测试一番,输入命令行

vercel dev

vercel dev

因为我们这是第一次执行,根目录下没有.vercel,所以要填写一些必要信息,这时你的本地和 Vercel 服务器就绑定好了

部署服务

vercel

vercel部署

https://vercel-sample-ten.vercel.app/ 中能访问到我们的静态服务

在截图中我们也看到了这句话Deployed to production. Run vercel --prod to overwrite later,后续我们要更新资源,用 vercel --prod 即可

好了,除去必要的登录,我们就用了三个命令就把本地服务部署到 Vercel 服务器上

  • vercel dev :开发时使用,查看应用是否跑得起来
  • vercel:部署服务
  • vercel --prod:更新应用(资源)

可以登录 Vercel 后台查看部署情况

vercel 后台

部署 Node 服务

回归主题,最终我们想部署的是 Node 服务,是后端服务,而非前端静态资源服务,这是关键

同样建立新项目

mkdir vercel-koa2
cd vercel-koa2
npm init -y
npm i koa -S
touch index.js

编写 index.js 中的内容

const Koa = require("koa");
const app = new Koa();

app.use(async (ctx) => {
  ctx.body = "Hello Vercel";
});

app.listen(3008, () => {
  console.log("3008项目启动");
});

PS: 3000 端口默认会被 Vercel 使用,所以 Koa 服务要换个端口

使用命令vercel dev

vercel koa dev

发现给我报错了,理由是 package.json 的 scripts 中没有 build 快捷符,修改之

...
"scripts": {
    "build": "node index.js",
},
...

再次使用 vercel dev,node 服务跑起来了

于是乎我们部署它

vercel

vercel 部署失败

搞半天没部署上去,后台查看也是无果,呜呼悲哉

google 后,发现原来还有一个 vercel.json,可以用 vercel.json 配置和覆盖 vercel 默认行为

下载 @vercel/node

npm i @vercel/node -S

填写配置:

{
  "version": 2,
  "builds": [
    {
      "src": "index.js",
      "use": "@vercel/node"
    }
  ]
}

执行 vercel 部署服务

vercel koa

访问地址:https://vercel-koa2-t511069160.vercel.app

至此,就完成了 Koa 服务的部署

与部署静态资源多了两个步骤

下载 @vercel/node 和配置 vercel.json

延伸思考

Vercel 当然不止笔者所说的这一功能,它还可以自定义域名、serverless、全球支持的 CDN 等等

可以毫不夸张地说,用 Vercel 来代替繁琐的云服务器,配合 Github Action 做 CI/CD,就

个人开发者或小团队而言,这或许就是神器

后续笔者也会尝试用 Vercel 部署一些小应用,实践出真理

附上项目地址:https://github.com/johanazhu/vercel-demo

参考资料

芝麻开门,显示全文!

疫情期间,我通过项目重学了小程序

前言

这段时间,上海疫情爆发的很严重,3月12日起笔者就被封在小区,至今已有40多天。这期间经历了面试,抢菜,写文章,到现在又捡起了小程序

笔者近几年用 github 来记录年度计划,并每周设立计划执行

年度计划

像前端知识地图、博客、读书还好,都是日常基本功,但是独立作品今年都没有动作,按照进度,上半年怎么也要有一个,于是乎,就想着从想法里拿出一个做成项目

正文

一开始我是想弄我的几行字,因为几行字是H5版本,所以想再做一版小程序,但做完之后感觉有点简单,就打算再做一个

一个机缘巧合的情况下,我想起了三年前尝试做的独立项目——NextDay

邮箱记录

想想,这个页面只有一页,但细节多,还有自定义组件,可以从中学习到做一个小程序会碰到的坑,于是乎,我的小程序之旅有一遭没一遭的做了起来

这里记录笔者做小程序时的困难和解决方案

  • 手画原型

    • NextDay App 为原型展开
  • 小程序开发

  • 自定义导航条

    • 全屏模式下,导航条不让其出现 "navigationStyle": "custom"
    • CSS 也要配合的做,不让他上下滑动postion: fixed
  • 调试接口

    • 需要授权,拿到 Partner NamePartner Secret
    • 也正是因为它严明要开源,所以开源
    • 需要在 header 处加上 Date 和 authorization,authorization 需要加上 md5 加密,可去开源处看代码
  • 主页(主画面)设计、功能

    • swiper 滑动功能接入。接口返回数据是从前几日到今天,如何在 onload 处显示今日数据?swiper 的current设置为今天
    • 布局上采用绝对定位,适配单位上采用rpx和百分比
  • 引入 npm 包

    • 返回的接口数据和页面上的数据是不同的,需要我们从中做转换,遂引入 dayjs
  • 注册小程序

    • 小程序流程
    • 文案
  • 阴影处理

    • 前端这边的设计,text-shadow 属性
  • 上拉出现设置和分享

    • 抽屉组件,点击分享Icon 分享
  • 分享给朋友和朋友圈设置

    • onShareAppMessage 和 onShareTimeline
    • 分享给朋友时,采用的图片应该是 5:4 比例尺寸的图,使用接口返回的小图,意味着需要每次加载时需要记住当前的所在组,每次滑动时都需要改变当前的所在组
  • 手机适配

    • 接口图片有多种,有适配浏览屏的图片和普通图片
    • wxml 中进行条件判断,这里注意不能加空格
  • 接口云端化

    • 因为 NextDay 的接口是 HTTP 协议,小程序不支持 HTTP 协议的,所以就用云开发做转发
    • 主要参考资料:官网微信学堂视频
  • 代码重构

    • 文件布局,设计,云开发,小程序文件,小程序中又有组件、npm 包等功能
    • 接口
  • 设计logo

    • 不用设计,去官网找即可
  • 字体改造、字体适配

    • 需要找到和 App 中一样的字体,比较难,采用字体识别 技术,两种字体,数字日期采用华为最细字体,正文描述字体采用日文字体GothicMB(和 App 中一致)
    • 截图对比,调整大小
  • 点击显示下载按钮

    • 如下所示,找到也没用
  • 下载图片功能

    • 失败,不能下载,图片域名不是 HTTPS,不支持下载,如果把图片放在云函数上免费额度肯定不够,后续再想想需不需要加,有没有必要加
  • 设置主页 关于版本、鸣谢

    • 多一个关于页面,新版本去除,感觉没必要
  • 引入自定义组件

    • 抽屉组件,点击弹出
    • 设置
  • 上拉模糊

    • CSS 样式 backdrop-filter
  • 搜索NextDay 关键字搜索不到

    • 应该是刚上架不久,又没做推广,所以搜不到
    • 要全拼NextDay101
  • 消息提醒

    • 希望每一天能提示弹出,新的想法,不知道会不会打扰到别人,第一版就没做

如要跑通这个项目,首先要去申请 PartnerName 、 Partner Secret,这样就能跑通项目,具体的答疑已经在 NextDay 的README中回答,如还有其他疑问,可在Issuse 区提出

效果截图

GIF动态

GIF

截图

截图1

scrrenshot2

scrrenshot3

小程序

小程序码

项目地址:https://github.com/johanazhu/nextday101

芝麻开门,显示全文!

网红面试题:从输入 url 到看到页面发生了什么

帝王需要约束,所以有了帝王约束力

流程图

这题扎眼看上去没问题,无非是 HTTP 请求到浏览器渲染,但可以聊的东西很多。我想它的执行顺序是,用户输入——开始导航——HTTP 请求——浏览器渲染。其中用户输入、开始导航、浏览器渲染是浏览器方面的知识点,HTTP 请求是 HTTP 方面的知识点

以下就是从输入 url 到看到页面的整个流程图

从url输入到页面渲染

前言

了解”开始导航”之前,需要先知道浏览器架构,简单来说,现代浏览器由 1 个浏览器主进程、1 个 GPU 进程、多个渲染进程、多个插件进程、网络进程、音频进程、存储进程组成

下图是李兵在《浏览器工作原理与实践》中所示,展示 Chrome 浏览器的架构

目前的浏览器架构

以及未来现代浏览器架构示意图:

未来现代chrome浏览器架构

文章现代浏览器内部揭秘中有一张图,是这样描述的

现代浏览器内部解密

图中表明浏览器主进程包含了 UI 线程、网络线程、存储线程,与李兵的观点有所不同。那以谁为准呢?以时间为准,李兵的专栏是 19 年所写,而《现代浏览器内部解密》是 18 年的文章,站在 2022 年的背景,现代浏览器,UI、网络、存储等都已升级为进程,而非是浏览器主进程中的线程

用户输入

当用户在地址栏中输入一个字符串时,地址栏会判断输入的关键字是搜索内容,还是请求的 URL

  • 如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,合成新的带搜索关键字的 URL

    • 例如在 chrome 中搜长泽雅美
    • 在chrome中搜长泽雅美
  • 如果输入内容符合 URL 规则,例如输入azhubaby.com,那么地址栏会根据规则,把这段内容加上协议合成完成的 URL,如 https://azhubaby.com

当用户输入关键字并键入回车之后,意味着当前页面将替换为新的页面,此时浏览器中有个 API——beforeunload,它允许页面在离开之前触发是否一个确认对话框。这里使用此 API,可让浏览器不再导航

// 监听离开页面前的事件
window.addEventListener("beforeunload", (event) => {
  event.preventDefault();
  event.returnValue = "";
});

可在这里看看 beforeunload 的demo

从浏览器架构分工上讲,当用户输入字符串时是 UI 进程(老一点的浏览器是浏览器主进程)在运作

开始导航

当敲下 Enter 键时,UI 进程将指挥权交接给了网络进程。网络进程接受请求指令前,会先查找本地缓存是否有缓存。如果有缓存该资源,那么直接返回资源给浏览器进程;如果在缓存中没找到该资源,那么则正式进入 HTTP 请求阶段

关于 HTTP 缓存方面的知识可以看看这篇——面试常客:HTTP 缓存

HTTP 请求

之前写过一篇TCP/IP 协议及网络分层模型,讲述了 TCP/IP 网络分层协议,它就像搭积木一样,每一层需要下一层的支撑,我们的 HTTP 请求是其 HTTP 协议的应用,需要先连接传输层(TCP)以及更底层网络互连层(IP)

TCP/IP 网络分层模型

而 IP 从哪里来,通过 DNS, 使其域名 和 IP 做映射

我们使用倒推法可以理清“路线”:

HTTP 请求 —— HTTP 协议连接 —— TCP 协议连接 —— IP 协议连接 —— 需要知道 IP——DNS 做域名/IP映射

所以进入 HTTP 请求的第一步是 DNS 解析

DNS 解析

这里对 DNS 不做过多概述,简单来说,它的作用是用域名代替 IP 地址,符合人的记忆。输入du.azhubaby.com ,表示 IP 地址 47.102.152.19 ,你可以在命令行中 ping 一个域名,来求证一下结果

ping域名

HTTP 请求之前的第一步是判断 DNS 中是否有缓存,如果有,直接返回 IP 地址;如果没有,则进行 DNS 解析,并把结果 IP 缓存到 DNS

有了 IP 地址后,IP 层连接成功,接下来就是 TCP 传输层

TCP 连接

这里要看 HTTP 协议的版本,如果是 HTTP/1.1 的话,就要考虑 TCP 队列否饱满,因为 HTTP/1.1 最多允许一个域名连接 6 条 TCP,太多了就要在等待 TCP 队列中排队;如果是 HTTP/2 的话,那就没事,它允许 TCP 并发

这里还要考虑到如果协议是 HTTPS 协议的话,还需要建立一条 TLS 连接

等真正 TCP 连接时,就联想到网红面试题:三次握手、四次挥手

三次握手、四次挥手

为什么是三次握手和四次挥手,因为只有这样才能让双方(客户端和服务端)知道彼此的接收能力和发送能力是没问题的

http-tcp-three-handshakes

步骤为:

  • 客户端提出建立连接,发出客户端 seq:seq=client_isn
  • 服务端收到消息后返回 ack=client_isn+1 和服务端 seq:seq=server_isn
  • 客户端收到后返回ack=server_isn+1 表示收到了

可以理解为男女双方确认关系,男女双方要结婚,怎么办?先见父母得到父母认同,之前听过这样一句话:得不到父母祝福的婚姻是不幸福的(当然,不见父母直接结婚的也有,但不主流)

  • 男方提出去女方家,带上见面礼 seq:seq=男方的诚意
  • 女方家收到见面礼后返回(给男方)红包 ack=我们认可你啦 以及女方去男方家也带上见面礼 seq:seq=女方的诚意
  • 男方家收到见面礼后返回(给女方的)红包 ack=server_isn+1

这个叫确定关系。所以要又来又回三次,双方都确保知道对方的诚意和自己的诚意

那什么是四次挥手呢?

在断开之前,需要进行四次挥手

http-tcp-four-handshakes

为什么要有四次挥手?

主要是为了确保双方都知道对方断开连接

具体步骤为:

  • 客户端第一次发送消息给服务端告诉它需要断开连接
  • 服务端收到消息后返回消息告诉客户端:知道了,为了确保服务端收到了之前所有的 HTTP 请求,服务端需要等一等再断开连接
  • 服务端确认所有的 HTTP 请求都收到了,主动发消息给客户端:我这边所有的请求都处理完了,我也可以断开连接了
  • 客户端收到这个请求后,返回消息告诉服务端:我知道,断开连接吧

主要是为了确认双方的接收能力和发送能力是否正常、制定自己的初始化序列号为后面的可靠性传送做准备

可以理解为一对男女要分手

  • 女方提出分手,说你对我不好,我要分手
  • 男方觉得需求合理,同意分手,但分手之前要把联系方式、合照、各种乱七八糟的的事情算清楚再分手
  • 男方理清楚后,主动发消息给女方,说这边都处理清楚了,以后你是你,我是我,我们可以分手了
  • 女方收到消息后,返回告诉男方:我知道了,分手吧

于是乎,它们就断了,分手手续完成。具体详细的信息可看猿人谷的面试官,不要再问我三次握手和四次挥手,一个字:细

发送 HTTP 请求

TCP 连接已经通了,现在正式发送 HTTP 请求,这里又有的聊了,如 HTTP 的报文内容、请求头、响应头、请求方法、状态码等知识点

首先 HTTP 的报文结构由 起始行 + 头部 + 空行 + 实体组成,简单来说就是 header+body,HTTP 的报文可以没有 body(get 方法),但必须要有 header

请求头由请求行 + 头部字段构成,响应头由状态行 + 头部字段构成

请求行有三部分:请求方法、请求目标和版本号

  • 例如 GET / HTTP/1.1

状态行也有三部分:版本号、状态码和原因字符串

  • 例如 HTTP/1.1 200 OK

在浏览器中,打开 F12,在 NetWork 中任何一个请求中,你都会看到这样的结构

报文结构

这里我们也常会遇到一些例如 GET 和 POST 请求方式的区别、HTTP 状态码等相关的衍生问题

GET 和 POST 请求方式的区别

  • 从缓存角度看,GET 会被缓存,POST 不会被缓存
  • 从参数角度看,GET 通过在 URL 的”?”后以 key=value 方式传参,数据之间以“&”相连接;POST 则要将数据封装到请求体中发送,这个过程不可见
  • 从安全角度看,GET 不安全,因为 URL 可见;POST 较 GET 安全度高
  • 从编码角度看,GET 只接受 ASCII 字符,向服务器发送中文字符可能会出现乱码;POST 支持标准字符集,可以正确传递中文
  • 从数据长度的限制看,GET 一般受 URL 长度限制(URL 的最大长度是 2048 个字符),POST 无限制

HTTP 状态码

RFC 标准把状态码分成了五类 ,用数字的第一位表示分类,而 099 不用,这样状态码的实际可用范围就大大缩小了,由 000999 变成了 100~599。

这五类的具体含义是:

  • 1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
  • 2××:成功,报文已经收到并被正确处理;
  • 3××:重定向,资源位置发生变动,需要客户端重新发送请求;
  • 4××:客户端错误,请求报文有误,服务器无法处理;
  • 5××:服务器错误,服务器在处理请求时内部发生了错误。

目前 RFC 标准里总共有 41 个状态码

101 - Switching Protocols,客户端使用 Upgrade 头字段

200 - 请求成功

204 - 无内容,服务器成功处理了请求,但没有返回任何内容。

206 - 一般用来做断点续传,或者是视频文件等大文件的加载

301 - 永久重定向

302 - 临时重定向

304 - 未修改协商缓存,返回缓存中的数据。它不具有通常的跳转含义,但可以理解成 重定向到缓存的文件(即缓存重定向)

400 - 请求中语法错误

401 - 未授权

403 - 服务器收到请求,但是拒绝提供服务,即资源不可用

404 - 无法找到请求资源

408 Request Timeout - 请求超时

414 - 请求 URI 过长(如图一新浪常有)

500 - 服务器内部错误

501 - 尚未实施:服务器不具备请求功能

502 - 网关错误

503 - 服务器不可用,主动用 503 响应请求或 Nginx 设置限速,超过限速,会返回 503

504 - 网关超时

这里要对 304 做一下说明,当请求头 If-Modified-SinceIf-None-Match 中判断修改时间是否一致(或唯一标识是否一致),是,则返回 304,使用浏览器内存中的本地缓存;不一致则说明要更新,继续请求资源放回给客户端,并带上 Last-ModifiedETag

请求方式

HTTP/1.1 规定了八种方法,都必须是大写形式

  • GET:获取资源,可以理解为读取或者下载数据。只有 GET 请求才能起到缓存效果
  • HEAD:获取资源的元信息
  • POST:像资源提交数据,相当于写入或上传数据
  • PUT:类似 POST
  • DELETE:删除资源
  • CONNECT:建立特殊的连接隧道
  • OPTIONS:列出可对资源实行的方式
  • TRACE:追踪请求 - 响应的传输路径

浏览器渲染

当 HTTP 请求完毕后,断开 TCP 连接,将资源返回给客户端(浏览器)。此时浏览器要判断是否与打开的网站是同一个站点。因为如果是同一个站点的话,则可使用同站点的渲染进程渲染页面,如果不是,浏览器则打开新的渲染进程解析资源

浏览器渲染的大致流程如下图所示:

浏览器渲染大致流程

我们可以将页面渲染分为三个步骤:

解析

  • HTML 被解析为 DOM 树,CSS 被解析为 CSS 规则树,JavaScript 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree

渲染

  • 浏览器引擎通过 DOM Tree 和 CSS Rule Tree 构建 Rendering Tree(渲染树),这其中进行大量的 回流(Reflow) 和 重绘(Repaint)
  • 回流和重绘
    • 回流:意味着元件的几何尺寸变了,需要重新验证并计算 Render Tree
    • 重绘:屏幕的一部分需要重画,比如某个 CSS 的背景色变了,但元件的几何尺寸没有变
    • 回流的成本要比重绘大

绘制

  • 最后通过操作系统(浏览器)的 Native GUI 的 API 绘制

其中,衍生出重绘和回流的问题,提高性能的方法之一就是减少浏览器的渲染时间,其中的一个优化点就是减少重绘和回流

减少回流和重绘的方法

  1. 不要一条条修改 DOM 样式,与其这样,不如预定义好 CSS 的 class,然后修改 DOM 的样式
  2. 把 DOM“离线”后修改
    1. 使用 documentFragment 对象在内存里操作 DOM
    2. 先把 DOM 给 display:none(有一次 Reflow),然后你想怎么改就怎么改,再把它显示出来
    3. clone 一个 DOM 节点到内存里,然后想怎么改就怎么改,改完后和在线的那个交换一下
  3. 不要把 DOM 节点的属性值放在一个循环中当作循环的变量,不然这会导致大量地读写这个节点的属性
  4. 尽可能地修改层级比较低的 DOM
  5. 不要使用 table 布局

造成回流的属性:

width、height、padding、margin、border、position、top、left、bottom、right、float、clear、text-align、vertical-align、line-height、font-weight、font-size、font-family、overflow、white-space

造成重绘的属性:

color、border-style、border-radius、text-decoration、box-shadow、outline、background

记住一点,回流是与几何大小相关,重绘与大小无关

如此,从输入 url 到看到页面的整个流程就走完了

总结

这道题能衍生很多问题,从一题可以测试出面试者的 HTTP、浏览器相关知识。正所谓”鹏怒而飞,其翼若垂天之云;水击三千里,碧空九万丈;好风凭借力,送我上青云。“。这道题之所以能成为经典题,不是没有它的原因的

笔者这里做一个总结,把这题可以衍生的知识点逐一列出,待君思索

浏览器方面

  • 浏览器架构
    • 由什么组成?浏览器主进程、GPU 进程、多个渲染进程、多个插件进程、网络进程、音频进程、存储进程等
    • 渲染进程中有哪些进程?GUI 渲染线程、JS 引擎线程、事件触发线程、网络异步线程、定时器线程
    • 进程和线程的区别?进程是应用程序创建的实例,而线程依托于进程,它是计算机最小的运行单位
  • 浏览器渲染
    • 渲染流程?解析、渲染、绘制
    • 重绘和回流
      • 两者的区别
      • 重绘和回流的属性
      • 如何减少重绘和回流,提高渲染性能

HTTP 方面

  • HTTP 缓存

    • 强缓存

      • HTTP/1.1 Cache-Control
      • HTTP/1.0 Expires
      • Cache-Control > Expires
    • 协商缓存

      • HTTP/1.1 ETag/If-None-Match
      • HTTP/1.0 Last-Modified/If-Modified-Since
      • 精准度:ETag > Last-Modified
      • 性能:Last-Modified > ETag
  • TCP/IP 连接

    • 三次握手、四次挥手
  • 网络层面的性能优化

    • HTTP/1.1 的做法
    • HTTP/2 的做法
    • HTTP/3 的做法
    • 每个阶段采用的性能优化是有所不同的

参考资料

芝麻开门,显示全文!

TCP/IP 协议及网络分层模型

互联网要互联连接,一条消息从发送到接受,都需要走一遍网络协议。我们讲讲 TCP/IP 协议以及网络分层模型

先看网络分层模型

网络分层模型

在网络分层模型中,有两种模型,一是 TCP/IP 标准,它提出得早,1970年就被发明。它提出了分层概念,将网络通信分为四层,分别是链接层、网络层、传输层、应用层

另一种是 OSI,全称叫开放式系统互联通信参考模型,是国际标准,用来统一各种网络协议,始于上世纪70年代后期。是网络分层的”空架子老大”

OSI 模型分成了七层,分别为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

两者的关系就好比手机系统的巨头是 iOS 和 Android,它们制定了标准,因为话语强,所以国际标准以它们为核心再添加指定一些其他标准,但核心是 iOS 和 Android。其中 TCP/IP 就是网络分层模型中的老大,OSI 就像英国女王那样,是皇室象征,是国际标准

TCP/IP 网络分层模型

很有意思,TCP/IP 明明是网络通信协议的统称,却以其中两个核心协议为名字来称呼。它总共有四层,像搭积木一样,每一层需要下层的支撑,同时又支撑着上层。如图所示:

TCP/IP 网络分层模型

最底层也是第一层叫 网络访问(链接)层,Network Access(link)layer,负责在以太网、Wi-Fi 这样底层网络上发送原始数据包。使用 MAC 地址来标记网络上的设备,所以有时候也叫 MAC 层

第二层叫网络互连层,internet layer, IP 协议就处在这一层。因为 IP 协议定义了 IP 地址的概念,所以就在链接层的基础上,用 IP 地址取代 MAC 地址

第三层叫传输层,transport layer,负责保证数据在 IP 地址标记的两点之间可靠地传输,例如 TCP、UDP、SCTP等

第四层叫应用层,顾名思义,是面向具体应用的协议,如 HTTP、SSH、FTP、SMTP、DNS等

按照 TCP/IP 模型,网络通信是一层包着一层,发送端每通过一层则增加首部,接收端每通过一层则删除首部

数据传输过程

这里要注意的是:

  • MAC 层的传输单位是帧(frame)
  • IP 层的传输单位是包(packet)
  • TCP 层的传输单位是段(segment)
  • HTTP 的传输单位则是消息或报文(message)

注:我们常说的丢包丢包丢的就是 IP 包

OSI 网络分层模型

官方定的分层模型叫 OSI,全称是开放式系统互联通信参考模型。你问我支不支持,我是支持的,但它又不是个强制标准,所以一直有四层(TCP/IP模型)、七层(OSI模型)之说。

OSI 网络分层模型

第一层:物理层,physical layer,网络的物理形式,例如电缆、光纤、网卡等;

第二层:数据链路层,data link layer,它基本相当于 TCP/IP 的链接层;

第三层:网络层,network layer,相当于 TCP/IP 的网络层;

第四层:传输层,transport layer,相当于 TCP/IP 的传输层;

第五层,会话层,session layer,维护网络中的连接状态,即保持会话和同步;

第六层,表示层,presentation layer,把数据转换为合适、可理解的语法和语义;

第七层,应用层,面向具体的应用传输数据

两个分层模型的映射关系

分层模型的映射关系

第一层:物理层,TCP/IP 里无对应;

第二层:数据链路层,对应 TCP/IP 的网络访问(链接)层;

第三层:网络层,对应 TCP/IP 的网络互连层;

第四层:传输层,对应 TCP/IP 的传输层;

第五、六、七层:统一对应到 TCP/IP 的应用层。

TCP/IP 协议

上诉我们知道两个网络分层模型是什么,由什么组成,并且知道它们的映射关系。虽然有国际标准,但这个世界还是以 TCP/IP 协议为主要标准。你可以理解为先入为主,也可以理解为单极霸权,甚至可以联想一下5G/6G 标准对世界话语权的重要性,总之,现阶段还是以 TCP/IP 协议为主。

四大层

网络访问(链接)层

MAC 地址,它表示的你的物理世界地址

网络层

IP 协议定义了 IP 地址

电影《黑客军团》中有个片段,被做成了程序员段子

程序员段子

A:你的地址是什么?

B: 173.168.15.10(IP地址)

A:不,你的本地地址呢?

B: 127.0.0.1

A:我的意思是你的物理地址

B: 29:01:38:62:31:58(物理地址指的就是 MAC 地址)

你也可以通过 ipconfig /all 来查看你的所有地址

网络地址

传输层

具有代表性的是 TCP,可以说在 HTTP3 之前,传输层就是 TCP 的天下,它代表的就是传输层,可在 HTTP2 之后,发现影响性能瓶颈的地方成了 TCP 的队头阻塞,所以 UDP 开始活跃起来,真是此一时彼一时

关于 TCP 和 UDP ,这张图表能更好地对比分析

TCP 和 UDP

应用层

应用层有很多协议,例如 HTTP 协议、FTP 协议、SMTP 协议…

应用层协议应用
HTTP万维网
FTP文件传输
DNS域名转换
SMTP电子邮件
TELNET远程终端接入

参考资料

芝麻开门,显示全文!

备忘录:全局下载的npm包

最近升级 node,不小心升级坏了,于是把原先的 Node 应用程序删了之后,输入命令后说”系统找不到命令“。试了各种方法,还是不行,索性把原先的 node 全部删除,重新安装一边

按照安装指示一下把 Node 安装完

把缓存和全局包放在自定义盘下

npm config set cache "D:\Program Files (x86)\NodeJS\node_cache"
npm config set prefix "D:\Program Files (x86)\NodeJS\node_global"

再下载个人认为可以全局下载的 npm 包

cnpm:阿里出品,npm 中国站,特点是下载 npm 包的源放在国内,所以下包速度会变快

npm install -g cnpm --registry=https://registry.npmmirror.com

pm2:小卡的 node 进程管理工具

npm install pm2 -g

nodemon: node 开发者必备,服务热更新

npm i nodemon -g

vuepress:尤式文档

npm install -g vuepress

umi:云谦的脚手架

yarn add umi

pnpm:更快的软件包管理器

npm install -g pnpm

yarn:和 npm 差不多的包管理器,速度快、又稳定

npm install -g yarn

http-server:快速启动 Node 静态服务

npm install -g http-server

serve:快速启动 Node 静态服务

npm install -g serve

npm-check-updates:升级 package.json 中的 各种包

npm install -g npm-check-updates
# 运行
ncu -u

typescript:添加了类型系统的 JavaScript

npm install -g typescript

antfu/ni:自动使用正确的包管理器

npm i -g @antfu/ni

芝麻开门,显示全文!

防抖与节流

最近去面试,又遇到面试官问我防抖与节流了,而明明前几天就看过手写代码,却写不出来。有时候我在想,是不是自己太笨了

回归正题

防抖(debounce)

先不说概念,按自己的理解,在单反里,有防抖机制。因为人在拿着单反的时候会手抖(单反重),按下快门的瞬间,照片会糊,所以有防抖机制,以防止新手把照片拍糊

单反中的防抖是防止抖动,让人拍出清晰的照片,JavaScript 中的防抖是为了什么?

同理,它的作用也是防止抖动。试想当你频繁触发一个事件时,就会引起不必要的性能损失,那么让该事件在停止触发后再触发,以此减少部分性能

防抖的定义

防抖就是要延迟执行,你一直操作触发事件一直不执行,当你停止操作等待多少秒后才执行

也就是说不管事件触发频率有多高,一定在事件触发 n 秒后执行。如果在事件触发的 n 秒又触发了这个事件,那就以新事件的事件为准,n 秒后才执行。总之,要等你触发完事件 n 秒内不再触发事件,它才执行

手写防抖

根据定义,我们知道要在时间 n 秒后执行,那么我们就用定时器来实现

function debounce(event, wait) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer); // 清除setTimeout,使其回调函数不执行
    timer = setTimeout(() => {
      event.apply(this, args);
    }, wait);
  };
}

代码很简单,即当还在触发事件时,就清除 timer,使其在 n 秒后执行,但此写法首次不会立即执行,为其健壮性,需加上判断是否第一次执行的第三个参数 flag,判断其是否立即执行

function debounce(event, wait, flag) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    if (!timer && flag) {
      event.apply(this, args);
    } else {
      timer = setTimeout(() => {
        event.apply(this, args);
      }, wait);
    }
  };
}

防抖场景

窗口大小变化,调整样式

window.addEventListener("resize", debounce(handleResize, 200));

搜索框,输入后 1000 毫秒搜索

debounce(fetchSelectData, 300);

表单验证,输入 1000 毫秒后验证

debounce(validator, 1000);

防抖帝王库

两大工具库都有防抖源码,可供参考

lodash-debounce

underscore-debounce

节流(throttle)

顾名思义,一节一节的流,就好似控制水阀,在事件不断触发的过程中,固定时间内执行一次事件

手写节流

因为是固定时间内执行一次时间,所以我们有两种实现方法,一用时间戳,二用定时器

时间戳

function throttle(event, wait) {
  let pre = 0;
  return function (...args) {
    if (new Date() - pre > wait) {
      // 当 n 秒内不重复执行
      pre = new Date();
      event.apply(this, args);
    }
  };
}

使用时间戳虽然能实现节流,但是最后一次事件不会执行

定时器

function throttle(event, wait) {
  let timer = null;
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        timer = null;
        event.apply(this, args);
      }, wait);
    }
  };
}

使用定时器实现节流,虽然最后一次能触发,但是第一次不会触发

时间戳 + 定时器

为解决第一次和最后一次都可以触发,把两者结合起来

function throttle(event, wait) {
  let pre = 0,
    timer = null;
  return function (...args) {
    if (new Date() - pre > wait) {
      clearTimeout(timer);
      timer = null;
      pre = new Date();
      event.apply(this, args);
    } else {
      timer = setTimeout(() => {
        event.apply(this, args);
      }, wait);
    }
  };
}

节流场景

scroll 滚动

window.addEventListener("scroll", throttle(handleScroll, 200));

input 动态搜索

throttle(fetchInput, 300);

节流帝王库

lodash-throttle

underscore-throttle

总结

防抖:只执行最后一次。事件持续触发,但只有等事件停止触发后 n 秒后才执行函数

节流:控制执行频率。持续触发,每 n 秒执行一次函数

对比图:

防抖节流对比图

线上 demo(司徒正美的 demo):防抖节流

参考资料

芝麻开门,显示全文!

面试题:渲染十万条数据解决方案2

之前写了渲染十万条数据解决方案,有两位朋友给出了不同的答案,这两种方案很厉害,我都没看过,想尝试着回答一下

一个朋友说:

考虑用canvas绘制列表,主要是渲染可视区域内的,滚动的时候进行增量渲染,把节点进行销毁、复用、新增,同时用离屏渲染来复用当前已经绘制过的内容

封印,不会 canvas。后续变强后再返工

还有就是这是一次性返回十万条数据的情况,但真实开发会如此吗?

如果后端一次性放回这么多数据,请求时就要花不少时间,如何提高体验呢?

正确的方法是请求时加上数量和页数,在后端进行分页,当划到指定位置(未触底)时请求接口,接口返回后拼接在原数据后面,虽然DOM数量增多,但有多少人会无聊到一直往下滑动呢

参考资料

芝麻开门,显示全文!

神人操作符:可选链与空值合并

分享两个工作中常用的操作符:可选连和空值合并

?. 操作符

中文翻译为 可选链操作符 ,允许开发者读取深嵌在对象链中的属性值,而不必显示验证每个引用。当引用为空时,表达式停止计算并返回一个未定义的值

let johan = {
  name: "johan",
  age: 28,
  lover: {
    name: "masami",
    age: 34,
  },
};

// 没有 ?. 操作符之前
const johanLover = johan.lover && johan.lover.name;
console.log(johanLover);
// 使用 ?. 操作符
const johanLover = johan.lover?.name;
console.log(johanLover); // masami
// 如果没有值则返回 undefined
const johanLover = johan.lover?.sex;
console.log(sex); // undefined

?? 操作符

另一个会用的是空值合并 ??

控制合并可以真正的检查 nullish 值,而不是 falsely 值。什么是 nullish 值,什么又是 falsely 值?

falsely 值:空字符串、数字0、undefined、null、false、NaN 等

然后,很多情况下你指向检测一个变量是否为空值——undefined 或者 null,就像变量可以是一个空字符串甚至是一个假值

640

参考资料

芝麻开门,显示全文!