查看原文
其他

【第1749期】 从传纸条轻松学习到基本网络概念

胡立 前端早读课 2019-12-10

前言

以青涩的故事来描述网络概念,代入感很强。今日早读文章由@胡立授权分享。

正文从这开始~~

每当提到「网络概念」这几个字,就会想起以前上计算机概论的时候,一大堆名词排山倒海而来,OSI 七层网络模型、TCP/IP 四层、三次握手…虽然说有个大概的感觉,但还是不知道那些理论到底在干嘛。

一直到很后来,我看到一篇利用传纸条来解释 HTTPS 原理的文章,才被打通任督二脉,并且察觉到利用「传纸条」这个很生活化的例子,可以很好地去解释 TCP/IP、HTTP 或甚至是任何跟网络有关的东西。

希望透过这一系列的传纸条小故事,可以让大家理解什么是 TCP/IP,什么又是 HTTP 协定,以及这些东西到底是为了什么而诞生的。虽然说希望读者没有任何相关背景也能看懂,但其实比较适合已经大概知道什么是 TCP/IP 以及 HTTP 的读者观看,会比较容易知道这一篇到底在讲什么。

在开始之前,请大家先在脑中想起自己初高中教室的画面,想起那些课桌椅,想起传过的纸条,这样会更融入这个情境,也对故事更有共鸣。

第一集:告白篇

故事的主角是小明跟小美,是一对青梅竹马,念一样的幼稚园、一样的小学,也念一样的初中(甚至还同班),甚至连住处都只隔了一个楼梯间而已,就在隔壁。

喔对了,这是一个还没有手机的年代。

虽然说一堂课大概 45 分钟左右,也不会到很长,但有些话就是想立刻跟对方分享,一刻都等不及。因此,大家都会利用上课时间传纸条来跟其他人对话(题外话,现在的初高中生是不是都不传纸条了啊,传 Line 就好)。

写纸条是件非常简单的事,只要随意从笔记本上面撕一页下来,写完内容之后折一折,在封面写上「To 小美」,然后把纸条丢给隔壁同学,同学们就会帮你把纸条传到小美手上。因为通常会传纸条给小美的也就只有小明一个人而已,所以也不用写是谁传的,大家都知道一定是小明。

传纸条示意图

原本一切都进行得很顺利,他们两人之间的感情透过一张张的纸条不断堆叠,越来越熟悉彼此,走得一天比一天近。而小明心中也慢慢浮现了一个想法:「好像差不多是时候告白,表示我的心意了」。

可是天不从人愿,没想到才过了几天,小美就被转到最远的十班去了!原本同在一班的两人就这样分隔两地。

小明不是个会轻言放弃的人,否则对不起自己,因此就买通一班到十班认识的同学,约定好互相帮忙跨班级传纸条,只要在封面写上是几班的谁就好,就可以传纸条到其他班级去。

除此之外,也要把寄件人写清楚,才知道纸条回传的时候要给谁。其实就跟寄信差不多啦,只要把收件人跟寄件人写清楚,其他同学就会充当邮差的角色,帮你把纸条传到对方手上。

跟之前的差别在于寄件人以及收件人要写清楚

确认了纸条可以跨班级传送之后,小明就决定要来告白了,于是传了一张「我喜欢你!如果你愿意答应我的告白,放学后操场榕树下见」的老派纸条给小美,并且在放学后到榕树下等着。

等呀等,等呀等,从下午四点等到晚上八点,小美始终没有出现。

原本小明想继续等的,但无奈老天爷都为他哭泣,天空抑制不了冲动,落下雨和眼泪,小明只好狼狈地走回家里。这样也好,就跟倒立泪水就不会流出来一样,只要下雨,就分不清楚脸上的是雨水还是泪水了。

隔天到了学校,心灰意冷的小明发现一件奇怪的事情,那就是小美神色自若,好像什么事情都没发生一样。

咦…该不会对她而言,真的什么都没发生吧?

于是小明在下课的时候鼓起勇气去问了小美:「昨天…你怎么没有来」,没想到小美一脸疑惑,说道:「怎么意思?」。就在这个瞬间,小明知道了一件事:「靠夭,原来我的纸条没有传到」。

确保沟通的办法

双方的沟通如果想要顺畅,说穿了就是要确保以下四点:

  1. 发送方的传送功能

  2. 发送方的接收功能

  3. 接收方的传送功能

  4. 接收方的接收功能

