如何修改国际象棋开局3步杀库某一步

腾讯这边有一款非常好的产品,可以测上线的网页在移动设备的兼容性。&br&&br&云端有很多真机,通过网页可以操作真实手机。&br&&br&选择一台:&br&&br&&figure&&img src=&https://pic2.zhimg.com/14c03696c9defa72a50e08f9ada8cb35_b.png& data-rawwidth=&1320& data-rawheight=&1014& class=&origin_image zh-lightbox-thumb& width=&1320& data-original=&https://pic2.zhimg.com/14c03696c9defa72a50e08f9ada8cb35_r.jpg&&&/figure&进入测试:&br&&br&&figure&&img src=&https://pic2.zhimg.com/572e7bfbbdc5_b.png& data-rawwidth=&1309& data-rawheight=&1101& class=&origin_image zh-lightbox-thumb& width=&1309& data-original=&https://pic2.zhimg.com/572e7bfbbdc5_r.jpg&&&/figure&&br&目前这个对网速的要求比较高,高清模式的话,需要1M/s左右的网速,如果网速不够的话,可以切换到流畅模式,流畅模式的话画质不是很清晰,不过也能看。&br&&br&这个真机的测试应该比模拟器更有优势,而且目前几乎就是免费试用。&br&&br&&a href=&//link.zhihu.com/?target=http%3A//wetest.qq.com/cloud/index.php/help/cloudindex& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&wetest.qq.com/cloud/ind&/span&&span class=&invisible&&ex.php/help/cloudindex&/span&&span class=&ellipsis&&&/span&&/a&&br&&br&大腾讯就是有钱
腾讯这边有一款非常好的产品,可以测上线的网页在移动设备的兼容性。 云端有很多真机,通过网页可以操作真实手机。 选择一台: 进入测试: 目前这个对网速的要求比较高,高清模式的话,需要1M/s左右的网速,如果网速不够的话,可以切换到流畅模式,流畅模式…
做为一名从切页面转到写js的前端开发工程师,看到这道题目非常有感触啊。&br&&br&先感性说一下,我还记得我第一年工作的时候,写css和html的时候听很high的歌,但是写js的时候,是绝对不敢听歌的。&br&&br&再理性的说一下,我记得我当时的页面兼容程度是ie678和firefox,恩。。没错,那时候还没有chrome。。&br&&br&盒模型什么的概念我也是几年前才知道的,我还记得我当时面试时,前端要求必须会div+css页面布局。&br&&br&你看的没错,当时很多网站都是table布局的。。很多人对div这个概念都不太理解,更别说什么双飞翼,圣杯布局,9/12宫格了。&br&&br&恩。。但是我记得我巅峰时期可以一口气切出兼容4个浏览器的页面,基本是一气呵成,不需要再单独调试,基本上是连着hack一起写完的。&br&&br&要说现在达到什么水平才行,我总结几条吧,也不知道算不算落伍。&br&&br&1,盒模型概念一定清楚,知道自己想要的布局到底该写多宽多高。&br&2,渲染模式要分清楚,怪异和标准,怎么转换,说白了也是盒模型表现的事。&br&3,不同浏览器的css hack怎么写,选择器的优先级弄明白怎么调整。&br&4,所有的html标签都知道语义,所有的tag attribute都知道什么含义,记住是所有,并且知道默认样式是什么,了解如何reset。&br&5,知道css3动画的所有细节,并且知道在不同浏览器下的差异,知道css media query和flex的细节和一些比例单位如em,rem,vm等用法,并且知道怎么用于实际的自适应布局。&br&6,知道如何做css性能优化和oocss,以及如何写出模块化的css。&br&7,知道不同浏览器的差异属性并且知道如何绕过不使用这些属性。&br&8,各种垂直居中的写法,相对,绝对,fixed还有float的各种用法。&br&9,对齐,各种垂直对齐,文本,图片,行内元素和块级元素等。&br&10,给你一份psd设计稿,写完css和html,去掉css,能保证页面不乱,依然按照语义可以默认样式正常显示文档。&br&11,对seo有了解,知道怎么让显示和文案同时存在页面,了解css icon fonts,svg,雪碧图等原理和用法。&br&12,写过简单的js,知道如何给js工程师预留结构和节点钩子,不随意使用id属性来做样式渲染。&br&&br&暂时想到这么多,感觉算是一个基础的标准,这些都有所了解后再去学习js可能会更事半功倍。别人想到可以一起评论补充~,我很久不写css和html了。。。-。-
做为一名从切页面转到写js的前端开发工程师,看到这道题目非常有感触啊。 先感性说一下,我还记得我第一年工作的时候,写css和html的时候听很high的歌,但是写js的时候,是绝对不敢听歌的。 再理性的说一下,我记得我当时的页面兼容程度是ie678和firefox,…
谢邀。&br&&br&网易云音乐是这样的&br&&ol&&li&每次打开页面之后,随机生成一个 id,放在变量里面&/li&&li&开始播放音乐之后,把这个 id 写到 cookie 里去,就是 playerid 这个 cookie&/li&&li&播放音乐的过程中,会不断有事件出发,比如说 timeupdate&/li&&li&事件触发的时候看 cookie 中的 playerid 是不是跟页面自己保存这个变量一致&/li&&li&不一致就暂停播放音乐&/li&&/ol&&br&&p&你试一下,先开一个网易云音乐页面播放音乐,然后在 console 里改写掉 playerid,音乐就会停掉了。&br&&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&cookie&/span& &span class=&o&&=&/span& &span class=&s1&&'playerid=1'&/span&
&/code&&/pre&&/div&
谢邀。 网易云音乐是这样的 每次打开页面之后,随机生成一个 id,放在变量里面开始播放音乐之后,把这个 id 写到 cookie 里去,就是 playerid 这个 cookie播放音乐的过程中,会不断有事件出发,比如说 timeupdate事件触发的时候看 cookie 中的 playerid 是…
&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/constantince/p/4565261.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Chrome开发者工具不完全指南(一、基础功能篇)&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/constantince/p/4579121.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Chrome开发者工具不完全指南(二、进阶篇)&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/constantince/p/4585983.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Chrome开发者工具不完全指南:(三、性能篇)&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/constantince/p/4607497.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Chrome开发者工具不完全指南(四、性能进阶篇)&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/constantince/p/4624241.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Chrome开发者工具不完全指南(五、移动篇)&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/constantince/p/4641796.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Chrome开发者工具不完全指南(六、插件篇)&/a&&br&&br&好东西要分享 :)&br&&br&==== 补充 ======&br&&a href=&//link.zhihu.com/?target=https%3A//developers.google.com/web/tools/chrome-devtools/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&developers.google.com/w&/span&&span class=&invisible&&eb/tools/chrome-devtools/&/span&&span class=&ellipsis&&&/span&&/a&&br&&a href=&https://www.zhihu.com/question/& class=&internal&&Chrome 有哪些鲜为人知的用法? - 软件&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//blog.jobbole.com/31178/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&用Chrome开发者工具做JavaScript性能分析&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//toutiao.com/ird& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&前端杂谈 - 深入了解Chrome开发工具&/a&
&blockquote&我最近搞虚拟DOM,就说说&a href=&//link.zhihu.com/?target=https%3A//github.com/RubyLouvre/avalon& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&avalon &/a&是怎么更新数据的吧,react的实现方式也差不多&/blockquote&&div class=&highlight&&&pre&&code class=&language-html&&&span class=&cp&&&!DOCTYPE html&&/span&
&span class=&nt&&&html&&/span&
&span class=&nt&&&head&&/span&
&span class=&nt&&&meta&/span& &span class=&na&&charset=&/span&&span class=&s&&&UTF-8&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&script &/span&&span class=&na&&src=&/span&&span class=&s&&&avalon.js&&/span&&span class=&nt&&&&/script&&/span&
&span class=&nt&&&script&&/span&
&span class=&kd&&var&/span& &span class=&nx&&vm&/span& &span class=&o&&=&/span& &span class=&nx&&avalon&/span&&span class=&p&&.&/span&&span class=&nx&&define&/span&&span class=&p&&({&/span&
&span class=&nx&&$id&/span&&span class=&o&&:&/span& &span class=&s1&&'test'&/span&&span class=&p&&,&/span&
&span class=&nx&&array&/span&&span class=&o&&:&/span& &span class=&p&&[&/span&&span class=&s2&&&a&&/span&&span class=&p&&,&/span& &span class=&s2&&&b&&/span&&span class=&p&&,&/span& &span class=&s2&&&c&&/span&&span class=&p&&,&/span& &span class=&s2&&&d&&/span&&span class=&p&&],&/span&
&span class=&nx&&change&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&vm&/span&&span class=&p&&.&/span&&span class=&nx&&array&/span& &span class=&o&&=&/span& &span class=&p&&[&/span&&span class=&s2&&&x&&/span&&span class=&p&&,&/span& &span class=&s2&&&c&&/span&&span class=&p&&,&/span& &span class=&s2&&&a&&/span&&span class=&p&&,&/span& &span class=&s2&&&b&&/span&&span class=&p&&,&/span& &span class=&s2&&&f&&/span&&span class=&p&&,&/span& &span class=&s2&&&k&&/span&&span class=&p&&]&/span&
&span class=&p&&}&/span&
&span class=&p&&})&/span&
&span class=&nt&&&/script&&/span&
&span class=&nt&&&/head&&/span&
&span class=&nt&&&body&/span& &span class=&na&&ms-controller=&/span&&span class=&s&&&test&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&button&/span& &span class=&na&&type=&/span&&span class=&s&&&button&&/span& &span class=&na&&ms-click=&/span&&span class=&s&&&change&&/span&&span class=&nt&&&&/span&change&span class=&nt&&&/button&&/span&
&span class=&nt&&&ul&&/span&
&span class=&nt&&&li&/span& &span class=&na&&ms-repeat=&/span&&span class=&s&&&array&&/span&&span class=&nt&&&&/span&{{el}}&span class=&nt&&&/li&&/span&
&span class=&nt&&&/ul&&/span&
&span class=&nt&&&/body&&/span&
&span class=&nt&&&/html&&/span&
&/code&&/pre&&/div&&br&&br&&figure&&img src=&https://pic3.zhimg.com/4db96b3fae1dc26db2d7d4c_b.png& data-rawwidth=&1388& data-rawheight=&1158& class=&origin_image zh-lightbox-thumb& width=&1388& data-original=&https://pic3.zhimg.com/4db96b3fae1dc26db2d7d4c_r.jpg&&&/figure&注意到列表多了许多注释节点吧,这叫做&b&路标系统&/b&,用于确定数组元素的作用域范围,方便移动它们。大家如果会angular,使用ng-repeat也会有这样的注释元素。但这些不是重点。&br&&br&为了让大家明白虚拟DOM为什么这么高效的原因,我们直接在chrome控制台下对这些LI元素做一些修改,全部加上title属性。它们能方便告诉我们,一会儿点击了change按钮,重新渲染列表,这些LI元素是新建的,还是沿用的旧的。&br&&br&&figure&&img src=&https://pic1.zhimg.com/fe6e72b2bd6e640ec56ad_b.png& data-rawwidth=&1000& data-rawheight=&402& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&https://pic1.zhimg.com/fe6e72b2bd6e640ec56ad_r.jpg&&&/figure&&br&当我们点击change按钮后&br&&figure&&img src=&https://pic4.zhimg.com/dcb2c9b9ae0fd08c52df_b.png& data-rawwidth=&854& data-rawheight=&530& class=&origin_image zh-lightbox-thumb& width=&854& data-original=&https://pic4.zhimg.com/dcb2c9b9ae0fd08c52df_r.jpg&&&/figure&&br&我们可以发现,能重复利用的节点被重复利用,它是优先考虑移动节点,这时它不用一一改动LI元素里面的所有要改动的地方;对于新添加的元素,它会利用上次被删除的元素对应的节点,然后改变其属性值 ;当没有可利用的节点,框架才会将虚拟节点转换真实节点。在某些MVVM框架,需要用户显式写“track by $index”, react则要求用key,avalon是内部使用hash算法搞定。&br&&br&这只是diff算法的冰山一角。&br&&br&其实,&b&如果没有循环绑定(ms-repeat, ng-repeat, v-for),MVVM是不存在性能问题&/b&。&br&&br&循环绑定会破坏原来的页面结构,引起页面重新布局。&br&&br&传统的字符串模板,对付它们只会innerHTML,这导致每次都会生成大量的节点,选区,光标位置,事件及挂在它上面的第三方组件都要重新处理。&br&&br&而一些弱的MVVM框架,它们的算法就差一点,无法尽量利用已有节点。&br&&br&avalon与react的做法是使用了虚拟DOM做缓冲层,每次数据变动,都生成对应区域的虚拟DOM树,然后两个虚拟DOM做合并操作(diff),得到更新的操作集(patch)。&br&&br&avalon目前没有考虑支持多种渲染形态(只有DOM,不存在canvas, webgl,
native等渲染介质),那么可以偷懒,对不存在绑定的元素及其孩子,不再转换为虚拟DOM,这样减少diff的节点。&br&&br&创建一个DOM的消耗是非常惊人,目前,在浏览器中存在四种形态的对象&br&&br&&ol&&li&超轻量 Object.create(nulll)&br&&/li&&li&轻量
一般的对象 {}&br&&/li&&li&重量
带有访问器属性的对象, avalon或vue的VM对象&br&&/li&&li&超重量 各种节点或window对象&br&&/li&&/ol&然后频繁访问或创建 3,4种形态的对象是很不明智的。但VM对象能为编程带来极好的用户体验,因此这个代价是可以接受的。&br&&br&在过去的MVVM框架中,绑定属性的操作主体是元素节点(它们一般放在观察者模式的订阅数组中),有了虚拟DOM,我们就直接操作虚拟DOM,这样它们移出或插入虚拟DOM树,是不耗费什么性能,更不会结合CSS样式表产生reflow,rerender 什么破事。 因此原真实DOM拥有了&b&绑定属性&/b&(ms-*, ng-*)与&b&路标系统 &/b&,以后我们将虚拟DOM树的变更同步到真实DOM树时是非常方便的。&br&&br&而react是怎么追踪到其更变更的节点呢?它有&b&data-reactid&/b&!只要确保对应关系,虚拟DOM怎么插入,移除,重排,react会在一个事务的尽头,才开始重新真实DOM,这样也能确保更新成功。&br&&br&&figure&&img src=&https://pic4.zhimg.com/ce7da9b36d8a9a6736c4ace0f995b783_b.png& data-rawwidth=&928& data-rawheight=&380& class=&origin_image zh-lightbox-thumb& width=&928& data-original=&https://pic4.zhimg.com/ce7da9b36d8a9a6736c4ace0f995b783_r.jpg&&&/figure&&br&总而言之,有了虚拟DOM,我们是使用够轻量的对象代替超重对象作为直接操作 主体,减少对超重对象的操作!虚拟DOM的结构是很轻量,最多不超过10个属性,并且其继承层级不超过2层。而DOM节点有70+个属性,继承层级有6,7层(文本节点6层,元素节点7层).访问一个属性,可能会追溯几重原型链&br&&br&&a href=&//link.zhihu.com/?target=https%3A//github.com/RubyLouvre/avalon/issues/272& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&通过JS修改属性时触发DOM回调 · Issue #272 · RubyLouvre/avalon · GitHub&/a&&br&&figure&&img src=&https://pic3.zhimg.com/514c13feafb400fcfb88c2_b.png& data-rawwidth=&1126& data-rawheight=&608& class=&origin_image zh-lightbox-thumb& width=&1126& data-original=&https://pic3.zhimg.com/514c13feafb400fcfb88c2_r.jpg&&&/figure&&br&&br&其二,能在虚拟DOM与真实DOM建立映射关系.avalon是使用绑定属性与路标系统 ,react使用data-reactid,但data-reactid是元素节点的自定义属性,对于文本节点,它没有办法,只好外包一个span,因此严重破坏旧有结构。即便插入了这么span,react性能还是很快。&br&&br&第三,react与avalon的所有虚拟DOM对象是可回收循环利用。而节点循环利用是很危险,很易造成内存泄漏。&br&&br&虚拟DOM不只是一个缓冲层,里面涉及大量算法,你可以使用hash或KMP,确保更新最少。或者在某级对象创建一个更新对象包,将重复变更的属性放在里面,这样aaa=bbb, aaa=ccc,aaa=ddd就自动合并成一个aaa=ddd。&br&&br&为了追求性能,react强制单向流动,方便让状态叠加。但双向绑定也能叠加,只是会让VM内部数据多转几圈。&br&&br&无论怎么样,虚拟DOM是一个伟大的发明,但react实现得比较笨拙而已。随着大家对其源码的研读,今年会冒出更多同类型产品。前端就是因为源码公开而繁荣昌盛!因此大家必须读源码,只会调API,永远是低级码农。
我最近搞虚拟DOM,就说说是怎么更新数据的吧,react的实现方式也差不多&!DOCTYPE html&
&meta charset="UTF-8"&
&script src="avalon.js"&&/script&
var vm = avalon.define({
$id: 'test',
array: ["a", "b", "c", "…
这是个Meta级别的好问题!如果你想把web前端性能优化到极致,一定要认真地去了解这个原则背后的原理,而非表面的技巧。&br&&br&&b&(已完结,转载请署名,否则保留追究的权利)&/b&&br&&br&事实上,如果对web优化比较了解,只要一句话就能说清楚了。&b&web页面性能优化其精髓就是——将浏览器基本无序的资源加载请求用js有序地控制起来,包括js本身。&/b&&br&&br&这个原则几乎适用于所有web场景,只是它演变出来的具体做法千差万别,PC端和H5端由于环境不一样,也要有不同的玩法,而Bigpipe也是基于这个理念。但请问你能理解吗?&br&&br&如果你能理解,那么可以不往下看,如果不能理解,建议看看,或许有收获的。&b&我主要以PC端几个知名电商网站的优化为案例,来说明将js放在html不同位置都有什么不同。&/b&&br&&br&-----------&br&&b&一、为什么我会认为这是个好问题?!&/b&&br&&br&在我带过的几个电商项目前端团队中,就因为这个问题开过几个不开窍的前端。&br&&br&理由很简单,也比较霸道。这几天我们项目又在做前端性能优化,又有同事拿着这个疑惑来问我,呵呵,不会再乱开人了。&br&&br&以前为此开人,可能是我没能力用通俗易懂的文字来描述好这个问题,难免有误伤。为了避免这样的事情,我决定要好好把问题说清楚,就从浏览器渲染的原则开始!&br&&br&&b&二、是不是网页JS调用都尽量放到网页底部?&/b&&br&&br&按知乎的原则,先要问是不是,再问为什么,但这个'是不是'对于做前端技术的人来说,一眼就能看出个所以然。&br&&br&显然,不是的!但性能优化做得好的网站基本上是这个原则。给两个我认为前端优化做得比较好的知名海淘类电商,大家去观摩一下别人的做法:&br&&br&-----------&br&&a href=&//link.zhihu.com/?target=http%3A//www.kaola.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&网易考拉海购!&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//www.beibei.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&贝贝网&/a&&br&-----------&br&&br&当然,我们不能用Bigpipe这种极端的优化技术流派来解答,或许是解答不了的,但即便是bigpipe其背后的原理也是和这个原则并没有冲突的,大道至简,底层的原理是一样的,只是它尽全力地利用每一次http请求,用前端的技术手段来加载足够多的资源。&br&&br&当然了,题主应该是一个前端开发,而且应该是在碰到页面优化需求或学习上的疑惑了,可能有人强逼这TA按照这样的原则去做,但又不知道什么别人为啥要求他这么做,于是就有了这个问题!&br&&br&至于情况是不是这样的,我只是瞎猜的。但是,我带的前端团队里面有不少同学存在同样的疑惑,因为我有这样一个规范:&br&&figure&&img src=&https://pic1.zhimg.com/5c60aba0f28_b.png& data-rawwidth=&757& data-rawheight=&753& class=&origin_image zh-lightbox-thumb& width=&757& data-original=&https://pic1.zhimg.com/5c60aba0f28_r.jpg&&&/figure&不知道有多少团队有这样的标准化demo模板规范?这是我1年多以前做的了,在这里——&a href=&//link.zhihu.com/?target=http%3A//pjg.pw/framework/front-end-dev-docc.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&基于gulp的前端框架开发规范&/a&。大家可以去我的博客看看,权当参考。当然,现在这个规范已经有了新版本。&br&&br&--------------------&br&&b&三、为什么网页JS调用都尽量放到网页底部?&/b&&br&&br&这部分如果《高性能网站建设指南》这本书上有详细说明的并且我也认同的,就直接截图贴过来,这里我只说自己的理解部分。还有,对于书本上的东西,我的态度是——尽信书,不如无书。&br&&br&&b&(一)大家的做法是不是一样的?&/b&&br&&br&ok,我先把问题分解一下。&br&&br&首先这里有个关键词——尽量。《高性能网站建设指南》这本书里面用的词是“如果可以的话”。&br&&figure&&img src=&https://pic2.zhimg.com/2a9ffa963cb71fb98e36ca69_b.jpg& data-rawwidth=&711& data-rawheight=&337& class=&origin_image zh-lightbox-thumb& width=&711& data-original=&https://pic2.zhimg.com/2a9ffa963cb71fb98e36ca69_r.jpg&&&/figure&&br&也就是说,js不完全是一定要放在页面底部的,但是你要了解清楚以下两个问题了:&br&&ol&&li&&b&什么是尽量的(可以的)那部分js代码?&/b&&br&&/li&&li&&b&什么是不尽量的&b&(不可以的)&/b&那部分呢?&/b&&br&&/li&&/ol&只有了解清楚这两个问题,你才知道如何去安排js在页面中的位置。ok,我们来看看考拉网和贝贝网的首页源代码。&br&&ol&&li&&b&考拉网:&/b&&head&&/head &之间就一段让IE9以下浏览器兼容HTML5标签的js代码,这是一个底层的兼容脚本,不涉及任何页面逻辑,而它的全部页面逻辑都是放置在脚步。&br&&/li&&li&&b&贝贝网:&/b&&head&&/head &之间放置的是一些全局设置和一些统计脚本,也不涉及页面逻辑,逻辑部分js也是放在页面底部。&br&&/li&&li&&b&我们项目的:&/b&做法是两者的结合,heah标签内就的js脚本就只是定义几个全局的命名空间和一段统计脚本,没了,而业务逻辑js就放置页面最底部。&/li&&/ol&&figure&&img src=&https://pic2.zhimg.com/a337d5d4b8164a7befaf84af8b56bc15_b.jpg& data-rawwidth=&789& data-rawheight=&500& class=&origin_image zh-lightbox-thumb& width=&789& data-original=&https://pic2.zhimg.com/a337d5d4b8164a7befaf84af8b56bc15_r.jpg&&&/figure&&br&整体对比来看,css样式规划大家都基本相同,是1个全局+1个当前,文件名上通过md5戳来解决强缓存问题,js的缓存解决方案也是一样的做法。&br&&figure&&img src=&https://pic2.zhimg.com/805edad04af25_b.png& data-rawwidth=&486& data-rawheight=&318& class=&origin_image zh-lightbox-thumb& width=&486& data-original=&https://pic2.zhimg.com/805edad04af25_r.jpg&&&/figure&&br&这是我们的PC端js的大致布局。考拉网也是类似分配方式,只是把core和common合并在一起(超过了200K),我们没有。我是觉得合并在一起这样模块太大了,不太利于弱网用户,但多一次http请求,有利有弊吧。&br&&figure&&img src=&https://pic3.zhimg.com/9d4d8507b2_b.jpg& data-rawwidth=&953& data-rawheight=&646& class=&origin_image zh-lightbox-thumb& width=&953& data-original=&https://pic3.zhimg.com/9d4d8507b2_r.jpg&&&/figure&&br&有部分人可能会问,为啥不统统合并在一起,就1个HTTP请求了?我只能呵呵,网站不是只有1个首页,还有很多其他页面呢,只要把js底层库和常用的公共类库加载一次,其他页面就可以被缓存起来(form cache或304)&br&&br&这个前端技术表面的对比,是不是有点意思呢?&br&&br&我们几个都是海淘类垂直电商,只是定位和强势品类有些小差别,但在业务层面其实是基本相同的,都是海淘。也就是说,我们互为竞争对手!那么,很显然我们不可能相互沟通、开会,然后通报我用什么前端架构,前后端协作开发的模式等等技术问题,但是为什么做法上大家是那么的一致呢?&br&&br&这就是问题了!我绝对保证不认识考拉的前端架构设计师,但我的前端设计方案出来的结果几乎是一模一样的!为什么呢?&br&&br&简单点说,条条道路通罗马。就这样,没为什么。后面我汇以网易考拉为案例,逆向分析他们的做法,进而尝试窥探他们的前端架构设计方案。不一定正确,只是个人看法。&br&&br&如果你想得到绝对正确的答案,就想办法进入里面,或者发一个类似这样的问题(&a href=&http://www.zhihu.com/question/& class=&internal&&美团的前端架构是怎样的? - 前端开发&/a&),看看有没有人出来回复。&br&&br&&b&-------日22:56:43---------&br&&/b&&b&这里上一个简单的web优化对比图(&a href=&//link.zhihu.com/?target=http%3A//www.alibench.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&阿里测,专业的网站即时探测工具&/a&&/b&&b&):&/b&&br&&figure&&img src=&https://pic2.zhimg.com/96f3eb9fceb9be6a6df6525_b.jpg& data-rawwidth=&1024& data-rawheight=&1698& class=&origin_image zh-lightbox-thumb& width=&1024& data-original=&https://pic2.zhimg.com/96f3eb9fceb9be6a6df6525_r.jpg&&&/figure&对比只是一个参考,不见得我们的技术开发人员实力很好,很多就是一般水平的。而且电商的竞争很多时候不仅仅是技术力量的角力,还有产品理念,运营能力,市场推广等等综合因素决定的,技术只是基础,特别是后进场的玩家,我们的项目上线才4个多月,我不想透露太多,避免中枪。&br&&br&这里是显摆的,我们项目最近优化的结果:&br&&figure&&img src=&https://pic2.zhimg.com/c11a932ee11ad6b421fd9_b.png& data-rawwidth=&1029& data-rawheight=&508& class=&origin_image zh-lightbox-thumb& width=&1029& data-original=&https://pic2.zhimg.com/c11a932ee11ad6b421fd9_r.jpg&&&/figure&&b&这次的优化心得:&/b&&br&由于我们运营增加了2个第三方统计,是的我们对第三方静态内容缓存控制力度下降了,但是我们改善了gzip和图片的压缩优化,比前几天提高了几分,但我认为还有优化空间,比如:&br&&ul&&li&css用到的雪碧图的压缩比例还不够,需要改进前端构建框架,改进图片压缩的算法,下个星期上一个行版本,看看效果&br&&/li&&li&html文档并没有迷你化,要将服务端的模板弄到前端构建流程里面,控制起来做压缩,这个有利有弊,利就是可以在发布到服务端前作压缩,弊端就是迷你化后不太方便调试&/li&&/ul&这两点弄好了,上90分应该不成问题。如果也是做这一块的,可以将经验分享出来,相互学习。我们和考拉网的代码量几乎一致,交互也基本相同,有一定对比价值。&br&&br&----------&br&&b&这里必须补充一点:&/b&&br&&b&用类似&a href=&//link.zhihu.com/?target=http%3A//www.alibench.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&阿里测&/a&这种工具来对比,只能作为一种参考,如果大家的业务不同,页面的Dom数量差异太多,且交互场景也有很大差别,那么这种对比是没有任何意义的。&/b&&br&----------&br&&br&&b&(二)js在页面中不同位置带来的影响或效果区别&/b&&br&&br&(这里将是讲浏览器资源加载原理和js执行原理的,用通俗易懂的方式说明白需要死掉很多脑细胞,查阅很多很多资料,可能包括已经还给老师的E文,会慢一点点。我争取说得通俗易懂,但发现很难,大家要有心理准备。)&br&&br&要彻底搞懂,为什么别人建议js放在页面的底部,那么我需要从js的语言机制及其运行环境说起。&br&&br&&b&1,浏览器不是单线程的,它多线程的,如果有必要它还是多进程的。&/b&&br&&br&很多同学并不理解浏览器不是单线程的,别问我为什么,暂时不打算做代课老师,这里有两篇,自己去理解。&br&&br&&a href=&//link.zhihu.com/?target=http%3A//ued.ctrip.com/blog/%3Fp%3D3287& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&浏览器是怎样工作的(一):基础知识&/a&&br&&a href=&//link.zhihu.com/?target=http%3A//ued.ctrip.com/blog/%3Fp%3D3295& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&浏览器是怎样工作的:渲染引擎,HTML解析(连载二)&/a&&br&&br&例如,Webkit或是Gecko引擎,都可能有如下线程:&br&&ul&&li&javascript引擎线程&br&&/li&&li&界面渲染线程&br&&/li&&li&浏览器事件触发线程&br&&/li&&li&Http请求线程&/li&&/ul&&b&&br&2,js是单线程的&/b&&br&&br&证明的脚本:&br&&figure&&img src=&https://pic1.zhimg.com/d4668c35dba4e13e5eefb0_b.jpg& data-rawwidth=&576& data-rawheight=&319& class=&origin_image zh-lightbox-thumb& width=&576& data-original=&https://pic1.zhimg.com/d4668c35dba4e13e5eefb0_r.jpg&&&/figure&&br&在1万次循环迭代过程中,foo()一直先打印了1万次‘first’,定时时间执行时间为0,它也不去执行里面的mylog函数,而是等待循环结束后,再输出1万次‘second’,看起来像运行了两次迭代,是不是表现很怪异?&br&&br&为什么?&br&&br&因为&strong&JS运行在浏览器中,是单线程的,每个浏览器页面就是一个JS线程&/strong&,既然是单线程的,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码。而浏览器是多线程的,它又一个名叫&strong&Event driven(&/strong&&strong&事件驱动&/strong&&strong&)的线程,而且浏览器&/strong&&b&具备&/b&&strong&Asynchronized(&/strong&&strong&异步&/strong&&strong&)&/strong&执行事件的特性,会创建事件并放入执行队列中,异步执行。&br&&br&浏览器定义的异步事件有很多种,例如mouse click(鼠标点击事件), a timer firing(定时器触发事件), 或者an XMLHttpRequest completing(XMLHttpRequest完成回调事件),一旦js代码中有这样的事件代码,浏览器就会将它们放入执行队列,等待当前js代码执行完成之后,再按队列情况逐个执行。&br&&br&于是,我们就看到了上面的
setTimeout(mylog, 0);
这段代码被执行的怪异表现了,也就是mylog的执行顺序被改变了。&br&&blockquote&一个有用的知识点: setTimeout(func, 0)的作用&br&&ul&&li&让浏览器渲染当前的变化(很多浏览器UI render和js执行是放在一个线程中,线程阻塞会导致界面无法更新渲染)&/li&&li&重新评估”script is running too long”警告&/li&&li&改变代码块的执行顺序&/li&&/ul&&/blockquote&&b&3,浏览器对资源的加载是线性的,可并行的,但js除外&/b&&br&&br&当我们在浏览器的地址栏里输入一个url地址,访问一个新页面时候,页面展示的快慢就是由一个单线程所控制,这个线程叫做UI线程,UI线程会根据页面里资源(资源是html文件、图片、css等)书写的先后顺序,它会按照资源的类型发起http请求来获取资源,当http请求处理完毕也就意味着资源加载结束。&br&&br&但是碰到javascript文件则不同,它的加载过程被分为两步,第一步和加载css文件和图片一样,就是执行一个http请求下载外部的js文件,但是javascript完成http操作后并不意味操作完毕,UI线程就会通知javascript引擎线程来执行它,如果javascript代码执行时间过长,那么用户就会明显感觉到页面的延迟。&br&&br&为什么浏览器不能把javascript代码的加载过程拆分为下载和执行两个并行的过程,这样就可以充分利用时间完成http请求,这样不是就能提升页面的加载效率了吗?&br&&br&答案当然是否定的。&br&&br&因为javascript是一个&a href=&//link.zhihu.com/?target=http%3A//baike.baidu.com/view/1757849.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&图灵完备&/a&的编程语言,js代码是有智力的,它除了可以完成逻辑性的工作,还可以通过操作页面元素来改变页面的UI渲染,如果我们忽略javascript对网页UI界面渲染的影响,让它下载和运行是分开的(也可以理解为js代码可以延迟执行),结果会造成页面展示的混乱,或多次重绘。很显然,这样的做法是不合适的,因此,js脚本的下载和执行必须是一个完整的操作,是不能被割裂的。&br&&figure&&img src=&https://pic1.zhimg.com/cba132e50_b.jpg& data-rawwidth=&1436& data-rawheight=&619& class=&origin_image zh-lightbox-thumb& width=&1436& data-original=&https://pic1.zhimg.com/cba132e50_r.jpg&&&/figure&百度首页的资源下载瀑布图(没有任何缓存状态下,所有http请求响应状态都是200)&br&&br&既然拆分js的下载和执行是不可行的,但为了提升用户体验,加快UI线程的执行又是一个无法回避的问题,于是浏览器就换了种方式,让它在同一个时间可以下载多个资源。&br&&br&例如上面百度的截图,在同一个域名下,firefox可以同时下载两种图片(chrome可以同时下载4个静态资源),不过这是针对图片和css文件,对于js文件似乎还是一个接着一个的下载,下载一个执行一个,不过到了js执行时候还是要严格按照顺序执行。当然,我在途中用黄色标记的几个js是并行加载的,其实是先前某个js发起的请求(这种做法就是无阻塞的js加载,也叫异步加载,后面我会说明这个东西有什么好坏)。&br&&br&多个http连接并行下载资源就好比多个线程共同完成某个任务,如果并行http连接更多,那么能有更多http资源同时被下载,但是浏览器提供并行执行的http连接实在太少了,例如上面firefox才两个,chrome也只有4个,那如何突破浏览器的连接个数的限制了?&br&&br&方法很简单就是将常用的,稳定的静态资源统一放在静态资源服务器上,由统一的域名对外提供连接,而这个域名要和主域名不一样就可以了。也就是将静态资源放在CDN节点上,单独用一个域名来对应。&br&&br&到这里,可能有人会问,是不是给每个静态资源分配一个域名,让所有资源都可以并行下载就会达到最佳状态了呢?&br&&br&答案当然也是否定的。简单滴说,有两个方面原因:&br&&br&一方面,我们采用的是http1.1版协议,它的特点是资源下载过程是一个长连接,而长连接的好处是在页面和服务端频繁交互时效率更好,但http协议有时候不是那么的可靠,导致服务器要维护一些无用的长连接,访问的人次越多越糟糕。另一方面,当我们同时在一个页面中使用的域名过多,会导致dns解析的开销增大。&br&&br&那么,多少个域是最合适的呢?好像是雅虎军规中有提过,最佳的建议是2个。也就是图片一个CDN域,css和js一个域。&br&&br&&b&4,&/b&&b&放到网页顶部的js就一定阻塞页面渲染吗?&/b&&br&&br&刚才说了,js之所以会阻塞UI线程的执行,是因为javascript能影响甚至控制UI渲染的过程,而页面加载的规则是要顺序执行,所以在碰到js代码时候UI线程就会通知js引擎来执行它。&br&&br&然而,很早很早以前,很多程序员不知道这个特点或者知道但被忽视,因此导致编写代码时候将用于展示的代码和用于处理逻辑的代码混淆在一起,这样做的后果是使js代码造成的阻塞更加严重,于是业界良心的雅虎出台了这个军规——将js脚本放置到html文档的末尾。&br&&br&如果不想深究这个话题,其实到这里就可以结束了。但是,我个人不是很喜欢按常规套路出牌,比如对于军规进行一个反问——难道将js脚本放置在head部分就一定会阻塞页面渲染吗?&br&&br&答案其实依然是否定的。我简单用常用的jQuery.lazyload插件整了两个简单的测试&br&&br&&a href=&//link.zhihu.com/?target=http%3A//sandbox.runjs.cn/show/mute4dfe& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&sandbox.runjs.cn/show/m&/span&&span class=&invisible&&ute4dfe&/span&&span class=&ellipsis&&&/span&&/a& --&这个是所有js在头部的&br&&a href=&//link.zhihu.com/?target=http%3A//sandbox.runjs.cn/show/bnam8lfs& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&sandbox.runjs.cn/show/b&/span&&span class=&invisible&&nam8lfs&/span&&span class=&ellipsis&&&/span&&/a& --&这个是所有js在尾部的&br&(这里请对比源代码)&br&两者的效果是一致的,但如果我们把js头部的lazyload实例化的脚本改成不放在
$(document).ready() 这个方法里,而是直接实例,那么lazyload就失效了。&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&c1&&//原来的&/span&
&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&nb&&document&/span&&span class=&p&&).&/span&&span class=&nx&&ready&/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=&s2&&&img.lazy&&/span&&span class=&p&&).&/span&&span class=&nx&&lazyload&/span&&span class=&p&&();&/span&
&span class=&p&&});&/span&
&span class=&c1&&//如果改成这样,并放在head标签内部,那么lazyload就失效&/span&
&span class=&c1&&//但是如果我们将这个放在所有的img元素以后,那么lazyload就又生效了&/span&
&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&s2&&&img.lazy&&/span&&span class=&p&&).&/span&&span class=&nx&&lazyload&/span&&span class=&p&&();&/span&
&/code&&/pre&&/div&&br&为什么会这样?&br&&br&虽然我们将js全部放在头部,但事实上是利用jq的一个延迟执行的接口——$(document).ready() ,让js逻辑( $(&img.lazy&).lazyload(); )的执行延迟到文档准备好了之后,因而保障了lazyload在执行的时候,页面中的img标签是存在的。&br&&br&但是,如果不放在这个接口里面,那么头部的js逻辑就会在文档准备完成之前执行,这时候页面中还没有img元素,因此也就失效了。&br&&br&不过,如果我们将这段实例化的逻辑弄到后面(所有img标签之后),即便不放在$(document).ready里面执行,lazyload就又能生效了。&br&&br&但这个时候,其实这段逻辑依赖的jQuery库和lazyload插件并不需要放在head里面了,而只要保持在实例化的逻辑之前一点点就可以了,而这种做法就是雅虎军规推荐的。&br&&br&当然,这里的结论是&b&“放到网页顶部的js不一定阻塞页面渲染”,只要将实例化的js接口或方法封装在&/b&$(document).ready接口内,这样就可以保障逻辑能够顺利进行。&br&也可以这么写:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(){&/span&
&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&s2&&&img.lazy&&/span&&span class=&p&&).&/span&&span class=&nx&&lazyload&/span&&span class=&p&&();&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&&br&&b&5,为什么电商网站喜欢将&/b&&b&js逻辑放在脚部呢?&/b&&br&&br&很显然,这并不是海淘电商的前端架构设计者有特殊的癖好,而是业务优化的需要。但是,这个需要结合不同的架构情况来具体分析,如果涉及到需要用前端模板引擎渲染页面的,情况就更加复杂了。&br&&br&比如&a href=&//link.zhihu.com/?target=http%3A//www.vip.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&唯品会&/a&,大家先去观摩一下她的首页,是不是有很多类似这样的代码:&br&&div class=&highlight&&&pre&&code class=&language-html&&&span class=&c&&&!-- 导航选择分区浮层 --&&/span&
&span class=&nt&&&script &/span&&span class=&na&&type=&/span&&span class=&s&&&text/html&&/span& &span class=&na&&id=&/span&&span class=&s&&&J_selectArea_list&&/span&&span class=&nt&&&&/span&
&span class=&o&&&&/span&&span class=&nx&&div&/span& &span class=&kr&&class&/span&&span class=&o&&=&/span&&span class=&s2&&&sel-area-box-inner&&/span& &span class=&nx&&id&/span&&span class=&o&&=&/span&&span class=&s2&&&J-areaBinner&&/span&&span class=&o&&&&/span&
&span class=&o&&&&/span&&span class=&nx&&i&/span& &span class=&kr&&class&/span&&span class=&o&&=&/span&&span class=&s2&&&ico-arw&&/span&&span class=&o&&&&&/span&&span class=&err&&/i&&/span&
&span class=&o&&&&/span&&span class=&nx&&p&/span& &span class=&kr&&class&/span&&span class=&o&&=&/span&&span class=&s2&&&sab-tit&&/span&&span class=&o&&&&/span&&span class=&nx&&请选择所在的收货地区&/span&&span class=&o&&&&/span&&span class=&err&&/p&&/span&
&span class=&o&&&&/span&&span class=&nx&&table&/span& &span class=&kr&&class&/span&&span class=&o&&=&/span&&span class=&s2&&&sab-table&&/span& &span class=&nx&&mars_sead&/span&&span class=&o&&=&/span&&span class=&s2&&&home_top_zone_link&&/span&&span class=&o&&&&/span&
&span class=&p&&{&/span&&span class=&err&&#&/span&&span class=&nx&&items&/span&&span class=&p&&}&/span&
&span class=&o&&&&/span&&span class=&nx&&tr&/span&&span class=&o&&&&/span&
&span class=&o&&&&/span&&span class=&nx&&th&/span&&span class=&o&&&&/span&&span class=&p&&{&/span&&span class=&nx&&$sort&/span&&span class=&p&&}&/span&&span class=&o&&&&/span&&span class=&err&&/th&&/span&
&span class=&o&&&&/span&&span class=&nx&&td&/span&&span class=&o&&&&/span&
&span class=&p&&{&/span&&span class=&err&&#&/span&&span class=&nx&&item&/span&&span class=&p&&}&/span&
&span class=&o&&&&/span&&span class=&nx&&span&/span& &span class=&kr&&class&/span&&span class=&o&&=&/span&&span class=&s2&&&J_select_item&&/span& &span class=&nx&&mars_sead&/span&&span class=&o&&=&/span&&span class=&s2&&&te_home_head_diqu_link&&/span& &span class=&nx&&data&/span&&span class=&o&&-&/span&&span class=&nx&&wh&/span&&span class=&o&&=&/span&&span class=&s2&&&{$warehouse}&&/span& &span class=&nx&&data&/span&&span class=&o&&-&/span&&span class=&nx&&id&/span&&span class=&o&&=&/span&&span class=&s2&&&{$id}&&/span&&span class=&o&&&&/span&&span class=&p&&{&/span&&span class=&nx&&$name&/span&&span class=&p&&}&/span&&span class=&o&&&&/span&&span class=&err&&/span&&/span&
&span class=&p&&{&/span&&span class=&err&&#&/span&&span class=&o&&/&/span&&span class=&nx&&item&/span&&span class=&p&&}&/span&
&span class=&o&&&&/span&&span class=&err&&/td&&/span&
&span class=&o&&&&/span&&span class=&err&&/tr&&/span&
&span class=&p&&{&/span&&span class=&err&&#&/span&&span class=&o&&/&/span&&span class=&nx&&items&/span&&span class=&p&&}&/span&
&span class=&o&&&&/span&&span class=&err&&/table&&/span&
&span class=&o&&&&/span&&span class=&err&&/div&&/span&
&span class=&nt&&&/script&&/span&
&span class=&c&&&!-- 导航选择分区浮层 end --&&/span&
&/code&&/pre&&/div&&br&这就是前端模板引擎的标志性代码。那么,就这种情况,我先提出几个疑问:&br&&br&a,请问当页面有大量这样的代码需要处理和维护时,页面中的js到底怎么布置才是合理的呢?&br&b,你不觉得,这样的模板维护起来是不太容易吗?直接放置在页面上,如果这个页面要经常变动(首页显然是变动频率比较高的),前端后端都可能有人来维护这份代码,请问冲突了怎么解决呢?有没有办法将冲突风险降到最低?&br&c,请问如果我们不把这些文件直接放在页面上,比如将它们弄到在某个js里面,这样做可以吗?&br&d,如果要这样做,如何让前端开发人员容易维护这样的代码,比如保存即可看到效果,需要怎样一种前端架构设计来完成这需求呢?&br&&br&&blockquote&&b&严重声明:&/b&&br&1. 这里并不是想拿VIP开刷,而是我真的一时半会找不到更合适、更有代表性的对象了。这些问题涉及前端开发模式选择的问题,这个章节我不打算展开,留给后面的前端架构分析来补充!&br&2. 虽然我觉得VIP的做法还有更好的选择,但并不是说换我去实施就一定比现在负责这一块的同行做得更好,真心不是这样的。电商这一个领域,很多时候业务的实现都是有时限的,特别是VIP这种高速发展的电商,因此这里只是一种局外人的视角,随便说说罢了。&/blockquote&&br&&b&先解决这个问题——&/b&&br&&b&为什么我会说电商喜欢将js放在页面的尾部是因为业务优化的需要呢?&/b&&br&至少我已经找到了3家(就是前面对比的),因此这不是个例。这里要搞清楚“电商的业务优化需要”是什么?&br&&br&首先,我们要知道,电商其实就是和钱打交道的网站,卖东西,然后收钱,本质上和你我他家门口的小卖部没啥两样,只是我们通过网络来进行,而网络这种东西是不透明的,你不知道卖东西的是不是一条狗还是一只猫,当然买东西的一样,大家相互信任的基础是非常薄弱的。&br&&br&好在淘宝等一大批先驱将网络支付的这种文化或习惯培养了起来,我们后进场的电商玩家就要基于各种在线支付手段来完成买卖,但是玩家人数很多啊,而且都不是BAT级别的,本来用户对于这种电商的信任度就比较低,大家对于任何可以提高用户体验的细节都要做到斤斤计较,能有多完美就要多完美。&br&&br&还有很多,但必须打住...我只是为了照顾部分客官,打了这么多废话出来,其实就是想说明电商的竞争非常激烈,极致的用户体验是分出胜负手的关键,这就是前端技术发挥余力的地方了。比如,尽快输出首屏幕的页面内容,尽快地让用户可以进行交互,等等吧。首页首屏秒开已经是电商的最低要求了,秒开,你懂吧?&br&&br&&b&那么,电商的首页首屏都有啥内容呢?如何以极限的速度呈现给用户呢?&/b&&br&&br&咱们是前端,我只能限制在前端的范畴。假设html文档都是150Kb左右,gzip之后是30Kb左右,网络带宽一样,而用户获得文档的时间基本是一致的。在这些前提下,我们同时获得文档之后,如何根据html结构布置js及其交互逻辑,才能以最快速度呈现首屏内容并提供交互呢?&br&&br&这里就以考拉网为例,其首屏内容如下图:&br&&figure&&img src=&https://pic4.zhimg.com/d00c43c8e96a60367ccf_b.png& data-rawwidth=&1440& data-rawheight=&900& class=&origin_image zh-lightbox-thumb& width=&1440& data-original=&https://pic4.zhimg.com/d00c43c8e96a60367ccf_r.jpg&&&/figure&&br&其首屏内容可细分为&br&&ol&&li&header部分(包括topbar、logo、搜索框和导航)&br&&/li&&li&slider广告幻灯片(多图)&br&&/li&&li&四个优势提醒(网易自营,低价保障,闪电发货,全场包邮)&br&&/li&&li&一个通栏广告(就是App下载的那个)&br&&/li&&/ol&由于浏览器线性加载的特点,首屏内容看到之前,我们至少会得到文档内容和css样式,如果我们第一时间让用户看到页面,那么,这里就应该只有首屏的图片就可以了,比如logo、购物侧图标、banner大图,四个优势对应的图标以及广告图片。&br&&br&如果我们将js布置在head里面,那么碰到js就要第一时间去加载js,这里就要消耗浏览器加载资源,对吗?而且js是阻塞方式加载的,即便是加载足够快,但也阻挡了首屏的内容!也就是,将js放在这里是不合适的。&br&&br&但是,我们将js放到文档的后面,如果前面有很多很多的图片资源,那么浏览器发起js加载请求可能就要等这些图片加载之后,那么首屏的页面却需要js提供交互了,比如幻灯片就是这种情况,那么首屏之后的内容就不能干扰js的加载,否则用户体验就很差,因为发生了TTI延迟问题(这个TTI兄弟,它有很多可以诉说的故事,切莫着急,俺会慢慢道来滴)。&br&&br&&b&事实上,网易考拉网的首页首屏内容的呈现的优化空间,至少有两点:&/b&&br&&br&&b&第1点:&/b&&br&&figure&&img src=&https://pic1.zhimg.com/5d3ee0961fca6fb6c18dec3c9d2a5f1c_b.jpg& data-rawwidth=&1125& data-rawheight=&475& class=&origin_image zh-lightbox-thumb& width=&1125& data-original=&https://pic1.zhimg.com/5d3ee0961fca6fb6c18dec3c9d2a5f1c_r.jpg&&&/figure&导航里面有很多“热卖大牌”,带有logo的,这些logo图片是直接用src发请求的,这是完全没有必要的,因为第一次进来用户没有触发导航下拉之前看不到这些东西,或者他可能从来到这里到离开,都不会点击打开下拉列表。&br&&br&比如我就是一个案例,在没有写这个文章之前,也不知道导航里面有那么多图片的,研究了源代码才发现这里还有那么多精彩,因此建议用js控制起来,用户没有触发之前就不要发起请求了。&br&&br&&b&第2点:&/b&&br&&figure&&img src=&https://pic3.zhimg.com/b3c1e9abc5c4da5fbd5dce_b.jpg& data-rawwidth=&1245& data-rawheight=&560& class=&origin_image zh-lightbox-thumb& width=&1245& data-original=&https://pic3.zhimg.com/b3c1e9abc5c4da5fbd5dce_r.jpg&&&/figure&首屏内容中轮播大图在用户进来之后就全部加载了,这也是没有必要的,只需要其中的一张即可。也就是,进入页面后用img的scr发起一个下载请求,其他img不要即刻去发起下载,而是等到js加载之后,用js来发起其他图片请求,等待所有轮播图片都加载完成之后,再执行轮播的逻辑。&br&&br&但是,如果要用js控制轮播图的加载就会有一个技术上的难点,如何知道轮播图片加载完成了呢?是全部完成之后再提供轮播切换交互,还是出现加载完一张就多一个切换,抑或是当加载到第n张之后就开始提供可交互,而不管n张以后的是否加载完成呢?此外,如果是要兼容IE6/7/8,坑很多。&br&&br&&b&按照这个思路,在兼顾SEO的前提下,&/b&&b&PC端&/b&&b&电商首页首屏要以极限的速度呈现给用户,我的做法是:&/b&&br&&ul&&li&&b&也就是说,&/b&&b&电商&/b&&b&网站&/b&&b&PC端做极限的性能优化,其精髓就是——&/b&&b&将浏览器基本无序的资源加载请求有序地控制起来,包括js本身,这就是js要放在脚步的缘由。&/b&&/li&&li&&b&除了首屏看得见的资源(主要是图片资源)外,其他资源一律需要通过js来控制,而不能随意地发起http请求&/b&&b&(包括首屏看不到的资源)&/b&&b&。按照这个原则,js只能放在body标签闭合之前,并且js逻辑不能随意书写!&/b&&/li&&/ul&其实这样做是有代价的,图片资源无法被搜索引擎正常抓取!但是这个对于电商来说,重要吗?大家都是要通过购买流量来PK的,所以不太重要。不同版本的浏览器下,对于资源下载都有一定优化,webkit内核的浏览器表现好很多,其他浏览器尤其是老版本的IE表现有时候让人搞不明。&br&&br&以考拉网为案例,说明电商网站喜欢将js放在底部,其缘由其实是因为业务优化需要,但并不是每一家都会这么做,典型的代表例如Tmall、VIP等,为什么有还是有不少电商是不放在底部的呢?&br&&br&其实很多时候还是要看业务需要,以及和一开始的技术选型有关,这个真不是一时半会可以说得清楚的,这里就不展开了。&br&&br&-----编辑于 09:12:23-----&br&&b&(三)逆向分析考拉网的前端架构方案&/b&&br&&br&这里分享的是根据别人将js放在底部,并且只能看到一大堆压缩优化后的代码,如何去学习别人前端架构设计的一种方法或途径。&br&&br&当然,我经常这么做,收获颇大,有时候想偷窥别人的源代码,这样才是最接近真实情况的,有时候确实也能瞎猫撞上死耗子。不管怎样,我觉得这种方法还是可行的,分享给大家。&br&&br&还是以考拉网为例。我先把首页html框架抽出来,梳理出大致页面结构,然后结合js,来看看别人是怎么做的。考拉的首页html框架如下:&br&&div class=&highlight&&&pre&&code class=&language-html&&&span class=&cp&&&!DOCTYPE HTML&&/span&
&span class=&nt&&&html&&/span&
&span class=&nt&&&head&&/span&
&span class=&nt&&&meta&/span& &span class=&na&&charset=&/span&&span class=&s&&&utf-8&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&title&&/span&网易考拉海购&span class=&nt&&&/title&&/span&
&span class=&nt&&&link&/span& &span class=&na&&rel=&/span&&span class=&s&&&stylesheet&&/span& &span class=&na&&href=&/span&&span class=&s&&&http://mm.bst.126.net/build/combo_f0ae46c.css?v=&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&link&/span& &span class=&na&&rel=&/span&&span class=&s&&&stylesheet&&/span& &span class=&na&&href=&/span&&span class=&s&&&http://mm.bst.126.net/build/combo_935bd74.css?v=&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&/head&&/span&
&span class=&nt&&&body&/span& &span class=&na&&class=&/span&&span class=&s&&&indexPage&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&nav&/span& &span class=&na&&id=&/span&&span class=&s&&&topNav&&/span&&span class=&nt&&&&/nav&&/span&
&span class=&nt&&&h1&/span& &span class=&na&&class=&/span&&span class=&s&&&hide&&/span&&span class=&nt&&&&/span&网易考拉海购&span class=&nt&&&/h1&&/span&
&span class=&nt&&&header&/span& &span class=&na&&id=&/span&&span class=&s&&&docHead&&/span&&span class=&nt&&&&/header&&/span&
&span class=&nt&&&nav&/span& &span class=&na&&id=&/span&&span class=&s&&&topTabBox&&/span&&span class=&nt&&&&/nav&&/span&
&span class=&nt&&&div&/span& &span class=&na&&class=&/span&&span class=&s&&&wrap&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&article&/span& &span class=&na&&class=&/span&&span class=&s&&&topBgWrap clearfix&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- sliderBox --&&/span&
&span class=&nt&&&/article&&/span&
&span class=&nt&&&article&/span& &span class=&na&&class=&/span&&span class=&s&&&m-slogan&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- 四个优势列表 --&&/span&
&span class=&nt&&&/article&&/span&
&span class=&nt&&&div&/span& &span class=&na&&class=&/span&&span class=&s&&&mainBgWrap clearfix&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&article&/span& &span class=&na&&class=&/span&&span class=&s&&&m-recomds&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- 每日上新列表 --&&/span&
&span class=&nt&&&/article&&/span&
&span class=&nt&&&article&/span& &span class=&na&&class=&/span&&span class=&s&&&m-halfbanner&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- 四个半屏banner列表 --&&/span&
&span class=&nt&&&/article&&/span&
&span class=&nt&&&h2&/span& &span class=&na&&class=&/span&&span class=&s&&&w-tit1&&/span&&span class=&nt&&&&span&/span& &span class=&na&&class=&/span&&span class=&s&&&big&&/span&&span class=&nt&&&&/span&今日限时特卖&span class=&nt&&&/span&&/h2&&/span&
&span class=&nt&&&div&/span& &span class=&na&&id=&/span&&span class=&s&&&hotsaleblock&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- 今日限时特卖列表 --&&/span&
&span class=&nt&&&/div&&/span&
&span class=&nt&&&article&/span& &span class=&na&&class=&/span&&span class=&s&&&m-recomds m-recomds-next&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&h2&/span& &span class=&na&&class=&/span&&span class=&s&&&w-tit2&&/span&&span class=&nt&&&&span&/span& &span class=&na&&class=&/span&&span class=&s&&&big&&/span&&span class=&nt&&&&/span&下期特卖预告&span class=&nt&&&/span&&/h2&&/span&
&span class=&c&&&!-- 下期特卖预告列表 --&&/span&
&span class=&nt&&&/article&&/span&
&span class=&nt&&&h2&/span& &span class=&na&&class=&/span&&span class=&s&&&w-tit1&&/span&&span class=&nt&&&&span&/span& &span class=&na&&class=&/span&&span class=&s&&&big&&/span&&span class=&nt&&&&/span&全球精选&span class=&nt&&&/span&&/h2&&/span&
&span class=&nt&&&div&/span& &span class=&na&&class=&/span&&span class=&s&&&m-hslist clearfix&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- 全球精选列表 --&&/span&
&span class=&nt&&&/div&&/span&
&span class=&nt&&&article&/span& &span class=&na&&class=&/span&&span class=&s&&&m-mustbuy&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&h2&/span& &span class=&na&&class=&/span&&span class=&s&&&w-tit1&&/span&&span class=&nt&&&&span&/span& &span class=&na&&class=&/span&&span class=&s&&&big&&/span&&span class=&nt&&&&/span& 海淘必买 / &span class=&nt&&&/span&&/h2&&/span&
&span class=&c&&&!-- 海淘必买列表 --&&/span&
&span class=&nt&&&/article&&/span&
&span class=&nt&&&/div&&/span&
&span class=&nt&&&/div&&/span&
&span class=&nt&&&div&/span& &span class=&na&&id=&/span&&span class=&s&&&rightBar&&/span& &span class=&na&&class=&/span&&span class=&s&&&m-rightbar&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- 左侧导航 --&&/span&
&span class=&c&&&!-- 这里语义有点问题,应该是右侧那条黑色Bar --&&/span&
&span class=&nt&&&/div&&/span&
&span class=&nt&&&footer&/span& &span class=&na&&id=&/span&&span class=&s&&&docFoot&&/span&&span class=&nt&&&&/span&
&span class=&c&&&!-- footer导航、版权区域 --&&/span&
&span class=&nt&&&/footer&&/span&
&span class=&nt&&&script &/span&&span class=&na&&src=&/span&&span class=&s&&&http://mm.bst.126.net/build/combo_a235696.js?v=&&/span&&span class=&nt&&&&/script&&/span&
&span class=&nt&&&script&&/span&
&span class=&c1&&//对导航效果进行初始化&/span&
&span class=&c1&&//这里为啥不判断 Core 是否存在呢?还不够严谨。&/span&
&span class=&nx&&Core&/span&&span class=&p&&.&/span&&span class=&nx&&navInit&/span&&span class=&p&&(&/span&&span class=&s2&&&http://mm.bst.126.net/&&/span&&span class=&p&&,&/span& &span class=&s2&&&&&/span&&span class=&p&&,&/span& &span class=&s2&&&&&/span&&span class=&p&&,&/span& &span class=&mi&&6&/span&&span class=&p&&,&/span& &span class=&kc&&true&/span&&span class=&p&&)&/span&
&span class=&nt&&&/script&&/span&
&span class=&nt&&&script &/span&&span class=&na&&src=&/span&&span class=&s&&&http://mm.bst.126.net/build/combo_ef4c658.js?v=&&/span&&span class=&nt&&&&/script&&/span&
&span class=&nt&&&script&&/span&
&span class=&c1&&//对导航效果进行初始化&/span&
&span class=&c1&&//这里的判断严谨多了&/span&
&span class=&c1&&//连续并列运算可简化判断,而且效率更高,请学习!&/span&
&span class=&nx&&Core&/span& &span class=&o&&&&&/span& &span class=&nx&&Core&/span&&span class=&p&&.&/span&&span class=&nx&&quickInit&/span& &span class=&o&&&&&/span& &span class=&nx&&Core&/span&&span class=&p&&.&/span&&span class=&nx&&quickInit&/span&&span class=&p&&();&/span&
&span class=&nt&&&/script&&/span&
&span class=&nt&&&script&&/span&
&span class=&c1&&//code here,省略&/span&
&span class=&nt&&&/script&&/span&
&span class=&nt&&&script &/span&&span class=&na&&src=&/span&&span class=&s&&&http://kxlogo.knet.cn/seallogo.dll?sn=e5000&size=0&&/span&&span class=&nt&&&&/script&&/span&
&span class=&nt&&&script &/span&&span class=&na&&src=&/span&&span class=&s&&&http://analytics.163.com/ntes.js&&/span& &span class=&na&&type=&/span&&span class=&s&&&text/javascript&&/span&&span class=&nt&&&&/script&&/span&
&span class=&nt&&&script &/span&&span class=&na&&type=&/span&&span class=&s&&&text/javascript&&/span&&span class=&nt&&&&/span&
&span class=&nx&&_ntes_nacc&/span& &span class=&o&&=&/span& &span class=&s2&&&kaola&&/span&&span class=&p&&;&/span& &span class=&c1&&//声明要统计的域&/span&
&span class=&nx&&neteaseTracker&/span&&span class=&p&&();&/span& &span class=&c1&&//网易自家的统计跟踪&/span&
&span class=&nx&&neteaseClickStat&/span&&span class=&p&&();&/span& &span class=&c1&&//网易自家对用户行文跟踪&/span&
&span class=&nt&&&/script&&/span&
&span class=&nt&&&script &/span&&span class=&na&&type=&/span&&span class=&s&&&text/javascript&&/span&&span class=&nt&&&&/span&
&span class=&c1&&//code here&/span&
&span class=&c1&&//这里还是一大堆第三方统计&/span&
&span class=&nt&&&/script&&/span&
&span class=&nt&&&/body&&/span&
&span class=&nt&&&/html&&/span&
&/code&&/pre&&/div&这份html结构可以膜拜一下,它在语义化方面做得非常出彩,几乎完美,特别是对H标签的使用非常合理,出神入化了,功力非常深厚,这种做法和163首页的做法如出一辙。可想而知,这一首页的html开发者应该系出同门,同样也可以想到网易内部的技术培训应该是完善的,建议大家好好观摩,值得学习。&br&&br&好了,神拜完了。我们开始吧。&b&第一个问题,网易考拉的js底层框架或类库是什么?&/b&&br&&br&咱们就从它的第一个js文件找找起,这就应该是他们的最基础的框架或类库,就这个 &a href=&//link.zhihu.com/?target=http%3A//mm.bst.126.net/build/combo_a235696.js& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&mm.bst.126.net/build/co&/span&&span class=&invisible&&mbo_a235696.js&/span&&span class=&ellipsis&&&/span&&/a&。&br&&br&大家不妨点击打开,然后格式化看看,工具比如&a href=&//link.zhihu.com/?target=http%3A//tool.oschina.net/codeformat/js& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&在线代码格式化&/a&,或者在Sublime text中安装一个格式化的插件,我用的是 &a href=&//link.zhihu.com/?target=https%3A//packagecontrol.io/packages/HTML-CSS-JS%2520Prettify& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&HTML-CSS-JS Prettify&/a& ,这个比较好用,其他编辑器没研究。格式化完成后,很容易找到 jquery: &1.4.2& 。就是这个了,底层是jquery-1.4.2版本,和官方的&a href=&//link.zhihu.com/?target=http%3A//code.jquery.com/jquery-1.4.2.js& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&code.jquery.com/jquery-&/span&&span class=&invisible&&1.4.2.js&/span&&span class=&ellipsis&&&/span&&/a&对比一下,没有改动。&br&&br&继续分解压缩后的代码,如下(跪求源码):&br&&div class=&highlight&&&pre&&code class=&language-text&&//jquery 1.4.2
!function(e, t) {
e.jQuery = e.$ = g
}(window);
//这里是一个二维码生成器基类,全局的
//学习点,QRCode定义在全局,但其实则放在一个闭包内部,避免污染
!function() {
QRCode = function(t, e) {
}, QRCode.prototype.makeCode = function(t) {
}, QRCode.prototype.makeImage = function() {
}, QRCode.prototype.clear = function() {
}, QRCode.CorrectLevel = l
//这里又是一个闭包,闭包内部主要是用来扩展jQuery的方法
//例如扩展Number/String原型方法、判断IE678、cookie等等
!function(e, t, i) {
//一大堆code
//这里有个有意思的东西,应该是一个弹窗
//不过是用Core.loadCdnJS异步加载的
t.dialog = t.dialog || function() {
Core.loadCdnJS(&js/dialog.js&, function() {
t.dialog.apply(t, e)
}(window, jQuery);
//这里就是页面上看到的自定义基类
var CopyCore = C
var Core = function(e, t, i) {
//一大堆code
}(window, jQuery);
jQuery(window).unload(function() {
//(⊙o⊙)…这里是让自定义基类Core在文档准备完成后初始化
jQuery(document).ready(function() {
Core.init()
//按语义,easyNav应该是一个快速导航的方法
var easyNav = function(e, t, i, n) {
//其实不用纠结是干什么的
//这里又是一个自运行的闭包
!function(e, t, i, n, o) {
//反正就是一大堆业务逻辑相关的代码
//其实不用纠结是干什么的
}(window, jQuery, Core, easyNav);
//这里又是一个自运行的闭包
!function() {
//这里里面好像是一些修复js原生方法缺陷的代码,比如json的处理
//其实不用纠结是干什么的
//这里定义了一个全局对象,带两个属性
this._nisas = {
_$host: location.host,
_$doc: document
//这里又是一个自运行的闭包
!function(e) {
//密密麻麻的不知道干什么,猜测是用来加密的,看得头晕眼花的
}(this._nisas);
//这里也还是一个自运行的闭包
!function(e, t, i) {
//autoSearch,也就是搜索时,输入关键词后自动联想的方法
}(window, jQuery);
//这里依然是一个自运行的闭包
!function(e, i, n) {
//目测就是一个弹出层登录注册业务空间
}(window, jQuery, easyNav);
&/code&&/pre&&/div&&br&----编辑于 12:54:22,继续偷窥----&br&这个文件 &a href=&//link.zhihu.com/?target=http%3A//mm.bst.126.net/build/combo_ef4c658.js& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&mm.bst.126.net/build/co&/span&&span class=&invisible&&mbo_ef4c658.js&/span&&span class=&ellipsis&&&/span&&/a&,内容就比较简单了,脉络如下:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&o&&!&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&t&/span&&span class=&p&&,&/span& &span class=&nx&&e&/span&&span class=&p&&,&/span& &span class=&nx&&o&/span&&span class=&p&&,&/span& &span class=&nx&&i&/span&&span class=&p&&,&/span& &span class=&nx&&n&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&//一大堆业务逻辑,目测是全局的,比如用户状态处理、加入购物车等&/span&
&span class=&p&&}(&/span&&span class=&nb&&window&/span&&span class=&p&&,&/span& &span class=&nx&&jQuery&/span&&span class=&p&&,&/span& &span class=&nx&&Core&/span&&span class=&p&&,&/span& &span class=&nx&&easyNav&/span&&span class=&p&&);&/span&
&span class=&o&&!&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&//easySlider,幻灯片逻辑,比较简单的,但做法值得借鉴&/span&
&span class=&c1&&//这可以学习一下,不知道是自己写的还是第三方库改的&/span&
&span class=&nx&&e&/span&&span class=&p&&.&/span&&span class=&nx&&fn&/span&&span class=&p&&.&/span&&span class=&nx&&easySlider&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&t&/span&&span class=&p&&)&/span& &span class=&p&&{}&/span&
&span class=&p&&}(&/span&&span class=&nx&&jQuery&/span&&span class=&p&&);&/span&
&span class=&o&&!&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&,&/span& &span class=&nx&&t&/span&&span class=&p&&,&/span& &span class=&nx&&a&/span&&span class=&p&&,&/span& &span class=&nx&&r&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&//也是一大堆业务逻辑,首页用到的 Core.quickInit 方法&/span&
&span class=&nx&&t&/span&&span class=&p&&.&/span&&span class=&nx&&extend&/span&&span class=&p&&(&/span&&span class=&nx&&a&/span&&span class=&p&&,&/span& &span class=&p&&{&/span&
&span class=&nx&&quickInit&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&//code&/span&
&span class=&p&&},&/span&
&span class=&c1&&//code&/span&
&span class=&nx&&myInit&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&//code&/span&
&span class=&p&&},&/span&
&span class=&p&&})&/span&
&span class=&p&&}(&/span&&span class=&nb&&window&/span&&span class=&p&&,&/span& &span class=&nx&&jQuery&/span&&span class=&p&&,&/span& &span class=&nx&&Core&/span&&span class=&p&&);&/span&
&span class=&o&&!&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&,&/span& &span class=&nx&&i&/span&&span class=&p&&,&/span& &span class=&nx&&o&/span&&span class=&p&&,&/span& &span class=&nx&&t&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&//签到,领取优惠券等逻辑&/span&
&span class=&nx&&i&/span&&span class=&p&&.&/span&&span class=&nx&&extend&/span&&span class=&p&&(&/span&&span class=&nx&&o&/span&&span class=&p&&,&/span& &span class=&p&&{&/span&
&span class=&nx&&yigouPop&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&//code&/span&
&span class=&p&&},&/span&
&span class=&nx&&getCoupon&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&,&/span& &span class=&nx&&t&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&//code&/span&
&span class=&p&&},&/span&
&span class=&c1&&//code&/span&
&span class=&p&&})&/span&
&span class=&p&&}(&/span&&span class=&nb&&window&/span&&span class=&p&&,&/span& &span class=&nx&&jQuery&/span&&span class=&p&&,&/span& &span class=&nx&&Core&/span&&span class=&p&&);&/span&
&span class=&o&&!&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&t&/span&&span class=&p&&,&/span& &span class=&nx&&e&/span&&span class=&p&&,&/span& &span class=&nx&&i&/span&&span class=&p&&,&/span& &span class=&nx&&n&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&e&/span&&span class=&p&&.&/span&&span class=&nx&&extend&/span&&span class=&p&&(&/span&&span class=&nx&&i&/span&&span class=&p&&,&/span& &span class=&p&&{&/span&
&span class=&nx&&checkIsNewer&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&//code&/span&
&span class=&p&&},&/span&
&span class=&nx&&initTemplates&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&//code&/span&
&span class=&p&&},&/span&
&span class=&c1&&//code&/span&
&span class=&p&&})&/span&
&span class=&p&&}(&/span&&span class=&nb&&window&/span&&span class=&p&&,&/span& &span class=&nx&&jQuery&/span&&span class=&p&&,&/span& &span class=&nx&&Core&/span&&span class=&p&&);&/span&
&/code&&/pre&&/div&到这里,我们基本得到了网易考拉的js业务框架模型,就是基于jQuery进行的扩展,然后定义名为‘ Core’的基类,后面均是继承这个基类来扩展业务,其实没有想象的那样复杂,反而非常简单。&br&&br&&b&简单就是高效,这是值得学习的,做架构就要将复杂的业务简单化。&/b&&br&&br&但问题是如何维护这样的代码?开发中肯定不是长成这样子的。团队配合开发,一定是分模块的,除非考拉只有一两个前端,这显然不合理嘛!如果要想得到这样的结果,并且容易维护,那么它的原来面目是怎样的呢?&br&&br&我画一个乱猜js目录结构示意图(重申,是乱猜的):&br&&figure&&img src=&https://pic2.zhimg.com/fcaafa4fe01f5e1bb89ba43dd5bed2c5_b.jpg& data-rawwidth=&1516& data-rawheight=&1095& class=&origin_image zh-lightbox-thumb& width=&1516& data-original=&https://pic2.zhimg.com/fcaafa4fe01f5e1bb89ba43dd5bed2c5_r.jpg&&&/figure&(请点击放大)&br&&br&在考拉的js源码里面,我们没有发现AMD或CMD的模块化身影,模块都是同步加载的,那么在开发中是如何调试呢?每一个js文件就写一个src请求吗?显然不合理。那么,他们是怎么做的呢?&br&&br&现在的前端本地都应该安装了nodejs环境了的,这是前提,那么能想方法有多:&br&&br&&b&方式1:&/b&&br&grunt直接本地combo,在开发状态下就如图所示,直接生成一份没有压缩的代码,比如core_debug.js,这个放置在某个地方,前端开发就直接构造URL,例如&br&&div class=&highlight&&&pre&&code class=&language-text&&&script src=&http://localhost:8080/build/core_debug.js&&&/script&
&script src=&http://localhost:8080/build/index_debug.js&&&/script&
&/code&&/pre&&/div&在开发状态下只需要弄一个grunt的任务监控,监控模块的变化,一旦有修改就马上生成新的,开发人员F5刷新一下页面就能看到页面了,或者开一个自动刷新页面的grunt任务。&br&&br&&b&方式2:&/b&&br&前端架构师写一个本地的在线combo服务器,服务器指向前端源码目录,然后让开发人员自己构造请求,然后combo服务器自动返回结果。类似这样:&br&&div class=&highlight&&&pre&&code class=&language-text&&&script src=&http://localhost:3333/??core/baseFn.js,core/core.js,core/core-init.js&&&/script&
&/code&&/pre&&/div&我以前写过名为‘一款淘宝风格的静态资源在线COMBO服务器’,有兴趣可以试试 &a href=&//link.zhihu.com/?target=http%3A//lmtdit.github.io/static-combo/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Static-combo&/a&,支持css和js。&br&&br&&b&方式3:&/b&&br&借助后端开发语言(一般就是php了,其实nodejs也可以),建立一个具备后端语言抽象能力的前端开发调试环境,然后弄一个js初始化的函数,比如写成这样:&br&&div class=&highlight&&&pre&&code class=&language-text&&&%- init_js('core/baseFn.js,core/core.js,core/core-init.js') %&
&/code&&/pre&&/div&输出的结果就如同上面的就可以了,这样的函数很容易实现的。其他语言版本也一样的。&br&&br&方法4、5、6、7、8……方法很多了,就不赘述了。&br&&br&开发好了之后,如何发版?如何解决由于内容摘要变化而引起的js引用路径变化的问题呢?这方面的原因和解析,可以参考FIS的做法,&br&&br&好了,这个问题基本到这里就超出了话题的范围。再深入,估计都没人再看了。&br&&br&&b&(四)之前的一些疑问解答&/b&&br&&b&&br&1,先针对VIP业务的几个问题&/b&&br&&br&&b&问题1:当我们要在页面中使用客户端渲染页面时,而渲染的模板结构代码直接书写在页面中,JS怎样布置才合理?&/b&&br&&br&很显然,由于模板是在页面中的,那么在这些代码之前最好将核心类库以及js模板引擎库放在它们的前面,就是这两段script代码。&br&&div class=&highlight&&&pre&&code class=&language-html&&&span class=&nt&&&script &/span&&span class=&na&&src=&/span&&span class=&s&&&//s2.vipstatic.com/js/public/jquery-1.10.2.js?&&/span& &span class=&na&&type=&/span&&span class=&s&&&text/javascript&&/span&&span class=&nt&&&&/script&&/span&
&span class=&nt&&&script &/span&&span class=&na&&src=&/span&&span class=&s&&&//s2.vipstatic.com/js/public/core3.js?&&/span& &span class=&na&&type=&/span&&span class=&s&&&text/javascript&&/span&&span class=&nt&&&&/script&&/span&
&/code&&/pre&&/div&看了一下,VIP用的是CMD模块化方案来实现core3.js,这个文件内部就是很多CMD模块合并起来的底层类库,挂载在VIPSHOP这个全局对象下,有部分公共方法则挂载在jQuery下,比如前端MVC渲染引擎就是jQuery.Template,这里的核心类库的实现方法很有意思,也是一种比较独特的实现,值得学习。&br&&br&不过在这里有一个做法让我百思不得其解,访问量如此之大的为啥还是采用覆盖式发布前端代码?跪求大神出来解答。静态资源覆盖式发布这个话题,在&a href=&http://www.zhihu.com/question/& class=&internal&&大公司里怎样开发和部署前端代码?&/a&这里有分析解答,我就不赘述了。这里只探讨当页面中存在前端模板时,我们到底如何布置js的位置。&br&&br&这里需要补充一个知识点“&a href=&//link.zhihu.com/?target=http%3A//www.w3school.com.cn/tags/att_script_type.asp& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&HTML &script& 标签的 type 属性&/a&”,type属性并没有包含“text/html”这个类型,也就是当页面碰到这个&a href=&//link.zhihu.com/?target=http%3A//www.w3school.com.cn/tags/att_script_type.asp& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&script&/a&标签时并不会当做js来处理,而是作为一个display=none的标签不做渲染。&br&&br&但是VIP页面上存在大量这样的标签,我们其实也知道这是一个前端模板引擎,需要替换为页面结构内容才有用,但是前提是有可以替换的内容才行。为此,在VIP的首页html文档上存在大量的后端输出的json内容,存储在 VIPTE 这个对象下。当用户获得html之后,这些内容就可用作渲染的数据了。&br&&figure&&img src=&https://pic1.zhimg.com/04d422e1b37c6abfdf6dcc_b.png& data-rawwidth=&767& data-rawheight=&368& class=&origin_image zh-lightbox-thumb& width=&767& data-original=&https://pic1.zhimg.com/04d422e1b37c6abfdf6dcc_r.jpg&&&/figure&&br&在这种情况下,为了更快地将 script type=&text/html& 标签内的东西渲染出来,那么就必须尽早部署前端的MVC渲染引擎,也就是core3.js,但这个文件又是基于jQuery的,jQuery又必须在它之前。这就是为什么要将这两个js放在 head 内第一时间加载的原因了。&br&&br&当然,将js部署在head第一时间发起下载请求,但是之前我已经说过了,如果要以极限的速度呈现首屏的内容,js放在head中并不是最佳的做法,但由于需要处理前端MVC的模板,不得已才将其放在了这里,这对于VIP的业务而言才是最合理的。&br&&br&&b&问题2:这样的模板维护起来容易吗?有没有更好的方法处理前端MVC模板?&/b&&br&&br&很显然,VIP的view层模板引擎系统应该是PHP提供的语言支持,可能是由前端和后端来共同维护,那么这样将大量的前端MVC模板书写在后端的模板上,在我看来这是不太容易维护的,因为前后端都可能要对其进行修改,即便主要由前端开发人员来维护,可能也会频繁改动,如果是多人维护的就非常容易导致代码冲突。&br&&br&当然,如果利用php模板包含的方式进行一定的抽象是可以将冲突的风险降低,但是还是可能被后端修改,这依然不是最完美的解决方案。而且,这种前端MVC的方式来做页面的渲染存在SEO问题,既然如此,前端MVC模板是否直接打印在html文档上,还是放在其他地方,对于搜索引擎而言都是一样的。&br&&br&那么,有没有更好的方式来处理前端模板引擎呢?}

我要回帖

更多关于 末日拾荒者开局修改 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信