不可思议的剖面大战舰用p2p怎么看片

额。。最高票答案没答到点子上,最后怎么跑到Nodejs上去了。。Websocket只是协议而已。。&br&我一个个来回答吧&br&&br&&b&一、WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)&/b&&br&首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以通过这样一张图理解&br&&img src=&/ec133b0e6d7e6d0e194b4c_b.jpg& data-rawwidth=&374& data-rawheight=&133& class=&content_image& width=&374&&有交集,但是并不是全部。&br&另外Html5是指的一系列新的API,或者说新规范,新技术。Http协议本身只有1.0和1.1,而且跟Html本身没有直接关系。。&br&通俗来说,你可以用HTTP&b&协议&/b&传输非Html&b&数据&/b&,就是这样=。=&br&再简单来说,&b&层级不一样&/b&。&br&&br&&b&二、Websocket是什么样的协议,具体有什么优点&/b&&br&首先,Websocket是一个&b&持久化&/b&的协议,相对于HTTP这种&b&非持久&/b&的协议来说。&br&简单的举个例子吧,用目前应用比较广泛的PHP生命周期来解释。&br&1) HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么&b&在&/b&HTTP1.0&b&中&/b&,这次HTTP请求就结束了。&br&在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。&br&但是请记住 Request = Response , 在HTTP中永远是这样,也就是说一个request只能有一个response。而且这个response也是&b&被动&/b&的,不能主动发起。&br&&br&&b&教练,你BB了这么多,跟Websocket有什么关系呢?&/b&&br&_(:з」∠)_好吧,我正准备说Websocket呢。。&br&首先Websocket是基于HTTP协议的,或者说&b&借用&/b&了HTTP的协议来完成一部分握手。&br&在握手阶段是一样的&br&-------以下涉及专业技术内容,不想看的可以跳过lol:,或者只看加黑内容--------&br&首先我们来看个典型的Websocket握手(借用Wikipedia的。。)&br&&div class=&highlight&&&pre&&code class=&language-text&&GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
&/code&&/pre&&/div&熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。&br&我会顺便讲解下作用。&br&&div class=&highlight&&&pre&&code class=&language-text&&Upgrade: websocket
Connection: Upgrade
&/code&&/pre&&/div&这个就是Websocket的核心了,告诉Apache、Nginx等服务器:&b&注意啦,窝发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。&/b&&br&&div class=&highlight&&&pre&&code class=&language-text&&Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
&/code&&/pre&&/div&首先,Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:&b&泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。&/b&&br&然后,Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。简单理解:&b&今晚我要服务A,别搞错啦~&/b&&br&最后,Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本),在最初的时候,Websocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么Firefox和Chrome用的不是一个版本之类的,当初Websocket协议太多可是一个大难题。。不过现在还好,已经定下来啦~大家都使用的一个东西~ 脱水:&b&服务员,我要的是13岁的噢→_→&/b&&br&&br&然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦!&br&&div class=&highlight&&&pre&&code class=&language-text&&HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
&/code&&/pre&&/div&这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~&br&&div class=&highlight&&&pre&&code class=&language-text&&Upgrade: websocket
Connection: Upgrade
&/code&&/pre&&/div&依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。&br&然后,Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。服务器:&b&好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。。&/b&&br&后面的,Sec-WebSocket-Protocol 则是表示最终使用的协议。&br&&br&至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。&br&具体的协议就不在这阐述了。&br&------------------技术解析部分完毕------------------&br&&br&&img src=&/afe119b52eedabc2dfa9661_b.jpg& data-rawwidth=&161& data-rawheight=&187& class=&content_image& width=&161&&你TMD又BBB了这么久,那到底Websocket有什么鬼用,http long poll,或者ajax轮询不都可以实现实时信息传递么。&br&&img src=&/edb1ed826e264_b.jpg& data-rawwidth=&176& data-rawheight=&193& class=&content_image& width=&176&&&br&&br&好好好,年轻人,那我们来讲一讲Websocket有什么用。&br&来给你吃点胡(苏)萝(丹)卜(红)&br&&img src=&/31ddf0cfbeecefb5f1ff_b.jpg& data-rawwidth=&53& data-rawheight=&65& class=&content_image& width=&53&&&b&三、Websocket的作用&/b&&br&在讲Websocket之前,我就顺带着讲下 long poll 和 ajax轮询 的原理。&br&首先是 ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。&br&场景再现:&br&客户端:啦啦啦,有没有新信息(Request)&br&服务端:没有(Response)&br&客户端:啦啦啦,有没有新信息(Request)&br&服务端:没有。。(Response)&br&客户端:啦啦啦,有没有新信息(Request)&br&服务端:你好烦啊,没有啊。。(Response)&br&客户端:啦啦啦,有没有新消息(Request)&br&服务端:好啦好啦,有啦给你。(Response)&br&客户端:啦啦啦,有没有新消息(Request)&br&服务端:。。。。。没。。。。没。。。没有(Response) ---- loop&br&&br&long poll &br&long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。&br&场景再现&br&客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)&br&服务端:额。。
等待到有消息的时候。。来 给你(Response)&br&客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop&br&&br&从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,&b&被动性&/b&。&br&何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。&br&简单地说就是,服务器是一个很懒的冰箱(这是个梗)(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。&br&&br&说完这个,我们再来说一说上面的缺陷(原谅我废话这么多吧OAQ)&br&从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。&br&ajax轮询 需要服务器有很快的处理速度和资源。(速度)&br&long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)&br&所以ajax轮询 和long poll 都有可能发生这种情况。&br&&br&&b&客户端:啦啦啦啦,有新信息么?&/b&&br&&b&服务端:月线正忙,请稍后再试(503 Server Unavailable)&/b&&br&&b&客户端:。。。。好吧,啦啦啦,有新信息么?&/b&&br&&b&服务端:月线正忙,请稍后再试(503 Server Unavailable)&br&&/b&&br&&b&客户端:&/b&&img src=&/7c0cf075c7ee4cc6cf52fc10_b.jpg& data-rawwidth=&143& data-rawheight=&50& class=&content_image& width=&143&&&br&&b&然后服务端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。(我错了。。这又是梗。。)&/b&&br&&br&--------------------------&br&&b&言归正传,我们来说Websocket吧&/b&&br&通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。&br&一种需要更快的速度,一种需要更多的'电话'。这两种都会导致'电话'的需求越来越高。&br&哦对了,忘记说了HTTP还是一个无状态协议。(感谢评论区的各位指出OAQ)&br&通俗的说就是,服务器因为每天要接待太多客户了,是个&b&健忘鬼&/b&,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。&br&&br&所以在这种情况下出现了,Websocket出现了。&br&他解决了HTTP的这几个难题。&br&首先,&b&被动性&/b&,当服务器完成协议升级后(HTTP-&Websocket),服务端就可以主动推送信息给客户端啦。&br&所以上面的情景可以做如下修改。&br&客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)&br&服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)&br&客户端:麻烦你有信息的时候推送给我噢。。&br&服务端:ok,有的时候会告诉你的。&br&服务端:balabalabalabala&br&服务端:balabalabalabala&br&服务端:哈哈哈哈哈啊哈哈哈哈&br&服务端:笑死我了哈哈哈哈哈哈哈&br&&br&就变成了这样,只需要经过&b&一次HTTP请求&/b&,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你)&br&这样的协议解决了上面同步有延迟,而且还非常消耗资源的这种情况。&br&那么为什么他会解决服务器上消耗资源的问题呢?&br&其实我们所用的程序是要经过两层代理的,即&b&HTTP协议在Nginx等服务器的解析下&/b&,然后再传送给相应的&b&Handler(PHP等)&/b&来处理。&br&简单地说,我们有一个非常快速的接&b&线员(Nginx)&/b&,他负责把问题转交给相应的&b&客服(Handler)&/b&。&br&本身&b&接线员基本上速度是足够的&/b&,但是每次都卡在&b&客服(Handler)&/b&了,老有&b&客服&/b&处理速度太慢。,导致客服不够。&br&Websocket就解决了这样一个难题,建立后,可以直接跟接线员建立持&b&久连接&/b&,有信息的时候客服想办法通知接线员,然后&b&接线员&/b&在统一转交给客户。&br&这样就可以解决客服处理速度过慢的问题了。&br&&br&同时,在传统的方式上,要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要&b&重新传输identity info(鉴别信息)&/b&,来告诉服务端你是谁。&br&虽然接线员很快速,但是每次都要听这么一堆,效率也会有所下降的,同时还得不断把这些信息转交给客服,不但浪费客服的&b&处理时间&/b&,而且还会在网路传输中消耗&b&过多的流量/时间。&/b&&br&但是Websocket只需要&b&一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中&/b&,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了接线员要反复解析HTTP协议,还要查看identity info的信息。&br&同时由&b&客户主动询问&/b&,转换为&b&服务器(推送)有信息的时候就发送(当然客户端还是等主动发送信息过来的。。)&/b&,没有信息的时候就交给接线员(Nginx),不需要占用本身速度就慢的&b&客服(Handler)&/b&了&br&--------------------&br&至于怎么在不支持Websocket的客户端上使用Websocket。。答案是:&b&不能&/b&&br&但是可以通过上面说的 long poll 和 ajax 轮询来 &b&模拟出类似的效果&/b&&br&-----&br&_(:з」∠)_两天写了两篇科普类文章。。好累OAQ,求赞。。&br&对啦,如果有错误,欢迎大家在底下留言指出噢~
额。。最高票答案没答到点子上,最后怎么跑到Nodejs上去了。。Websocket只是协议而已。。 我一个个来回答吧 一、WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算) 首先HTTP…
&p&最近刚搞完一轮,作为评委也作为被评人,从自己写给团队的一些总结中摘抄部分,希望能给提问者一些启发。&/p&&h2&&b&1. 颜值即正义&/b&&/h2&&p&很多同学的 Slide,连最基础的排版都没有注意,这是第一印象,至少要简洁。&/p&&ul&&li&必读 &a href=&///?target=https%3A///sparanoid/chinese-copywriting-guidelines& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&中文排版指北&i class=&icon-external&&&/i&&/a&&/li&&li&文字一定要精练,关键词高亮加粗。&/li&&li&提炼关键词,用最精练的语言来阐述,关键词高亮,关键地方可以配图(配图一定要优化好,不要投屏后看不清,适当减少不必要的内容)。&/li&&/ul&&h2&&b&2. STAR 法则&/b&&/h2&&p&述职阶段只有短短 15 分钟,所以一定要抓重点,主要目的是给评委一个全貌的印象,突出关键词,便于引导后面的互动环节。&/p&&p&因此,很多同学常常犯的错误有:&/p&&ul&&li&大篇幅的介绍业务多牛逼 -& &b&晋升汇报 ≠ 绩效汇报&/b&。&/li&&li&常常陷入技术细节,长篇累牍 -& 这也不是技术分享会。&/li&&li&思维跳跃,前因后果不清晰 -& 其实评委不一定熟悉你的业务背景。&/li&&li&罗列 + 罗列 + 罗列 -& 你做 100 个 P6 的活不代表你达到 P7,只能说明你熟。&/li&&/ul&&blockquote&STAR 法则 &br&&b&情景(Situation) + 任务(Task) + 行动(Action)
+ 结果(Result)&/b&&br&一种结构化的描述方式,能让你的阐述更有层次性和逻辑性。&/blockquote&&ul&&li&首先,挑选你最重要的几个事迹,3~4 即可,不要太多,每一个都有目的性的说明你的某一个特质,阐述为什么你能达到下一个职级的要求。&/li&&li&&b&情景(Situation)&/b&&/li&&ul&&li&业务的背景,价值。&/li&&li&通常评委不熟悉你的业务背景。&/li&&li&一定要&b&非常简要&/b&。&/li&&/ul&&li&&b&任务(Task)&/b&&/li&&ul&&li&你在这个工作中所承担的任务或者职责。&/li&&li&当时遇到的问题和挑战点,难点。&/li&&/ul&&li&&b&行动(Action)&/b&&/li&&ul&&li&为了达到最终目标,你是如何展开行动的,采取了哪些措施和方法来解决问题。&/li&&li&即&b&你个人的价值体现&/b&,这块是重点,而且要阐述清楚你的思路。&/li&&/ul&&li&&b&结果(Result)&/b&&/li&&ul&&li&最终的结果是怎么样的,包括业务的,技术的,团队的。&/li&&li&你自己的总结和反思。&/li&&li&「如果从现在的角度回去审视这件事,你又会有哪些不一样的做法? 」 — 评委爱问的问题。&/li&&/ul&&li&一句话总结:什么背景下,遇到了什么痛点,我的思路是怎么样的,具体做了哪几件重要的事,最终的结果怎么样?&/li&&/ul&&p&&br&&/p&&h2&&b&3. Slide vs 演讲&/b&&/h2&&p&一般的流程是:&b&`主管介绍 -& 候选人述职 -& 评委提问互动环节 -& 评委讨论 -& 评委反馈`&/b&&/p&&p&自我介绍是给评委的第一印象,不要废话太多,列出自己的核心特质,并在后面的描述中进行呼应。&/p&&p&Slide 控制在 10 页左右,演讲者备注必须写,写好后,多练,练到不用看备注。一定要&b&多演练,多演练,多演练&/b&,而且述职环节往往时间不多,短短十几分钟。&/p&&p&这短短的时间内,你的主要目的是非常精准的阐述你能达到下一个职级的理由,包括从业绩,技术,团队等维度。&/p&&p&切忌讲太多细节,觉得重要的地方,可以重点提醒下评委,然后说这块如果有兴趣的话,可以等会提问环节进一步交流。(引导评委的思路)&/p&&p&&br&&/p&&h2&&b&4. 其他&/b&&/h2&&p&很多同学平时埋头于业务中,到晋升季时整理材料,才发现忙忙碌碌一整年,却没有太多值得挖掘的事迹。&/p&&p&建议多抬头看天,多跟不同业务线的同学交流。多自我审视下,如果想明年晋升,倒推过来,现在应该做些什么?&/p&&p&要多分享,多交流,真正能讲明白给别人听的,才是你真正吃透的知识。&/p&&p&技术要结合业务,不能空中楼阁。&/p&&p&&br&&/p&&p&最后,一切看脸。&/p&
最近刚搞完一轮,作为评委也作为被评人,从自己写给团队的一些总结中摘抄部分,希望能给提问者一些启发。1. 颜值即正义很多同学的 Slide,连最基础的排版都没有注意,这是第一印象,至少要简洁。必读 文字一定要精练,关键词高亮加粗。提炼关键…
ppt:&br&&br&多图多数据&br&少代码&br&少文字&br&&br&演讲策略:&br&&br&&br&业务背景是什么,或者发现了什么问题,想去解决&br&考虑的技术方案是什么&br&遇到什么问题&br&怎么解决的(包含优化方案)&br&结果收益是什么样的&br&&br&找一个最能突出个人价值的项目重点阐述,其他项目一句带过,毕竟时间有限&br&&br&我想说的是:你做的事情是对公司有益的,不是对个人有益的,在这里要搞清楚重点,公司获得最大收益,你才能获得最大收益&br&&br&评委一般都是其他部门的人,都是技术出身,肯定关心的是你解决问题的能力,不会关注你的业务本身&br&&br&你对团队的贡献:&br&&br&包括指导初、中级工程师解决问题&br&对内对外的技术分享&br&通过各种途径影响团队对技术的追求和进步,扩大技术视野,提高团队整体技术水平&br&&br&未来计划:&br&&br&这个看你怎么思考了,思考对公司项目或者产品技术方案的优化,思考怎么打造更高效的团队,思考工程师在整个行业内如何发展&br&&br&我在两个月前通过了评审,当时自己的一些做法,仅供参考
ppt: 多图多数据 少代码 少文字 演讲策略: 业务背景是什么,或者发现了什么问题,想去解决 考虑的技术方案是什么 遇到什么问题 怎么解决的(包含优化方案) 结果收益是什么样的 找一个最能突出个人价值的项目重点阐述,其他项目一句带过,毕竟时间有限 我想…
&img src=&/v2-aa0e329fecf1b_b.jpg& data-rawwidth=&2560& data-rawheight=&1172& class=&origin_image zh-lightbox-thumb& width=&2560& data-original=&/v2-aa0e329fecf1b_r.jpg&&&blockquote&很荣幸在今年 2 月到 5 月的时间里,以顾问的身份加入饿了么,参与 PWA 的相关工作。这篇文章其实最初是在 I/O 之前以英文写作发表在 medium 上的:&a href=&/?target=https%3A///elemefe/upgrading-ele-me-to-progressive-web-app-2a& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Upgrading Eleme to Progressive Web Apps&i class=&icon-external&&&/i&&/a&,获得了一定的关注。所以也决定改写为中文版本再次分享出来,希望能对你有所帮助 ;)
本文首发于&a href=&/?target=http%3A//geek.csdn.net/news/detail/210535& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&《程序员》&i class=&icon-external&&&/i&&/a&7 月刊,现同步发布于 &a href=&/ElemeFE& class=&internal&&饿了么前端 - 知乎专栏&/a&、 &a href=&/?target=https%3A//huangxuan.me/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Hux Blog&i class=&icon-external&&&/i&&/a&,转载请保留链接。&/blockquote&&p&自 Vue.js 官方推特第一次&a href=&/?target=https%3A///vuejs/status/239619& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&公开&i class=&icon-external&&&/i&&/a&到现在,我们就一直在进行着将&a href=&/?target=https%3A//h5.ele.me/msite/%23pwa%3Dtrue& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&饿了么移动端网站&i class=&icon-external&&&/i&&/a&升级为 &a href=&/?target=https%3A///web/progressive-web-apps/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Progressive Web App&i class=&icon-external&&&/i&&/a& 的工作。直到近日在 Google I/O 2017 上&a href=&/?target=https%3A///status/5652& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&登台亮相&i class=&icon-external&&&/i&&/a&,才终于算告一段落。我们非常荣幸能够发布全世界第一个专门面向国内用户的 PWA,但更荣幸的是能与 Google、UC 以及腾讯合作,一起推动国内 web 与浏览器生态的发展。&/p&&h2&&b&多页应用、Vue、PWA?&/b&&/h2&&p&对于构建一个希望达到原生应用级别体验的 PWA,目前社区里的主流做法都是采用 SPA,即单页面应用模型(Single-page App)来组织整个 web 应用,业内最有名的几个 PWA 案例 &a href=&/?target=https%3A///2017/how-we-built-twitter-lite& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Twitter Lite&i class=&icon-external&&&/i&&/a&、 &a href=&/?target=https%3A///progressive-web-apps/building-flipkart-lite-a-progressive-web-app-2c211e641883& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Flipkart Lite&i class=&icon-external&&&/i&&/a&、&a href=&/?target=https%3A///engineering-housing/progressing-mobile-web-fac3efb8b454& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Housing Go&i class=&icon-external&&&/i&&/a& 与 &a href=&/?target=https%3A//shop.polymer-project.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Polymer Shop&i class=&icon-external&&&/i&&/a& 无一例外。&/p&&p&然而饿了么,与很多国内的电商网站一样,青睐多页面应用模型(MPA,Multi-page App)所能带来的一些好处,也因此在一年多将移动站从基于 Angular.js 的单页应用重构为目前的多页应用模型。团队最看重的优点莫过于页面与页面之间的隔离与解耦,这使得我们可以将每个页面当做一个独立的“微服务”来看待,这些服务可以被独立迭代,独立提供给各种第三方的入口嵌入,甚至被不同的团队独立维护。而整个网站则只是各种服务的集合而非一个巨大的整体。&/p&&p&与此同时,我们仍然依赖 &a href=&/?target=http%3A//vuejs.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vue.js&i class=&icon-external&&&/i&&/a& 作为 JavaScript 框架。Vue 除了是 React/Angular 这种“重型武器”的竞争对手外,其轻量与高性能的优点使得它同样可以作为传统多页应用开发中流行的 “jQuery/Zepto/Kissy + 模板引擎” 技术栈的完美替代。Vue 提供的组件系统、声明式与响应式编程更是提升了代码组织、共享、数据流控制、渲染等各个环节的开发效率。&a href=&/?target=http%3A//localhost%3A/12/upgrading-eleme-to-pwa/%28https%3A///watch%3Fv%3DpBBSp_iIiVM%29& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vue 还是一个渐进式框架&i class=&icon-external&&&/i&&/a&,如果网站的复杂度继续提升,我们可以按需、增量地引入 Vuex 或 Vue-Router 这些模块。万一哪天又要改回单页呢?(谁知道呢……)&/p&&p&2017 年,PWA 已经成为 web 应用新的风潮。我们决定试试,以我们现有的“Vue + 多页”的架构,能在升级 PWA 的道路上走多远,达到怎样的效果。&/p&&h2&&b&实现 “PRPL” 模式&/b&&/h2&&p&&a href=&/?target=https%3A///web/fundamentals/performance/prpl-pattern/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&“PRPL”&i class=&icon-external&&&/i&&/a&(读作 “purple”)是 Google 的工程师提出的一种 web 应用架构模式,它旨在利用现代 web 平台的新技术以大幅优化移动 web 的性能与体验,对如何组织与设计高性能的 PWA 系统提供了一种高层次的抽象。我们并不准备从头重构我们的 web 应用,不过我们可以把实现 “PRPL” 模式作为我们的迁移目标。“PRPL”实际上是 Push/Preload、Render、Precache、Lazy-Load 的缩写,我们会在下文中展开它们的具体含义。&/p&&h2&1. PUSH/PRELOAD,推送/预加载初始 URL 路由所需的关键资源。&/h2&&p&无论是 HTTP2 Server Push 还是 &link rel=&preload&&,其关键都在于,我们希望提前请求一些隐藏在应用依赖关系(Dependency Graph)较深处的资源,以节省 HTTP 往返、浏览器解析文档、或脚本执行的时间。比如说,对于一个基于路由进行 code splitting 的 SPA,如果我们可以在 webpack 清单、路由等入口代码(entry chunks)被下载与运行之前就把初始 URL,即用户访问的入口 URL 路由所依赖的代码用 Server Push 推送或 &link rel=&preload&& 进行提前加载。那么当这些资源被真正请求时,它们可能已经下载好并存在在缓存中了,这样就加快了初始路由所有依赖的就绪。&/p&&p&在多页应用中,每一个路由本来就只会请求这个路由所需要的资源,并且通常依赖也都比较扁平。饿了么移动站的大部分脚本依赖都是普通的 &script& 元素,因此他们可以在文档解析早期就被浏览器的 preloader 扫描出来并且开始请求,其效果其实与显式的 &link rel=&preload&& 是一致的。&/p&&img src=&/v2-3c1385eacb1ee19ae1976b_b.png& data-rawwidth=&2942& data-rawheight=&2032& class=&origin_image zh-lightbox-thumb& width=&2942& data-original=&/v2-3c1385eacb1ee19ae1976b_r.png&&&p&我们还将所有关键的静态资源都伺服在同一域名下(不再做域名散列),以更好的利用 HTTP2 带来的多路复用(Multiplexing)。同时,我们也在进行着对 API 进行 Server Push 的&a href=&/p/& class=&internal&&实验&/a&。&/p&&h2&2. RENDER,渲染初始路由,尽快让应用可被交互&/h2&&p&既然所有初始路由的依赖都已经就绪,我们就可以尽快开始初始路由的渲染,这有助于提升应用诸如首次渲染时间、可交互时间等指标。多页应用并不使用基于 JavaScript 的路由,而是传统的 HTML 跳转机制,所以对于这一部分,多页应用其实不用额外做什么。&/p&&h2&3. PRE-CACHE,用 Service Worker 预缓存剩下的路由&/h2&&p&这一部分就需要 &a href=&/?target=https%3A//w3c.github.io/ServiceWorker/v1/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Service Worker&i class=&icon-external&&&/i&&/a& 的参与了,Service Worker 是一个位于浏览器与网络之间的客户端代理,它以可拦截、处理、响应流经的 HTTP 请求,使得开发者得以从缓存中向 web 应用提供资源而闻名。不过,Service Worker 其实也可以主动发起 HTTP 请求,在“后台” 预请求与预缓存我们未来所需要的资源。&/p&&img src=&/v2-006e3f82611e2ecd2efd4a438d649f30_b.png& data-rawwidth=&2560& data-rawheight=&1600& class=&origin_image zh-lightbox-thumb& width=&2560& data-original=&/v2-006e3f82611e2ecd2efd4a438d649f30_r.png&&&p&我们已经使用 &a href=&/?target=https%3A//webpack.github.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Webpack&i class=&icon-external&&&/i&&/a& 在构建过程中进行 .vue 编译、文件名哈希等工作,于是我们编写了一个 webpack 插件来帮助我们收集需要缓存的依赖到一个“预缓存清单”中,并使用这个清单在每次构建时生成新的 Service Worker 文件。在新的 Service Worker 被激活时,清单里的资源就会被请求与缓存,这其实与 &a href=&/?target=https%3A///%40Huxpro/how-does-sw-precache-works-2d99c3d3c725& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SW-Precache 这个库的运行机制&i class=&icon-external&&&/i&&/a&非常接近。&/p&&p&&b&实际上,我们只对我们标记为“关键路由”的路由进行依赖收集。&/b&你可以将这些“关键路由”的依赖理解为我们整个应用的 &a href=&/?target=https%3A///web/updates/2015/11/app-shell& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&“App Shell”&i class=&icon-external&&&/i&&/a& 或者说“安装包”。一旦它们都被缓存,或者说成功安装,无论用户是在线离线,我们的 web 应用都可以从缓存中直接启动。对于那些并不那么重要的路由,我们则采取在运行时增量缓存的方式。我们使用的 &a href=&/?target=https%3A//googlechrome.github.io/sw-toolbox/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SW-Toolbox&i class=&icon-external&&&/i&&/a& 提供了 LRU 替换策略与 TTL 失效机制,可以保证我们的应用不会超过浏览器的缓存配额。&/p&&h2&4. LAZY-LOAD 按需懒加载、懒实例化剩下的路由&/h2&&p&懒加载与懒实例化剩下的路由对于 SPA 是一件相对麻烦点儿的事情,你需要实现基于路由的 code splitting 与异步加载。幸运的是,这又是一件不需要多页应用担心的事情,多页应用中的各个路由天生就是分离的。&/p&&p&值得说明的是,无论单页还是多页应用,如果在上一步中,我们已经将这些路由的资源都预先下载与缓存好了,那么懒加载就几乎是瞬时完成的了,这时候我们就只需要付出实例化的代价。&/p&&p&&br&&/p&&p&这四句话即是 PRPL 的全部了。有趣的是,我们发现多页应用在实现 PRPL 这件事甚至比单页还要容易一些。那么结果如何呢?&/p&&p&&br&&/p&&img src=&/v2-ed62da0e0e237d022ddfb4b_b.png& data-rawwidth=&493& data-rawheight=&188& class=&origin_image zh-lightbox-thumb& width=&493& data-original=&/v2-ed62da0e0e237d022ddfb4b_r.png&&&p&&br&&/p&&p&根据 Google 推出的 Web 性能分析工具 Lighthouse(v1.6),在模拟的 3G 网络下,用户的初次访问(无任何缓存)大约在 2 秒左右达到“可交互”,可以说非常不错。而对于再次访问,由于所有资源都直接来自于 Service Worker 缓存,页面可以在 1 秒左右就达到可交互的状态了。&/p&&p&但是,故事并不是这么简单得就结束了。在实际的体验中我们发现,&b&应用在页与页的切换时,仍然存在着非常明显的白屏空隙&/b&,由于 PWA 是全屏运行的,白屏对用户体验所带来的负面影响甚至比以往在浏览器内更大。我们不是已经用 Service Worker 缓存了所有资源了吗,怎么还会这样呢?&/p&&img src=&/v2-ddaf87b131b_b.png& data-rawwidth=&2560& data-rawheight=&1280& class=&origin_image zh-lightbox-thumb& width=&2560& data-original=&/v2-ddaf87b131b_r.png&&&p&&i&从首页点击到发现页,跳转过程中的白屏&/i&&/p&&h2&&b&多页应用的陷阱:重启开销&/b&&/h2&&p&与 SPA 不同,在多页应用中,路由的切换是原生的浏览器文档跳转(Navigating across documents),这意味着之前的页面会被完全丢弃而浏览器需要为下一个路由的页面重新执行所有的启动步骤:重新下载资源、重新解析 HTML、重新运行 JavaScript、重新解码图片、重新布局页面、重新绘制……即使其中的很多步骤本是可以在多个路由之间复用的。这些工作无疑将产生巨大的计算开销,也因此需要付出相当的时间成本。&/p&&p&图中为我们的入口页(同时也是最重的页面)在 2 倍 CPU 节流模拟下的 profile 数据。即使我们可以将“可交互时间”控制在 1 秒左右,我们的用户仍然会觉得这对于“仅仅切换个标签”来说实在是太慢了。&/p&&img src=&/v2-c584c70b9cffa46330ffae2e34f3769d_b.png& data-rawwidth=&2259& data-rawheight=&1229& class=&origin_image zh-lightbox-thumb& width=&2259& data-original=&/v2-c584c70b9cffa46330ffae2e34f3769d_r.png&&&h2&巨大的 JavaScript 重启开销&/h2&&p&根据 Profile,我们发现在首次渲染(First Paint)发生之前,大量的时间(900 毫秒)都消耗在了 JavaScript 的运行上(Evaluate Script)。几乎所有脚本都是阻塞的(Parser-blocking),不过因为所有的 UI 都是由 JavaScript/Vue 驱动的,倒也不会有性能影响。这 900ms 中,约一半是消耗在包括 Vue 运行时、组件、库等依赖的运行上,而另一半则花在了业务组件实例化时 Vue 的启动与渲染上。从软件工程角度来说,我们需要这些抽象,所以这里并不是想责怪 JavaScript 或是 Vue 所带来的开销。&/p&&p&&b&但是,在 SPA 中,JavaScript 的启动成本是均摊到整个生命周期的:&/b& 每个脚本都只需要被解析与编译一次,诸如生成 Virtual DOM 等较重的任务可以只执行一次,像 Vue 的 ViewModel 或是 Virtual DOM 这样的大对象也可以被留在内存里复用。&b&可惜在多页应用里就不是这样了,我们每次切换页面都为 JavaScript 付出了巨大的重启代价。&/b&&/p&&h2&浏览器的缓存啊,能不能帮帮忙?&/h2&&p&能,也不能。&/p&&p&V8 提供了&a href=&/?target=http%3A///2015/07/code-caching.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&代码缓存(code caching)&i class=&icon-external&&&/i&&/a&,可以将编译后的机器码在本地拷贝一份,这样我们就可以在下次请求同一个脚本时一次省略掉请求、解析、编译的所有工作。而且,对于缓存在 Service Worker 配套的 Cache Storage 中的脚本,会在第一次执行后就触发 V8 的代码缓存,这对于我们的多页切换能提供不少帮助。&/p&&p&另外一个你或许听过的浏览器缓存叫做“进退缓存”,Back-Forward Cache,简称 bfcache。浏览器厂商对其的命名各异,Opera 称之为 Fast History Navigation,Webkit 称其为 Page Cache。但是思路都一样,&b&就是我们可以让浏览器在跳转时把前一页留存在内存中,保留 JavaScript 与 DOM 的状态,而不是全都销毁掉。&/b&你可以随便找个传统的多页网站在 iOS Safari 上试试,无论是通过浏览器的前进后退按钮、手势,还是通过超链接(会有一些不同),基本都可以看到瞬间加载的效果。&/p&&p&Bfcache 其实非常适合多页应用。但不幸的是,Chrome 由于内存开销与其多进程架构等原因目前并不支持。Chrome 现阶段仅仅只是用了传统的 HTTP 磁盘缓存,来稍稍简化了一下加载过程而已。对于 Chromium 内核霸占的 Android 生态来说,我们没法指望了。&/p&&h2&&b&为“感知体验”奋斗&/b&&/h2&&p&尽管多页应用面临着现实中的不少性能问题,我们并不想这么快就妥协。一方面,我们尝试尽可能减少在页面达到可交互时间前的代码执行量,比如减少/推迟一些依赖脚本的执行,还有减少初次渲染的 DOM 节点数以节省 Virtual DOM 的初始化开销。另一方面,我们也意识到应用在感知体验上还有更多的优化空间。&/p&&p&Chrome 产品经理 Owen 写过一篇 &a href=&/?target=https%3A///%40owencm/reactive-web-design-the-secret-to-building-web-apps-that-feel-amazing-b5cbfe9b7c50& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Reactive Web Design: The secret to building web apps that feel amazing&i class=&icon-external&&&/i&&/a&,谈到两种改进感知体验的手段:一是使用骨架屏(Skeleton Screen)来实现瞬间加载;二是预先定义好元素的尺寸来保证加载的稳定。跟我们的做法可以说不谋而合。&/p&&p&为了消除白屏时间,我们同样引入了尺寸稳定的骨架屏来帮助我们实现瞬间的加载与占位。即使是在硬件很弱的设备上,我们也可以在点击切换标签后立刻渲染出目标路由的骨架屏,以保证 UI 是稳定、连续、有响应的。我录了&a href=&/?target=https%3A//youtu.be/K5JBGnMYO1s& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&两个&i class=&icon-external&&&/i&&/a&&a href=&/?target=https%3A//youtu.be/w1ZbNsHmRjs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&视频&i class=&icon-external&&&/i&&/a&放在 Youtube 上,不过如果你是国内读者,你可以直接访问饿了么移动网站来体验实地的效果 ;) 最终效果如下图所示。&/p&&img src=&/v2-ba988cefe42b86cadbe8_b.png& data-rawwidth=&2560& data-rawheight=&1280& class=&origin_image zh-lightbox-thumb& width=&2560& data-original=&/v2-ba988cefe42b86cadbe8_r.png&&&p&&i&在添加骨架屏后,从发现页点回首页的效果&/i&&/p&&p&这效果本该很轻松的就能实现,不过实际上我们还费了点功夫。&/p&&h2&在构建时使用 Vue 预渲染骨架屏&/h2&&p&你可能已经想到了,为了让骨架屏可以被 Service Worker 缓存,瞬间加载并独立于 JavaScript 渲染,我们需要把组成骨架屏的 HTML 标签、CSS 样式与图片资源一并内联至各个路由的静态 *.html 文件中。&/p&&p&不过,我们并不准备手动编写这些骨架屏。你想啊,如果每次真实组件有迭代(每一个路由对我们来说都是一个 Vue 组件)我们都需要手动去同步每一个变化到骨架屏的话,那实在是太繁琐且难以维护了。好在,&a href=&/?target=https%3A///ff/entry.asp%3F1797& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&骨架屏不过是当数据还未加载进来前,页面的一个空白版本而已&i class=&icon-external&&&/i&&/a&。如果我们能将骨架屏实现为真实组件的一个特殊状态 —— “空状态”的话,我们理论上就可以从真实组件中直接渲染出骨架屏来。&/p&&p&而 Vue 的多才多艺就在这时体现出来了,我们真的可以用 &a href=&/?target=https%3A//ssr.vuejs.org/en/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vue.js 的服务端渲染模块&i class=&icon-external&&&/i&&/a&来实现这个想法,不过不是用在真正的服务器上,而是在构建时用它把组件的空状态预先渲染成字符串并注入到 HTML 模板中。你需要调整你的 Vue 组件代码使得它可以在 Node 上执行,有些页面对 DOM/BOM 的依赖一时无法轻易去除得,我们目前只好额外编写一个 *.shell.vue 来暂时绕过这个问题。&/p&&h2&关于浏览器的绘制(Painting)&/h2&&p&HTML 文件中有标签并不意味着这些标签就能立刻被绘制到屏幕上,你必须保证页面的&a href=&/?target=https%3A///web/fundamentals/performance/critical-rendering-path/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&关键渲染路径&i class=&icon-external&&&/i&&/a&是为此优化的。很多开发者相信将 script 标签放在 body 的底部就足以保证内容能在脚本执行之前被绘制,这对于能渲染不完整 DOM 树的浏览器(比如桌面浏览器常见的流式渲染)来说可能是成立的。但移动端的浏览器很可能因为考虑到较慢的硬件、电量消耗等因素并不这么做。&b&不仅如此,即使你曾被告知设为&/b& &b&async&/b& &b&或&/b& &b&defer&/b& &b&的脚本就不会阻塞 HTML 解析了,但这可不意味着浏览器就一定会在执行它们之前进行渲染。&/b&&/p&&img src=&/v2-b574cf32f8d968ec8546418eac91ac03_b.png& data-rawwidth=&1447& data-rawheight=&355& class=&origin_image zh-lightbox-thumb& width=&1447& data-original=&/v2-b574cf32f8d968ec8546418eac91ac03_r.png&&&p&首先我想澄清的是,根据 &a href=&/?target=https%3A//html.spec.whatwg.org/multipage/scripting.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&HTML 规范 Scripting 章节&i class=&icon-external&&&/i&&/a&,async 脚本是在其请求完成后立刻运行的,因此它本来就可能阻塞到解析。只有 defer(且非内联)与最新的 type=module 被指定为“一定不会阻塞解析”。(不过 defer 目前也有点小问题……我们稍后会再提到)&/p&&p&&b&而更重要的是,一个不阻塞 HTML 解析的脚本仍然可能阻塞到绘制。&/b&我做了一个简化的&b&“最小多页 PWA”&/b&(Minimal Multi-page PWA,或 MMPWA)来测试这个问题,:我们在一个 async(且确实不阻塞 HTML 解析)脚本中,生成并渲染 1000 个列表项,然后测试骨架屏能否在脚本执行之前渲染出来。下面是通过 USB Debugging 在我的 Nexus 5 真机上录制的 profile:&/p&&img src=&/v2-c82f13d5fde995e09ddfd5_b.png& data-rawwidth=&1725& data-rawheight=&1022& class=&origin_image zh-lightbox-thumb& width=&1725& data-original=&/v2-c82f13d5fde995e09ddfd5_r.png&&&p&是的,出乎意料吗?首次渲染确实被阻塞到脚本执行结束后才发生。究其原因,&b&如果我们在浏览器还未完成上一次绘制工作之前就过快得进行了 DOM 操作,我们亲爱的浏览器就只好抛弃所有它已经完成的像素,且一直要等待到 DOM 操作引起的所有工作结束之后才能重新进行下一次渲染。&/b&而这种情况更容易在拥有较慢 CPU/GPU 的移动设备上出现。&/p&&h2&黑魔法:利用 setTimeout() 让绘制提前&/h2&&p&不难发现,骨架屏的绘制与脚本执行实际是一个竞态。大概是 Vue 太快了,我们的骨架屏还是有非常大的概率绘制不出来。于是我们想着如何能让脚本执行慢点,或者说,“懒”点。于是我们想到了一个经典的 Hack: setTimeout(callback, 0)。我们试着把 MMPWA 中的 DOM 操作(渲染 1000 个列表)放进 setTimeout(callback, 0) 里……&/p&&img src=&/v2-76b70dfed61c9_b.png& data-rawwidth=&1725& data-rawheight=&1022& class=&origin_image zh-lightbox-thumb& width=&1725& data-original=&/v2-76b70dfed61c9_r.png&&&p&当当!首次渲染瞬间就被提前了。如果你熟悉浏览器的&b&事件循环模型(event loop)&/b&的话,这招 Hack 其实是通过 setTimeout 的回调把 DOM 操作放到了事件循环的任务队列中以避免它在当前循环执行,这样浏览器就得以在主线程空闲时喘息一下(更新一下渲染)了。如果你想亲手试试 MMPWA 的话,你可以访问 &a href=&/?target=https%3A///Huxpro/mmpwa& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&/Huxpro/mmpwa&i class=&icon-external&&&/i&&/a& 或 &a href=&/?target=https%3A//huangxuan.me/mmpwa& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&huangxuan.me/mmpwa/&i class=&icon-external&&&/i&&/a& 访问代码与 Demo。我把 UI 设计为了 A/B Test 的形式并改为渲染 5000 个列表项来让效果更夸张一些。&/p&&p&回到饿了么 PWA 上,我们同样试着把 new Vue() 放到了 setTimeout 中。果然,黑魔法再次显灵,骨架屏在每次跳转后都能立刻被渲染。这时的 Profile 看起来是这样的:&/p&&img src=&/v2-69f91dda0cdd40eefdc6dd_b.png& data-rawwidth=&2259& data-rawheight=&1163& class=&origin_image zh-lightbox-thumb& width=&2259& data-original=&/v2-69f91dda0cdd40eefdc6dd_r.png&&&p&现在,我们在 400ms 时触发首次渲染(骨架屏),在 600ms 时完成真实 UI 的渲染并达到页面的可交互。你可以拉上去详细对比下优化前后 profile 的区别。&/p&&h2&被我 “defer” 的有关 defer 的 Bug&/h2&&p&不知道你发现没有,在上图的 Profile 中,我们仍然有不少脚本是阻塞了 HTML 解析的。好吧让我解释一下,由于历史原因,我们确实保留了一部分的阻塞脚本,比如侵入性很强的 &a href=&/?target=https%3A///amfe/lib-flexible& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&lib-flexible&i class=&icon-external&&&/i&&/a&,我们没法轻易去除它。不过,profile 里的大部分阻塞脚本实际上都设置了 defer,我们本以为他们应该在 HTML 解析完成之后才被执行,结果被 profile 打了一脸。&/p&&p&我和 &a href=&/?target=https%3A///jaffathecake& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jake Archibald&i class=&icon-external&&&/i&&/a& &a href=&/?target=https%3A///Huxpro/status/827841& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&聊了一下&i class=&icon-external&&&/i&&/a&,果然这是 Chrome 的 Bug:defer 的脚本被完全缓存时,并没有遵守规范等待解析结束,反而阻塞了解析与渲染。Jake 已经提交在 &a href=&/?target=https%3A//bugs.chromium.org/p/chromium/issues/detail%3Fid%3D717979& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&crbug&i class=&icon-external&&&/i&&/a& 上了,一起给它投票吧~&/p&&p&最后,是优化后的 Lighthouse 跑分结果,同样可以看到明显的性能提升。需要说明的是,能影响 Lighthouse 跑分的因素有很多,所以我建议你以控制变量(跑分用的设备、跑分时的网络环境等)的方式来进行对照实验。&/p&&img src=&/v2-32741daf12feb9c416a70fb_b.png& data-rawwidth=&500& data-rawheight=&195& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/v2-32741daf12feb9c416a70fb_r.png&&&p&最后附上一张图,这张图当时是做给 Addy Osmani 的 I/O 演讲用的,描述了饿了么 PWA 是如何结合 Vue 来实现多页应用的 PRPL 模式,可以作为一个架构的参考与示意图。&/p&&img src=&/v2-a5c327fa3aac_b.png& data-rawwidth=&3051& data-rawheight=&3024& class=&origin_image zh-lightbox-thumb& width=&3051& data-original=&/v2-a5c327fa3aac_r.png&&&h2&&b&一些感想&/b&&/h2&&h2&多页应用仍然有很长的路要走&/h2&&p&Web 是一个极其多样化的平台。从静态的博客,到电商网站,再到桌面级的生产力软件,它们全都是 Web 这个大家庭的第一公民。而我们组织 web 应用的方式,也同样只会更多而不会更少:多页、单页、Universal JavaScript 应用、WebGL、以及可以预见的 Web Assembly。不同的技术之间没有贵贱,但是适用场景的差距确是客观存在的。&/p&&p&&a href=&/?target=https%3A///jaffathecake& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jake&i class=&icon-external&&&/i&&/a& 曾在 &a href=&/?target=https%3A//youtu.be/J2dOTKBoTL4%3Flist%3DPLNYkxOF6rcIBTs2KPy1E6tIYaWoFcG3uj& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Chrome Dev Summit 2016&i class=&icon-external&&&/i&&/a& 上说过 “PWA !== SPA”。可是尽管我们已经用上了一系列最新的技术(PRPL、Service Worker、App Shell……),我们仍然因为多页应用模型本身的缺陷有着难以逾越的一些障碍。多页应用在未来可能会有“bfcache API”、Navigation Transition 等新的规范以缩小跟 SPA 的距离,不过我们也必须承认,时至今日,多页应用的局限性也是非常明显的。&/p&&h2&而 PWA 终将带领 web 应用进入新的时代&/h2&&p&即使我们的多页应用在升级 PWA 的路上不如单页的那些来得那么闪亮,但是 PWA 背后的想法与技术却实实在在的帮助我们在 web 平台上提供了更好的用户体验。&/p&&p&PWA 作为&a href=&/p/& class=&internal&&下一代 Web 应用模型&/a&,其尝试解决的是 web 平台本身的根本性问题:对网络与浏览器 UI 的硬依赖。因此,任何 web 应用都可以从中获益,这与你是多页还是单页、面向桌面还是移动端、是用 React 还是 Vue 无关。或许,它还终将改变用户对移动 web 的期待。现如今,谁还觉得桌面端的 web 只是个看文档的地方呢?&/p&&p&还是那句老话:让我们的用户,也像我们这般热爱 web 吧。&/p&&p&最后,感谢饿了么的 &a class=&member_mention& href=&/people/2a4ab6decf57c& data-hash=&2a4ab6decf57c& data-hovercard=&p$b$2a4ab6decf57c&&@王亦斯&/a&、任光辉、&a class=&member_mention& href=&/people/790dccce26904cdcd11b0fad3bac37b7& data-hash=&790dccce26904cdcd11b0fad3bac37b7& data-hovercard=&p$b$790dccce26904cdcd11b0fad3bac37b7&&@题叶&/a&,Google 的 &a class=&member_mention& href=&/people/eb0e760e50d6& data-hash=&eb0e760e50d6& data-hovercard=&p$b$eb0e760e50d6&&@Michael Yeung&/a&、DevRel 团队, UC 浏览器团队,腾讯 X5 浏览器团队在这次项目中的合作。感谢 &a class=&member_mention& href=&/people/cfdec6226ece879d2571fbcf& data-hash=&cfdec6226ece879d2571fbcf& data-hovercard=&p$b$cfdec6226ece879d2571fbcf&&@尤雨溪&/a&、&a class=&member_mention& href=&/people/b5ee88a44& data-hash=&b5ee88a44& data-hovercard=&p$b$b5ee88a44&&@陈蒙迪&/a& 和 Jake Archibald 在写作过程中给予我的帮助。&/p&
很荣幸在今年 2 月到 5 月的时间里,以顾问的身份加入饿了么,参与 PWA 的相关工作。这篇文章其实最初是在 I/O 之前以英文写作发表在 medium 上的:,获得了一定的关注。所以也决定改写为中文版本再次分享出来,希…
不推荐用外部变量锁定或修改按钮状态的方式,因为那样比较难:&br&&ul&&li&要考虑并理解 success, complete, error, timeout 这些事件的区别,并注册正确的事件,一旦失误,功能将不再可用;&br&&/li&&li&不可避免地比普通流程要要多注册一个 complete 事件;&br&&/li&&li&恢复状态的代码很容易和不相干的代码混合在一起;&br&&/li&&/ul&&br&我推荐用主动查询状态的方式(A、B,jQuery 为例)或工具函数的方式(C、D)来去除重复操作,并提供一些例子作为参考:&br&&br&&b&A. 独占&/b&&b&型&/b&&b&提交&/b&&br&只允许同时存在一次提交操作,并且直到本次提交完成才能进行下一次提交。&div class=&highlight&&&pre&&code class=&language-js&&&span class=&nx&&module&/span&&span class=&p&&.&/span&&span class=&nx&&submit&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&promise_&/span&&span class=&p&&.&/span&&span class=&nx&&state&/span&&span class=&p&&()&/span& &span class=&o&&===&/span& &span class=&s1&&'pending'&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span&
&span class=&p&&}&/span&
&span class=&k&&return&/span& &span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&promise_&/span& &span class=&o&&=&/span& &span class=&nx&&$&/span&&span class=&p&&.&/span&&span class=&nx&&post&/span&&span class=&p&&(&/span&&span class=&s1&&'/api/save'&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&&b&B. 贪婪&/b&&b&型&/b&&b&提交&/b&&br&无限制的提交,但是以最后一次操作为准;亦即需要尽快给出最后一次操作的反馈,而前面的操作结果并不重要。&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&nx&&module&/span&&span class=&p&&.&/span&&span class=&nx&&submit&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&promise_&/span&&span class=&p&&.&/span&&span class=&nx&&state&/span&&span class=&p&&()&/span& &span class=&o&&===&/span& &span class=&s1&&'pending'&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&promise_&/span&&span class=&p&&.&/span&&span class=&nx&&abort&/span&&span class=&p&&()&/span&
&span class=&p&&}&/span&
&span class=&c1&&// todo&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&比如某些应用的条目中,有一些进行类似「喜欢」或「不喜欢」操作的二态按钮。如果按下后不立即给出反馈,用户的目光焦点就可能在那个按钮上停顿许久;如果按下时即时切换按钮的状态,再在程序上用 abort 来实现积极的提交,这样既能提高用户体验,还能降低服务器压力,皆大欢喜。&br&&br&&b&C. 节制型提交&/b&&br&无论提交如何频繁,任意两次有效提交的间隔时间必定会大于或等于某一时间间隔;即以一定频率提交。&div class=&highlight&&&pre&&code class=&language-js&&&span class=&nx&&module&/span&&span class=&p&&.&/span&&span class=&nx&&submit&/span& &span class=&o&&=&/span& &span class=&nx&&throttle&/span&&span class=&p&&(&/span&&span class=&mi&&150&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&// todo&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&如果客户发送每隔100毫秒发送过来10次请求,此模块将只接收其中6个(每个在时间线上距离为150毫秒)进行处理。&br&这也是解决查询冲突的一种可选手段,比如以知乎草稿举例,仔细观察可以发现:&br&编辑器的 blur 事件会立即触发保存;&br&保存按钮的 click 事件也会立即触发保存;&br&但是存在一种情况会使这两个事件在数毫秒内连续发生——当焦点在编辑器内部,并且直接去点击保存按钮——这时用 throttle 来处理是可行的。&br&另外还有一些事件处理会很频繁地使用 throttle,如: resize、scroll、mousemove。&br&&br&&b&D. 懒惰&/b&&b&型&/b&&b&提交&/b&&br&任意两次提交的间隔时间,必须大于一个指定时间,才会促成有效提交;即不给休息不干活。&div class=&highlight&&&pre&&code class=&language-js&&&span class=&nx&&module&/span&&span class=&p&&.&/span&&span class=&nx&&submit&/span& &span class=&o&&=&/span& &span class=&nx&&debounce&/span&&span class=&p&&(&/span&&span class=&mi&&150&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&// todo&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&还是以知乎草稿举例,当在编辑器内按下 ctrl + s 时,可以手动保存草稿;如果你连按,程序会表示不理解为什么你要连按,只有等你放弃连按,它才会继续。&br&&br&============&br&更多记忆中的例子&br&&br&方式 C 和 方式 D 有时更加通用,比如这些情况:&br&&ul&&li&游戏中你捡到一把威力强大的高速武器,为了防止你的子弹在屏幕上打成一条直线,可以 throttle 来控制频率;&br&&/li&&li&在弹幕型游戏里,为了防止你把射击键夹住来进行无脑游戏,可以用 debounce 来控制频率;&br&&/li&&li&在编译任务里,守护进程监视了某一文件夹里所有的文件(如任一文件的改变都可以触发重新编译,一次执行就需要2秒),但某种操作能够瞬间造成大量文件改变(如 git checkout),这时一个简单的 debounce 可以使编译任务只执行一次。&br&&/li&&/ul&&br&而方式 C 甚至可以和方式 B 组合使用,比如自动完成组件(Google 首页的搜索就是):&br&&ul&&li&当用户快速输入文本时(特别是打字能手),可以 throttle
keypress 事件处理函数,以指定时间间隔来提取文本域的值,然后立即进行新的查询;&br&&/li&&li&当新的查询需要发送,但上一个查询还没返回结果时,可以 abort 未完成的查询,并立即发送新查询;&br&&/li&&/ul&&br&----- update
-----&br&&b&E. 记忆型&/b&&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&var&/span& &span class=&nx&&scrape&/span& &span class=&o&&=&/span& &span class=&nx&&memoize&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&url&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&nx&&$&/span&&span class=&p&&.&/span&&span class=&nx&&post&/span&&span class=&p&&(&/span&&span class=&s1&&'/scraper'&/span&&span class=&p&&,&/span& &span class=&p&&{&/span& &span class=&s1&&'url'&/span&&span class=&o&&:&/span& &span class=&nx&&url&/span& &span class=&p&&})&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&对于同样的参数,其返回始终结果是恒等的——每次都将返回同一对象。&br&应用例子有编辑器,如粘贴内容时抓取其中的链接信息,memoize 用以保证同样的链接不会抓取两次。&br&&br&----- update
-----&br&&b&F. 累积型&/b&&br&前几天处理自动完成事件时得到这个函数,发现也可以用在处理连续事件上,它能够把连续的多次提交合并为一个提交,比如:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&var&/span& &span class=&nx&&request&/span& &span class=&o&&=&/span& &span class=&nx&&makePile&/span&&span class=&p&&(&/span&&span class=&mi&&5&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&$&/span&&span class=&p&&.&/span&&span class=&nx&&post&/span&&span class=&p&&(&/span&&span class=&s1&&'/'&/span&&span class=&p&&,&/span& &span class=&p&&{&/span& &span class=&nx&&list&/span&&span class=&o&&:&/span& &span class=&nx&&JSON&/span&&span class=&p&&.&/span&&span class=&nx&&stringify&/span&&span class=&p&&([].&/span&&span class=&nx&&slice&/span&&span class=&p&&.&/span&&span class=&nx&&call&/span&&span class=&p&&(&/span&&span class=&nx&&arguments&/span&&span class=&p&&))&/span& &span class=&p&&})&/span&
&span class=&p&&})&/span&
&span class=&c1&&// 连续发送五次 &/span&
&span class=&nx&&request&/span&&span class=&p&&({&/span&&span class=&nx&&a&/span&&span class=&o&&:&/span&&span class=&mi&&1&/span&&span class=&p&&}),&/span& &span class=&nx&&request&/span&&span class=&p&&({&/span&&span class=&nx&&a&/span&&span class=&o&&:&/span&&span class=&mi&&2&/span&&span class=&p&&}),&/span& &span class=&nx&&request&/span&&span class=&p&&({&/span&&span class=&nx&&a&/span&&span class=&o&&:&/span&&span class=&mi&&3&/span&&span class=&p&&}),&/span& &span class=&nx&&request&/span&&span class=&p&&({&/span&&span class=&nx&&a&/span&&span class=&o&&:&/span&&span class=&mi&&4&/span&&span class=&p&&}),&/span& &span class=&nx&&request&/span&&span class=&p&&({&/span&&span class=&nx&&a&/span&&span class=&o&&:&/span&&span class=&mi&&5&/span&&span class=&p&&})&/span&
&span class=&cm&&/* post =&&/span&
&span class=&cm&&list:[{&a&:1},{&a&:2},{&a&:3},{&a&:4},{&a&:5}]&/span&
&span class=&cm&& */&/span&
&/code&&/pre&&/div&样例实现:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&var&/span& &span class=&nx&&makePile&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&count&/span&&span class=&p&&,&/span& &span class=&nx&&onfilter&/span&&span class=&p&&,&/span& &span class=&nx&&onvalue&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kd&&var&/span& &span class=&nx&&values&/span& &span class=&o&&=&/span& &span class=&p&&[],&/span& &span class=&nx&&id&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{&/span& &span class=&k&&return&/span& &span class=&nx&&value&/span& &span class=&p&&}&/span&
&span class=&k&&return&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&values&/span&&span class=&p&&.&/span&&span class=&nx&&push&/span&&span class=&p&&((&/span&&span class=&nx&&onvalue&/span& &span class=&o&&||&/span& &span class=&nx&&id&/span&&span class=&p&&).&/span&&span class=&nx&&apply&/span&&span class=&p&&(&/span&&span class=&k&&this&/span&&span class=&p&&,&/span& &span class=&nx&&arguments&/span&&span class=&p&&))&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nx&&values&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span& &span class=&o&&===&/span& &span class=&nx&&count&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&onfilter&/span&&span class=&p&&.&/span&&span class=&nx&&apply&/span&&span class=&p&&(&/span&&span class=&k&&this&/span&&span class=&p&&,&/span& &span class=&nx&&values&/span&&span class=&p&&)&/span&
&span class=&nx&&values&/span& &span class=&o&&=&/span& &span class=&p&&[]&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&----- update
-----&br&另一种累积是按时间而不是次数,比如应用在行为统计上,可能在瞬间收集到数十上百类似的行为,这时可以用上面 pile 的结构加上 debounce 来防止大批重复请求(但又不丢失任何统计):&br&&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&var&/span& &span class=&nx&&trackFactory&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&delay&/span&&span class=&p&&,&/span& &span class=&nx&&action&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kd&&var&/span& &span class=&nx&&params&/span& &span class=&o&&=&/span& &span class=&p&&[],&/span& &span class=&nx&&slice&/span& &span class=&o&&=&/span& &span class=&p&&[].&/span&&span class=&nx&&slice&/span&
&span class=&kd&&var&/span& &span class=&nx&&touch&/span& &span class=&o&&=&/span& &span class=&nx&&debounce&/span&&span class=&p&&(&/span&&span class=&nx&&delay&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nx&&params&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&action&/span&&span class=&p&&(&/span&&span class=&nx&&params&/span&&span class=&p&&)&/span&
&span class=&nx&&params&/span& &span class=&o&&=&/span& &span class=&p&&[]&/span&
&span class=&p&&}&/span&
&span class=&p&&})&/span&
&span class=&k&&return&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&params&/span&&span class=&p&&.&/span&&span class=&nx&&push&/span&&span class=&p&&(&/span&&span class=&nx&&slice&/span&&span class=&p&&.&/span&&span class=&nx&&call&/span&&span class=&p&&(&/span&&span class=&nx&&arguments&/span&&span class=&p&&))&/span&
&span class=&nx&&touch&/span&&span class=&p&&()&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&kd&&var&/span& &span class=&nx&&track&/span& &span class=&o&&=&/span& &span class=&nx&&trackFactory&/span&&span class=&p&&(&/span&&span class=&mi&&550&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&params&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&// send tracking request&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&&br&&b&G. 采样型&/b&&br&这是最近重构时联想到的,一种和上面都不同的去重操作,可以应用在自动加载(timeline)行为控制上:&br&&div class=&highlight&&&pre&&code class=&language-text&&autoload.listen(feeds, 'next', sample(3, function() {
this.enable()
&/code&&/pre&&/div&&br&如果 sample 是固化的选择函数(n 选 1),它这实际上会这样工作:&br&&div class=&highlight&&&pre&&code class=&language-text&&O-O-X-O-O-X
&/code&&/pre&&/div&&br&但「自动加载」的应用可能想要的是(两次自动,一次手动):&br&&div class=&highlight&&&pre&&code class=&language-text&&X-X-O-X-X-O
&/code&&/pre&&/div&&br&对于这种情况,可以定义作为配置的选择函数来实现控制:&br&&div class=&highlight&&&pre&&code class=&language-text&&options { sample: (n) =& n % 3 !== 0 }
&/code&&/pre&&/div&即每个下一次加载完成之后, 每三次有两次对下一次加载实行自动加载。
不推荐用外部变量锁定或修改按钮状态的方式,因为那样比较难: 要考虑并理解 success, complete, error, timeout 这些事件的区别,并注册正确的事件,一旦失误,功能将不再可用; 不可避免地比普通流程要要多注册一个 complete 事件; 恢复状态的代码很容易…
作者:猫大哥&br&链接:&a href=&/?target=https%3A///discuss/28485%3Ftype%3D2%26order%3D1%26pos%3D14%26page%3D1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&BAT面经,暑假前趁热来一发吧_笔经面经_牛客网&i class=&icon-external&&&/i&&/a&&br&&br&&br&&h2&&b&百度(offered) &/b&&/h2&
&br&&b&百度一面&/b&
1 手写ArrayList
2 手写进制转换算法,求出一个数的二进制数1的个数
3 JAVA基础 equals和==
4 多线程方式、threadlocal,各种锁,synchronized和lock
5 设计模式、spring类加载方式、实例保存在哪、aop ioc、反射机制
6 类加载器,双亲委派模型,热部署
7 jvm内存模型,内存结构、堆的分代算法、堆的分区、gc算法、gc过程。
8 tcp ip 七层模型
rest接口规范
get和post区别,长度,安全。
9 tcp ip的arp协议,两个同一网络的主机如何获得对方的mac地址。
10 负载均衡、高并发、高可用的架构
11 mysql的引擎区别
12 redis缓存,redis的集群部署,热备份,主从备份,主从数据库,hash映射找到知道指定节点。
13 了解云计算么,了解云容器docker么,容器和虚拟机的区别。
&br&&br&&br&&br&&b&百度二面&/b&
1自我介绍 项目中负责哪些 做了哪些
2项目中的数据库备份,主从数据库、集群
3数据库的索引原理,b+树原理,trie树引申,二叉查找树的原理
4海量数据中查找一个单词,分布式计算map reduce ,或者用hsah映射筛选部分结果
5java的抽象类和接口区别、java的hashmap,java的内存模型,分区,分代垃圾回收算法。实例、常量放在哪里。
6 int 4个字节,double 8个字节。
7 多线程中的wait和sleep区别,notify的作用
8 设计模式了解哪些,写一个观察者模式。实现两个接口,一个是主题一个是观察者,并写出对应方法。
9写一个生产者消费者队列的方法,分别写两个类代表生产者和消费者,并且用队列模拟其生产消费。用while循环和wait
notify可以实现,但我忘记在队列上加synchronize关键字,于是让我再写一题。写的是:输入一个字符串,输入第一个只出现一次的字符,写出来了。
10:tcp ip的四次挥手 子网掩码的作用,
子网掩码(subnet mask)又叫
&a href=&/?target=http%3A///item/%25E7%25BD%%25BB%259C%25E6%258E%25A9%25E7%25A0%2581& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&网络掩码
&i class=&icon-external&&&/i&&/a&
&a href=&/?target=http%3A///item/%25E5%259C%25B0%25E5%259D%%258E%25A9%25E7%25A0%2581& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&地址掩码
&i class=&icon-external&&&/i&&/a&
、子网络遮罩,它是一种用来指明一个
&a href=&/?target=http%3A///item/IP%25E5%259C%25B0%25E5%259D%2580& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&IP地址
&i class=&icon-external&&&/i&&/a&
的哪些位标识的是
&a href=&/?target=http%3A///item/%25E4%25B8%25BB%25E6%259C%25BA& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&主机
&i class=&icon-external&&&/i&&/a&
所在的子网,以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合IP地址一起使用。子网掩码只有一个作用,就是将某个IP地址划分成
&a href=&/?target=http%3A///item/%25E7%25BD%%25BB%259C%25E5%259C%25B0%25E5%259D%2580& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&网络地址
&i class=&icon-external&&&/i&&/a&
&a href=&/?target=http%3A///item/%25E4%25B8%25BB%25E6%259C%25BA%25E5%259C%25B0%25E5%259D%2580& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&主机地址
&i class=&icon-external&&&/i&&/a&
11 :了解linux么,说一下linux的内核锁?没接触过。
12 有没有用过sed
使用shell脚本写一个将文本中的字符替换掉的脚本,大概说了一下用grep ||
替换。没再问linux的
&br&&br&&br&&br&&b&百度三面&/b&
2负责模块,哪些模块,项目一的架构,我说太久了忘了,说最近的项目。
3数据库连接池用的是什么,配置文件呢,数据库驱动怎么下载的,持久层框架呢。
4为什么要用数据库连接池,有什么好处。
5java的内存模型,变量和实例存在哪。java栈的作用,java的堆存什么,方法区存什么。
java的分代回收。
6项目如何部署到云主机上,有什么速度提升,为什么有提升,答主要是设备性能和带宽。
7tomcat的配置,堆得初始大小是多少,达不知道。
8在网易实习的相关事项。
9问遇到过什么难题。
10问台湾交流经历,学习内容。
11问在遇到问题时候的办法。
12抗压能力
14接受加班。
15遇到工作问题,同事不配合怎么办。
16快速上手工作的办法。
17平时学习的方法,举几点。
18为什么要换工作,个人,女友。详情。
19什么时候能来。
20职业规划
&br&&br&&br&&br&&strong&三次面试每一次都是1个小时,只能说面试质量好高。&/strong&
&br&&br&&br&&br&&br&&b&然后通过了。&/b&
&br&&br&&br&&h2&&b&阿里巴巴&/b&&/h2&
&br&&b&阿里一面&/b&:
2 项目中的问题
3 java三大特性
4 jvm虚拟机 结构、算法、垃圾回收、分代、
5 集合类 链表数组 map几种 set等
6 二分查找代码
7 mysql相关
9 前后端数据交互
10 网络、操作系统
11 用过哪些技术
&br&&br&&br&&br&&br&&br&&b&阿里二面&/b&:
2 spring springmvc mybatis
3 GitHub上的代码是什么
代码包括 剑指offer 项目一 项目二 游戏项目 校招笔试题
Android项目
4 大三机器人项目
5 实习经历
6 github上某个项目的开发过程
7 学院排名
20分钟直接过了,十分开心,基本没问什么技术问题。
&br&&br&&br&&br&&b&阿里hr面&/b&
4实习经历 具体
5实习为什么找这里
6实习方向,开发方向转变
9大学时期获奖
10为什么选择支付宝部门,其他部门呢
11有什么问题
&br&&br&&br&&br&&br&&br&
问了好多问题,基本是一个问题接着上一个。hr面也顺利结束
&br&&br&&br&&br&
&br&&br&&br&&strong& 作死的事发生了,背景调查的时候时间没谈妥,我本来以为后续会再商讨,结果20天没有一个电话再打进来,由于是总机号码我也打不回去。直到今天流程被回绝,累觉不爱。不得不吐槽一下这坑爹的安排。&/strong&
&br&&br&&strong&来年再来,今年实习进不了来年校招再见吧。&/strong&
&br&&br&&br&&br&&h2&&b&腾讯&/b&&/h2&
&br&&br&&b&腾讯校招1面&/b&
学校经历 &br&&br&
学习方式 &br&&br&
项目 &br&&br&
找实习的方式 &br&&br&
什么是后端开发 &br&&br&
女朋友 &br&&br&
意向城市 &br&&br&
独生子女 &br&&br&&br&&br&
http的数据包格式 &br&&br&
tcp包含ip么 &br&&br&
tcp的数据包格式 &br&&br&
mysql数据库连接池的驱动参数 &br&&br&
数据库连接池如何防止失效 &br&&br&
部署项目时tomcat 的参数 &br&&br&
热加载的原理 &br&&br&
类加载的原理 &br&&br&
mybatis的#和$号区别 &br&&br&
java的jdk源码 &br&&br&
hashmap的iterator读取时是否会读到另一个线程put的数据 &br&&br&
linux的显示文件夹大小 ls -al &br&&br&
linux的查看端口状态 natstat加参数 &br&&br&
linux的查看进程的启动时间 linux ps &br&&br&&br&&br&
个人的优缺点 &br&&br&
什么问题想问的 &br&&br&&br&&br&
校招一面答得不好,主要是网络方面,答得不好。 &br&&br&&br&&br&&b&腾讯散招一面&/b&
1自我介绍 &br&&br&
2项目 &br&&br&
3java的堆和栈 &br&&br&
4内存泄漏发生在哪 &br&&br&
5设计模式 工厂模式 单例模式 举例子 &br&&br&
6 mysql数据库
实现层级树形结构
多方面 &br&&br&
7微信红包的设计
用户数据的统计
项目相关 &br&&br&
8cdg事业群问题&br&&br&&b&点击链接,直接跟作者交流&/b&:&a href=&/?target=https%3A///discuss/28485%3Ftype%3D2%26order%3D1%26pos%3D14%26page%3D1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&BAT面经,暑假前趁热来一发吧&i class=&icon-external&&&/i&&/a&
作者:猫大哥 链接: 百度(offered)
1 手写ArrayList 2 手写进制转换算法,求出一个数的二进制数1的个数 3 JAVA基础 equals和== 4 多线程方式、threadlocal,各种锁,synchronized和lock 5 设计…
&h2&&b&1.扎实的基础&/b&&/h2&&p&&b&算法&/b&&/p&&div class=&highlight&&&pre&&code class=&language-text&&二分搜索 Binary Search
分治 Divide Conquer
宽度优先搜索 Breadth First Search
深度优先搜索 Depth First Search
回溯法 Backtracking
双指针 Two Pointers
动态规划 Dynamic Programming
扫描线 Scan-line algorithm
快排 Quick Sort
&/code&&/pre&&/div&&p&&b&数据结构&/b&&/p&&div class=&highlight&&&pre&&code class=&language-text&&栈 Stack
队列 Queue
链表 Linked List
数组 Array
哈希表 Hash Table
二叉树 Binary Tree
并查集 Union Find
字典树 Trie
&/code&&/pre&&/div&&p&&b&语言&/b&&/p&&p&需要熟练掌握一门语言,在此基础上,可根据需要和个人时间情况,考虑再掌握1-2门语言&/p&&p&&b&理论知识&/b&&/p&&div class=&highlight&&&pre&&code class=&language-text&&数学(线性代数、离散数学、概率统计)
操作系统与编译原理
计算机组成原理
计算机网络
&/code&&/pre&&/div&&p&其中最重要的就是算法与数据结构,你可以去&a href=&///?target=http%3A///zh-cn/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&LintCode - 主页&i class=&icon-external&&&/i&&/a&上刷题,只要你能够把Lintcode上70%的题目刷两遍,60%的题目做到bug free,你的编程能力将会有质的飞跃。&/p&&p&对于系统设计,建议可以多做做系统设计的面试题,了解系统设计的基本原理,以及设计Instagram、Youtube、设计优步等方面的问题。有条件的话,可以参加一下系统设计的课程。另外,九章算法每个月都有免费试听,对于新手小白来说,可以提前了解系统设计的面试内容、如何回答系统设计题、如何设计Instagram、如何设计 Uber 等等。&/p&&h2&&b&2.自主学习的意识和能力&/b&&/h2&&p&自主学习是一种对自己的投资,可能需要投入大量精力,时间,甚至是金钱,但是自我学习所带来的收益远远超过投资。&/p&&p&尤其是在步入社会之后,没有人会主动教会你所有的知识,你必须靠自己的意识和能力进一步提升自己。你必须要有自主学习的意识和能力,并且比较好的在自主能力上还有一定的要求,这样你才能逐渐从一个“小白”成长起来。&/p&&h2&&b&3.解决问题的能力&/b&&/h2&&p&工作中会遇到各种各样的问题,这些问题有可能是技术上的,也可能是人际交往上的,你需要学习如何去解决这些问题,通过解决问题学习如何发现问题、规避问题。&/p&&p&比如一个好的程序员如果在编程时出现了bug,会仔细找出bug,然后完善它修正它,然后提交一份问题说明和反馈,而差的程序员会删掉这个部分或者说这个问题解决不了。&/p&&h2&&b&4.人际沟通能力&/b&&/h2&&p&虽然程序员大多数时间都是在与计算机打交道,但是不可否认,人际沟通能力对于程序员来说十分重要。人际沟通能力会影响到你与同事、与上司、与客户之间的关系,对你在面试、求职、晋升等都极为重要。&/p&&p&反映在面试中,好的沟通者能够博得面试官的好感,而不善于沟通的面试者,即使自身技术过关,也会因为沟通不良而错失很多机会。所以试着在面试前参考一些专业的面试指导:&a href=&///?target=http%3A///article/%3Ftags%3Dguidance& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&《硅谷精英工程师面试指导大全》&i class=&icon-external&&&/i&&/a&,比如代码风格、薪资谈判、behavior interview 等对于沟通能力不佳的面试者很有帮助。&/p&&h2&&b&5.观察能力&/b&&/h2&&p&察言观色的能力也十分重要,具备了良好的观察能力可以在生活和职场中的各个方面都如鱼得水。&/p&&p&观察力强的人,可以根据身边的事物和趋势做出正确的判断,调整自己的行为,从而更加适应环境的变化。&/p&&h2&&b&6.谦虚&/b&&/h2&&p&无论你拥有怎样的能力,都需要保持谦虚。要知道,人外有人,天外有天,社会上比你厉害的人比比皆是。&/p&&p&保持谦虚的态度,可以帮你获得同事和上司的好感,他们会更加愿意与你分享新知识、教你新技能。保持谦虚的态度,也可以帮助你发现自身的不足,取长补短,不断地完善自身。&/p&&p&&b&欢迎关注我的微信公众号:九章算法(ninechapter ),带你了解IT前沿技术,帮你通过面试,拿到offer,找到好工作!&/b&&/p&
1.扎实的基础算法二分搜索 Binary Search
分治 Divide Conquer
宽度优先搜索 Breadth First Search
深度优先搜索 Depth First Search
回溯法 Backtracking
双指针 Two Pointers
动态规划 Dynamic Programming
扫描线 Scan-line algorithm
快排 Quick …
提高js原生能力无非就是看和写,对于看的话,肯定是看优秀的书和源码了,对于书主要分两类:第一类是基础类,肯定犀牛书,红宝书,你所不知道的js,es6等;第二类主要讲思想的:比如《JavaScript设计模式》,对于node肯定是《node深入浅出》等。对于源码可以看看underscore,zepto,vue,react和koa等,不建议看jquery和angularjs。然后就是写了,先模仿别人的代码,然后形成自己的风格和思路。
提高js原生能力无非就是看和写,对于看的话,肯定是看优秀的书和源码了,对于书主要分两类:第一类是基础类,肯定犀牛书,红宝书,你所不知道的js,es6等;第二类主要讲思想的:比如《JavaScript设计模式》,对于node肯定是《node深入浅出》等。对于源码可…
&img src=&/f56ced0fef5ae0_b.jpg& data-rawwidth=&500& data-rawheight=&500& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/f56ced0fef5ae0_r.jpg&&&p&(转载请注明出处,真的不费事)&/p&&p&已于更新,地址:&a href=&/wille/& class=&internal&&傅里叶分析之掐死教程(完整版)更新于 - 与时间无关的故事 - 知乎专栏&/a&&/p&我保证这篇文章和你以前看过的所有文章都不同,这是12年还在果壳的时候写的,但是当时没有来得及写完就出国了……于是拖了两年,嗯,我是拖延症患者……&br&&p&这篇文章的核心思想就是:&/p&&h2&要让读者在不看任何数学公式的情况下理解傅里叶分析。&/h2&&p&傅里叶分析不仅仅是一个数学工具,更是一种可以彻底颠覆一个人以前世界观的思维模式。但不幸的是,傅里叶分析的公式看起来太复杂了,所以很多大一新生上来就懵圈并从此对它深恶痛绝。老实说,这么有意思的东西居然成了大学里的杀手课程,不得不归咎于编教材的人实在是太严肃了。(您把教材写得好玩一点会死吗?会死吗?)所以我一直想写一个有意思的文章来解释傅里叶分析,有可能的话高中生都能看懂的那种。所以,不管读到这里的您从事何种工作,我保证您都能看懂,并且一定将体会到通过傅里叶分析看到世界另一个样子时的快感。至于对于已经有一定基础的朋友,也希望不要看到会的地方就急忙往后翻,仔细读一定会有新的发现。&/p&&br&&p&————以上是定场诗————&/p&&p&下面进入正题:&/p&&p&抱歉,还是要啰嗦一句:其实学习本来就不是易事,我写这篇文章的初衷也是希望大家学习起来更加轻松,充满乐趣。但是千万!千万不要把这篇文章收藏起来,或是存下地址,心里想着:以后有时间再看。这样的例子太多了,也许几年后你都没有再打开这个页面。无论如何,耐下心,读下去。这篇文章要比读课本要轻松、开心得多……&/p&&h2&一、嘛叫频域&/h2&&p&
从我们出生,我们看到的世界都以时间贯穿,股票的走势、人的身高、汽车的轨迹都会随着时间发生改变。这种以时间作为参照来观察动态世界的方法我们称其为时域分析。而我们也想当然的认为,世间万物都在随着时间不停的改变,并且永远不会静止下来。但如果我告诉你,用另一种方法来观察世界的话,你会发现&u&&b&世界是永恒不变的&/b&&/u&,你会不会觉得我疯了?我没有疯,这个静止的世界就叫做频域。&/p&&br&&p&先举一个&b&&u&公式上并非很恰当&/u&&/

我要回帖

更多关于 大战舰金刚 的文章

 

随机推荐