简单来讲就是双方的传送跟接收都必须要正常,否则就会发生遗漏信息的状况,例如说 A 发给 B,可是 B 的接收坏掉了,或者是 B 回传给 A,可是 B 的信息根本发不出去。

那要怎么样确保这四个功能都正常呢?可以透过三个打招呼的步骤来确认。以下直接用小明以及小美来举例:

  1. 小明传给小美:安安,在吗?

  2. 小美收到后回复:在呀,你好

  3. 小明收到后回复:收到,太好了

如果这三个步骤都能够顺利进行,就代表彼此之间沟通的管道无碍。

不过有一点要注意,这边先假设一旦某个功能确认正常以后就不会改变,例如说小明的发送功能如果确认正常,就不会突然失效。以传纸条的例子来说,就是帮你传纸条的同学不会无缘无故消失的意思。

那为什么透过这三个步骤可以确认呢?我们一步一步来分析。

第一步:小明传送给小美:「安安,在吗?」

如果小美没收到这个信息,那就代表小明的发送或者是小美的接收端有问题,所以就可以确认沟通管道是有问题的。

如果小美收到了这个信息,从小美的角度来看,就知道两件事:

  1. 自己的接收功能是好的(否则收不到信息)

  2. 小明的发送功能是好的(否则收不到信息)

第二步:小美传送给小明:「在呀,你好」

小明收到这个信息以后,就可以知道四件事情:

  1. 自己的发送功能是好的(因为小美一定有收到之前传的「安安,在吗」,才会回传这个信息)

  2. 自己的接收功能是好的(不然收不到信息)

  3. 小美的发送功能是好的(不然收不到信息)

  4. 小美的接收功能是好的(因为小美有收到之前传过去的信息)

所以对小明来说,已经可以知道双方的沟通管道是没问题的了。但是对小美来说她还不知道啊,所以还需要最后一个步骤。

第三步:小明传给小美:「收到,太好了」

小美收到信息以后,就又知道两件事情:

  1. 自己的发送功能是好的(因为前一句有发出去,小明才会回这个信息)

  2. 小明的接收功能是好的(因为前一句小明有收到)

或者也可以参考底下的动图,代表的是「在接收到信息时,可以确认的自己的部分」:

从以上的推论可以知道,如果这三个步骤都正常地进行,就代表这两人之间的沟通管道是没有问题的,传纸条都不会被漏掉。

有了这一个确认沟通的机制之后,小明鼓起勇气再一次的告白,而这次终于在榕树下等到小美了 🎉

传纸条守则

从以上的故事当中,可以得知三个传纸条守则:

  1. 写明来源

  2. 写明目的地

  3. 经过三次的前置作业,确保双方都能收发

其实可以把传纸条这件事情「分层」来看,意思就是把传纸条分成不同的层级,每一层都只专注于传纸条的其中一个面向:

最底下一层就是传纸条,指的就是「传递纸条」这个实际的动作,可是如果只有这层的话,你怎么知道要把纸条传给谁?所以中间那层是加上收发地址,才能知道要传给谁嘛。

有了底下两层,已经可以传纸条给任何人了,但没办法保证收发是正常的。所以最上面那层代表的是「如何传送资料」,意思是说你可以使用我们上面提到的机制(三个步骤)来传纸条,就能够确保收发正常,但你不想要也可以。

第一集故事中所对应到的网络概念

为什么会用传纸条来比喻网络?因为这两者的本质是一样的,都是「资讯的传递」。而故事中的纸条其实就是网络世界的「封包」,承载著要传递的资讯。

既然两者是类似的,我们就可以从上面传纸条的守则跟分层当中推导出网路的模型,先从传纸条守则开始:

  1. 写明来源,在网路世界中其实就是大家常听到的 IP 位置

  2. 写明目的地,同上

  3. 经过三次的前置作业,确保双方都能收发。这个在网络里是一个叫做「TCP 三次握手」的东西,TCP(Transmission Control Protocol)是一个通讯协定(Protocol),能够保证双方收发正常。

通讯协定是什么?简单来说就是一些制定好的规则。

以传纸条的例子来说,你可以随意传,但不能保证对方一定收得到。而为了不让小明的悲剧重演,我们发明了一个通讯协定叫做《纸条保证传得到通讯协定》,只要你符合这个协定裡面制定的规则,就能保证纸条的传递。

而这个规则就是我们前面所提到的三次前置作业。

所以在传纸条的时候,你可以选择乱传但对方不一定接收的到,也可以选择遵守《纸条保证传得到通讯协定》,确保对方一定收得到。

网络也是一样的,TCP 是一个保证你的封包可以被对方接收到的协定,确保双方沟通无碍。那要符合什么规则呢?就是我们前面所提到的三次前置作业,而专有名词叫做 TCP 三次握手(Three-way handshake)。

会透过三次握手来确保双方都收发功能都正常,才会开始进行后续的资料交换。

网络的分层

上面提到的纸条分层图表裡面,可以对应到网络的分层:

纸条与网络的分层

以网络来说,最底下一层就是实际传递资料,例如说路由器或是海底电缆,是在真实世界传递讯号的方式。而收发地址这一层前面不是说过了,是透过 IP 位置吗?虽然大家常常会简称为 IP,例如说查 IP 之类的,但 IP 的全名其实是:Internet Protocol,就是我们前面所提到的「协定」。

在这个协定裡面,有一个东西叫做 IP Address,就是你在网路上的地址。所以加上收发地址的这一层,对应到的网路通讯协定就是 IP。

而「如何传送资料」那边,对应到的是 TCP 通讯协定,只要你採用这个协定,就可以保证双方收发正常。

第一集小结

在第一集里面,我们用了传纸条当作生活化的范例,主要是想让大家知道「传纸条就跟网路传封包没两样」,想让大家把这两个东西对应起来,在思考网络概念时就会比较有画面。

而我们也把传纸条与网络进行了「分层」,分层的好处就是可以很明确看到每一层关注的东西都不同,所以只要处理跟那一层有关的事情就好了。

而第一集最后,我们导入了《纸条保证传得到通讯协定》(TCP),来确保收发正常。能够确保传输正常以后,就有了更多传纸条的应用出现。

第二集:便当篇

小明跟小美的故事传遍了整个校园,大家都知道了传纸条的威力,纷纷透过类似的方法传纸条给自己心仪的对象,想要效法他们两个。

在校园当中,有个食量很大的同学千千却看到了传纸条的潜力,认为传纸条不应该只是谈情说爱的工具,它可以做到的事情还多著呢。

例如说:订便当。

千千在校园中开启了代订便当的服务,只要传纸条给她并说明要订什么便当,千千就会回传订便当是否成功以及价钱,从中收取 5~10 元不等的手续费:

订便当示意图

顺带一提,千千的代订便当服务是有遵守《纸条保证传得到通讯协定》的,否则信息漏掉就糟糕了,就会发生有同学以为自己有订但其实没订到的这种状况。

在那个还没有空腹熊猫的年代,代订便当服务在校园中快速窜红,吃腻学校营养午餐的人都透过千千来帮忙代订便当,而千千也从中赚到不少零用钱。

但随着使用人数越来越多,千千发觉到了一些问题。

问题一:数字格式不统一

明明就是订便当这麽简单的一件事,却有一些人很喜欢用不同的数字格式,不写「一个排骨饭」而是写「乙个排骨饭」,还有人是晶晶体的爱好者,写「two 个鸡腿饭」,千千只好无奈地以其人之道还治其人之身,回了个「你在公 three 小?」

这些格式的问题让千千很困扰,因为她必须每一张纸条都很仔细看,才能看出对方到底要几个便当。为了改善这个问题,千千决定统一格式,纸条一律都要长得像:

排骨饭 2 鸡排饭 3 鸡腿饭 5

这种格式,就是「品项 数量」,这样子千千就能够一目了然,迅速知道同学们到底想要什麽。

问题二:特殊需求

虽然说统一了品项的格式,但有时候同学会有些特殊需求,也是让千千很苦恼的问题,例如说:

纸条的特殊格式

上面的纸条就写明了要在第四堂送到,并且要加辣。而每个不同的同学都会有各自的特殊需求,这方面一定也要统一格式才行。

于是千千决定把纸条分成两部分,上半部跟下半部,上半部就叫做 header,下半部叫做 body,灵感来自于人类的头跟身体。header 的部分放特殊需求,而且要符合一定格式;body 的部分则放真正的订单内容,如下图所示:

看起来清爽一百倍

如此一来,千千就可以很快地掌握这张纸条想要表达的意涵。

问题三:回复格式

对于每一张纸条,千千在收到之后都要做后续处理并且回复。

例如说当排骨饭卖完了,就会回说:「排骨卖完了,换一个吧」;或是如果有同学字写太丑导致千千看不懂,就会回说:「第三行字太丑看不懂」,有时候订单一多,千千也会挑客人,不接边缘人的订单,因为数量太少了没赚头,会回说:「一个便当太少了,我不接」。

经过一段时间以后,千千意识到一件事情,那就是会发生的状况其实都差不多,可能就那五六种而已,但以现在的情形来说,每次都要写一样的字,实在是很麻烦的一件事情。

这时候千千有了一个 idea,何不把这些情况变成数字呢?提供大家一张数字代码对照表,例如说 200 对应到「订购成功」,这样只要在纸条上面写 200,对方就知道有成功了,就可以少写一大堆字!

于是千千把状况归类成这六个,并且把这个回传代码对照表发给所有同学:

可是还有一个问题,代码归代码,但有时候除了这些制式化的代码以外,还要写一些解释才行。例如说「400 你纸条内容有错,我看不懂」,还要明确指出是哪一行有错,不然对方也不知道怎麽修正。

于是千千也把自己的回覆分成了 header 与 body 两个部分,利用 header 传这个代码,必要时则在 body 说明附加讯息,会长的像这样:

字太丑错了吗,QQ

如此一来,也成功地解决了问题,大部分情形都只要回传一个数字,不用跟之前一样写那麽多字了,真是可喜可贺。

问题四:动作不统一

这是最后一个问题了,就是动作不统一。

什麽是动作不统一呢?除了订便当以外,还有可能会修改订单啊!例如说有同学突然想翘课去打咖,就要跟千千说要少订一份便当,否则多一份很不方便。或者是原本有同学订了排骨饭,看了鸡排妹的影片之后突然很想吃鸡排,想改成鸡排饭。

这一些东西也应该统一才对,不然对千千来说太累了。

于是千千决定在纸条的 header 加一个栏位,大家可以放四种动词之一:

  1. GET 取得订单信息

  2. POST 订便当

  3. DELETE 取消订单

  4. PUT 修改订单

例如说想要订便当,就是传这样一个纸条:

POST 送达时间:第四堂

鸡腿饭 5 鸡排饭 2

若是想要取消订单,就是这样:

DELETE 鸡腿饭

这样千千就能很明确地看出这张纸条到底是想要干嘛了。

千千的专长之一就是发现问题并且解决它,运用自己的头脑解决了上面四个棘手的问题,把代订便当的事业做得愈来愈大。

第二集所对应到的网络概念

从第二集订便当服务的发展过程当中,我们可以很清楚地知道一件事情。

想要规模化,就要标准化

当你今天只在一个 30 人的班级裡面经营代订便当服务时,哪来这麽多规则要管?反正人很少,你自己每张纸条都慢慢解读就好,也花不多少时间。

可是,当千千想把事业扩张到全校 30000 名同学时(人还是不多就是了,毕竟只有狠爱演工作团队的一半),就必须先把自己的工作流程标准化,因为这是规模化的前置作业。若是没有标准化,解读一张纸条可能要花 10 秒,但如果标准化了,可能只要 1 秒,效率是十倍。

这就是为什麽千千要透过不断地制定规则去约束纸条的内容,因为这样可以让解读纸条这件事变得更快更容易,千千才能处理更多的订单。

在网络世界也是如此,为什麽要有 Protocol?为什麽要有这些规范?因为网路的封包都是由电脑来解读的,它跟人脑最大的差别在于它是死的。

例如说「一个便当」、「乙个便当」、「one 个 bento」,对人类来说很轻易能够看得懂这三个是在指涉同一个东西,但是对电脑来说,只是三个不同的「字串」,是三个不同的词,它没办法知道这三个其实是一样的。

所以必须制定一套标准,例如说「便当 1」这样的格式,让输入全都符合这一套格式,电脑就只要去解析这一个单一格式就好。

开头有提到说这个订便当的服务是建立在《纸条保证传得到通讯协定》之上,其实除了订便当以外,还能发展出更多的服务,例如说订饮料之类的,这些说穿了其实都是「如何应用传纸条」来建立更多服务。

而这一段故事裡所提到的「订便当服务」,对应到网路中其实就是我们最常看到的 HTTP(Hypertext Transfer Protocol):

网络与传纸条的分层

这边会是我们传纸条模型的最上层,因为确认如何传送资料以后,就可以发展出更多实际应用。例如说「用纸条谈情说爱」是一种传纸条的应用,「用纸条订便当」也是,你想拿来做什麽就可以做什麽。

在前面的故事中,我们可以得出底下的传纸条守则:

  1. 标准化内容格式

  2. 分为 header 跟 body

  3. 用状态码标准化结果

  4. 用动词标准化动作

而这四个其实就是 HTTP 通讯协定的内容。

在这边简单介绍一下 HTTP,例如说你透过浏览器输入 http://google.com 并按下 Enter 之后,其实背后就是发了一个:

GET(用动词标准化动作)

http://google.com

的纸条出去

而 Google 的 Server 在收到这张纸条并处理过后,就会把画面传回来,回传的格式其实就跟千千回传的格式差不多:

Status: 200(用状态码标准化结果)<html>…..</html>(分为 header 与 body)

所以网页运作背后其实是透过 HTTP 这个协定,也可以想成就是订便当这个协定,只是把便当换成了网页。(或是更广义来说,把便当换成了「资源(resource)」)。

这样你在学习 HTTP 这个通讯协定的时候,就不会对 Status code 感到陌生,它就只是传纸条故事中的「用状态码标准化结果」而已;也不会觉得那些 HTTP method 很奇怪,那也只是故事中的「用动词标准化动作」。

只要你能想起之前订便当的故事,就能知道为什麽会有这些东西,以及这些东西到底在干嘛。

第三集:发大财篇

透过代订便当服务,千千在学生时期就赚到了人生中的第一桶金。但千千的野心并没有在此而止步,下一个阶段,她想让同班同学们一起发大财。

具体上应该怎麽做呢?

就是发展不同的业务,并且让每个同学都负责一个独立的业务,才不会彼此互相干涉。举例来说,A 同学负责代订饮料服务,B 同学负责 NBA 即时战况报导,C 同学负责借篮球等等。

但马上就碰到一个问题,那就是纸条该传给谁?

如果传给 A 同学本人,那 A 同学请假怎麽办?所以应该是传到班级就好,并且标注需要的服务,例如说:

From 一班杂鱼 To 八班:订饮料

POST 送达时间:三点前

全糖珍珠奶茶 5 过气的黄金比例翡翠柠檬 1

在冒号后面接上需要的服务,八班的人自然就会把这个纸条传给对应到的同学。

这些字写久了手也是会痠的,于是同学们决定效法之前千千提出的「数字对照表」的概念,把这些服务也换成数字:

服务代码一览表

这样子用数字来表示就好,方便很多,大家只要有这个表格就可以来做对照。当八班的同学收到这张纸条以后,就会根据上面的服务转给负责处理的那一位同学。

而接下来又碰到一个问题,就是格式。千千当初是以订便当为基础,才有了现在看到的格式,例如说状态代码、动词以及 header、body 等等的设置。

可是有些服务根本用不到这些啊!

例如说「帮忙借篮球」服务,就只需要知道几颗就好,不会有修改或是取消的事情发生,根本不需要这麽複杂的格式。

于是借篮球服务就有了自己独特的格式,只要写一两个字就好:

超级简洁

再来呢,原本的传纸条也都是建立在《纸条保证传得到通讯协定》之上,每次传之前都要先经过三次的确认。但有些同学发现「NBA 即时战况」这个服务并不需要这件事,因为每隔三秒钟,就会写一张新的纸条到指定的班级去。

就算中间漏掉两张纸条,也只损失了六秒钟的战况而已,这时间可能比数完全没有变动!再者,如果少掉了前面三次确认这个前置作业,传纸条的速度就可以更快,可以更即时地提供战况讯息。

于是,他们后来就决定不让 NBA 即时战况这个服务遵守《纸条保证传得到通讯协定》,因为根本没有必要。在这个服务上,不遵守这个协定可以得到更多的好处。

就这样,千千他们班开发出来的服务愈来愈多元,全校三个年级的学生都是他们的客户,实现了千千一开始的愿景:全班一起发大财。

至于之后千千碰到竞争对手恶意窜改纸条,就是另外一个故事了。

第三集所对应到的网络概念

这一集里面我们多了许多不同的服务,也产生了对应的服务代码,这些对应到网路世界裡就是 Port(连线埠,中国翻叫端口)的概念,一台主机上面可以跑很多不同的服务,但你要怎麽区别呢?就是利用 Port(服务代码)。

就像千千班上一样,有订便当、订饮料还有借篮球,要把纸条传到「八班:3000」才是订饮料这个服务。网路也是如此,一台主机上有很多服务,你一定要传到「Server IP:80」才是 HTTP 伺服器这个服务。

每一种服务都有预设的 Port,例如说 HTTP 是 80,FTP(File Transfer Protocol)是 21,所以你会发现在使用浏览器的时候没有输入 Port,因为预设就是 80,所以不用特别填写。

这可以对应到我们之前的图表,在最上面一层「实际应用」,可以有不同的应用,例如说订便当跟订饮料。网路世界的话则是 HTTP 跟 FTP 这两个不同的通讯协定。

实际应用的不同

除此之外,第二集裡面我们以订便当做基础,发展出了一堆格式,而这些格式其实都只是为了更方便订便当,对其他的服务不一定适用。

举例来说,「借篮球」就不适用,而且还造成反效果,增添了複杂度。或是故事中的 NBA 即时战况也一样,甚至连第一集尾声的《纸条保证传得到通讯协定》都不适用,因为漏掉讯息根本没差,更重要的是传输的即时性。

这边对应到网路的话,就是说不同服务可以有不同的内容格式,不一定都要遵照同一个。而 NBA 即时战况那个案例告诉我们说,可以不遵守《纸条保证传得到通讯协定》。

在传输资料的时候有很多种协定可以选择, TCP 是一种,就是我们故事中的《纸条保证传得到通讯协定》,还有另外一种叫做 UDP(User Datagram Protocol),就是上面提到的「漏掉讯息没差,重要的是传输即时性」的通讯协定。

不同传送资料的方式

在「如何传送资料」这一层裡面,有可以保障收发正常的 TCP,也有不能保障但是效能较好的 UDP,而上层的实际应用可以选择任何一种。

这边的图可能有点小误导所以要特别说明,图片并不是说 FTP 建立在 UDP 之上,只是想表达应用这一层有 HTTP 「与」 FTP,传输这层有 TCP「与」UDP,而 HTTP 与 FTP 事实上都选择了以 TCP 作为传输方式。

举例来说,HTTP 就像订便当一样,为了保证讯息收发正常,就选择了 TCP(不过最新的 HTTP/3 又是另一个故事了,这个先跳过);而通常那种即时通话或是视讯的通讯协定都会选择 UDP,因为少掉几个封包根本没有差,比较重要的是传输的速度,就像 NBA 即时战况一样。

总结

在上面连续三集的故事裡面,我们透过传纸条这个实际案例,去想像会碰到的困难以及解决方法,每一种优化方法都其来有自,绝对不是凭空产生的。

而传纸条的这个模型,其实就是俗称的 TCP/IP 四层模型:

四层模型

这个模型会把网路分成四层,每一层都关注不同的面向,例如说传输层指的就是「你要以什麽方式来传送资料?」,而应用层是最贴近我们的一层,就是实际应用。这四层关注的对象也可以透过传纸条的例子来看,会清楚很多。

对我来说,与其去思考「为什麽会有 HTTP?」、「为什麽要这样分层?」,不如先从传纸条的例子开始慢慢去想,去想说当服务变大的时候该怎麽办,当格式不统一的时候该做些什麽,这时候就会发现这些问题的解答,就是 Protocol 出现的原因。

传纸条的故事可以直接对应到网路相关的例子,而这样子的对应我认为很能帮助大家对于网路相关知识的理解。

希望这一篇对大家有帮助,能够更理解 TCP/IP 以及 HTTP 这些通讯协定。

关于本文 作者:@胡立 原文:https://medium.com/@hulitw/learning-tcp-ip-http-via-sending-letter-5d3299203660

他曾分享过


【第1698期】白话Session与Cookie:从经营杂货店开始


【第1655期】零基础的小明要如何成为前端工程师?


【第1571期】从 React 源代码看 keyPress 与 keyDown 事件


为你推荐


【第1457期】CSS 与网络性能


【第1205期】网络现状:性能提升指南


【第1601期】JavaScript函数的6个基本术语

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存