京东2016版首页改版前端总结

cover

深圳的天气总是多变,前一段时间还是凉意浓浓,似乎要步入冬天了,最近却又变得炎热起来,气温骤升,让人措手不及。正如我们负责的业务一样,一年下来有诸多变化。今年9月份我们接手了京东2016版首页改版开发,历经1个多月紧张地开发测试,终于在双11前成功全量发布,回想整个开发历程,感觉还是有很多值得思索的地方,例如北京一月,虽然日夜颠倒,加班不止,但整个人居然胖了许多,不禁让人匪夷所思。

整体架构

最初听说要做新版京东首页的时候,是怀有一丝惶恐的,毕竟是作为京东的门户,其重要性和受关注程度自然不言而喻,一行代码的失误可能会造成不可挽回的后果,而且过去的首页无论性能,还有体验在业界都已经是做得非常优秀了,要再想有些出彩的地方,也是十分困难,所以综上就是压力山大。当然,花开两朵,咱们单表一枝,本文主要还是相对这次改版工作中提炼的工作方法和优化方式做出一定的总结。

这次改版,在前端架构上大体还是沿用过去的架构,使用 jQuery + Seajs 这种古老的开发方式,因为首页还依赖着许多旧的系统与组件,无法在短时间内对基础架构进行升级,当然并不是说旧的就不好,要去盲目追求一些新的东西,而是这种架构还是有可以提升的地方。

而整个项目的架构是经历之前业务进行总结提炼出来的

架构

Athena前端工程化工具,是我们团队自己探索开发的一套基于NodeJs的命令行式前端工程化工具,解决了自动化编译、代码处理、依赖分析、文件压缩等前端开发中的常规问题,有效地提升了我们的工作效率,解放生产力,目前已经应用于我们团队的多个业务中,首页改版也使用Athena来进行开发;

Athena管理平台,是Athena工具配套的管理后台,它会收集本地工具操作中上报的统计数据,包括项目、模块、页面、组件创建的信息,文件、资源依赖关系的信息等,通过这些数据来进行项目和资源的管理,同时提供了项目模板,方便使用本地工具创建项目时选择,具体可以参考之前的博文我们是如何做好前端工程化和静态资源管理

Athena组件平台,是基于Athena总结的一套业务组件的平台,可以很好地管理我们的业务组件,方便组件的复用和传播;

Athena基础库及组件库,是业务中总结出的基于jQuery + Seajs的js库,简化业务开发,提供完整的框架;

Athena模拟接口,可以自由编辑生成指定接口的假数据,用于开发时真实接口的替代,让开发不再依赖后端接口;

Athena兜底接口服务,可以指定接口生成一份兜底数据接口,平台会定时去抓取指定接口数据,然后生成兜底数据到CDN,从而生成对应的兜底接口,这样让正常接口多一份兜底保障;

Athena前端监控,通过在页面中进行埋点上报的方式,我们可以在监控系统中,实时地看到性能相关数据。我们进行上报的不止有页面性能、速度相关的数据,同时会上报用户的环境信息,例如操作系统、浏览器、网速等,而且还会对页面中错误信息进行上报,如模块的隐藏等,通过这些数据,对我们的业务进行实时地监控与分析。

在我们的架构中,各种各样的工具与系统相辅相成,覆盖到了开发到上线的各个环节,自成一套体系。这样的架构不止是针对首页这个业务的,而是在基于对之前业务开发总结的基础上进行完善、调整的架构,适用于我们各个业务。而这次首页的改版中,我们对开发模式、性能优化、体验优化都进行了一些新的探索,让我们对于业务开发的整体解决方案又有了新的改进。

开发模式

Athena

开发效率的提升是我们一直追求的,工欲善其事,必先利其器,我们通过总结以往的开发工作,提出了各种手段来优化我们的开发效率,前端工具Athena就是其中的一个产物,当然它又不仅仅是为了提升开发效率而已,它是我们总结出的一套针对前端开发的完整解决方案,可以让我们的整体开发流程更加简单明了。

Athena提供了统一的项目架构,根据业务功能不同,我们将一个项目(app)拆分成不同的业务模块(module),而每一个模块都包含自身的页面(page)以及构成页面所需要的组件(widget)。

项目架构

在本地使用Athena创建完整的项目结构,随后我们就可以只关注代码逻辑的书写,Athena提供了简便的操作命令可以一键式地实时编译预览我们的页面,从而让我们不必去关心文件处理、代码编译等细节,开发完后,可以通过Athena执行完整的编译步骤并同步到我们的服务器上方便进行浏览测试。

使用Athena,新版首页开发模式大致如下:

开发模式

前后端分离

基于Athena工具,我们目前已经可以做到完全地本地开发调试了,但是还并不能做到完全的前后端分离,以过去首页为例,页面被拆分成首屏楼层,首屏采用直出的方式以提升速度,楼层则使用异步加载的方式,拉取服务器上已经渲染好的HTML字符串,如图

过去模式

整个页面,包括首屏和楼层,都需要前端写好静态HTML,然后给后端开发同学来套用,转成后端语言对应的模板,这样导致前后端耦合较深,HTML更新极不方便,开发成本较高。

为了解决这样前后端耦合的问题,减少沟通成本,这次首页改版我们使用了新的开发方式,为了保证首屏速度,首屏依然采用直出的方式,但对非首屏的楼层进行改进,使用前端模板 + 数据开发方式,将DOM字符串的渲染放到前端来做,后端只提供数据接口,以此来达到前后端分离的效果,同时在开发中使用假数据平台模拟接口,让前端工作不再依赖后端。

现网模式

在最开始提出这样前后端分离方案的时候还是受到了不少的质疑,因为使用前端模板 + 数据开发方式,会使得每个楼层都多一个接口,并且需要依靠JS来动态渲染,会影响到楼层加载的性能,但经过我们的测试证明在现代PC浏览器下两种模式前端渲染和后端渲染并不会相差太多,并且在模板、数据双重缓存下,这样的差距更是微乎其微了,更关键的是能让我们的开发效率有所提升。

性能数据对比

当然,我们对于性能的追求总是孜孜不倦,为了让楼层的加载速度更快,减少请求,我们在后续将使用在服务端定时获取数据编译前端模板,然后生成静态文件推送到CDN的方式来改进,和之前的由后端开发同学套模板生成静态文件不同的是,我们将自己搭建这样的中间层服务,在服务端编译前端模板,实现前后端同构,而前端可以随时切换渲染方式,改成请求渲染好的 HTML 字符串来进行加载,以此来提升性能。

对性能优化的探索

性能永远是前端工程师追求的主题,过去首页在性能优化上已经做得非常极致了,它已经使用了各种手段来优化性能,包括首屏直出、样式直出来提升首屏速度,楼层按需加载,减少不必要的请求等等,所以在做新版首页的时候,我们感觉战战兢兢,因为改版不能让页面受到影响,而且最好还能比原来更快,所以,这次改版中我们主要通过如下手段来进行性能的优化。

首屏直出和精简

首屏直出是让首屏速度更快的最佳选择,此次版本依然选择了首屏直出的方式,直出的内容包括首屏HTML,页面楼层骨架,以及样式和一些必须的脚本,看起来和之前的方式如出一辙,但此次改版我们还是做了很大的改进,那就是让首屏更加精简。

过去是将页面引用的所有样式都直出在页面上,没有外链的CSS,各种优化手段都考虑进去了,那么新版首页就只能在精简大小上下功夫了,所以新版首页的首屏只直出了首屏必须的样式,同时只直出一小部分必须的脚本,而非首屏的楼层样式拆分到各自楼层中,和楼层的模板放在一起,按需加载。

通过对Athena工具的改造,我们实现样式、模板的统一抽离这一功能,并且是在项目编译阶段自动实现的,开发者勿需关心。由于Athena统一的项目结构,每一个楼层在我们的项目中对应一个widget的组件,组件包含自己的HTMLCSSJavaScript文件,同时widget的组件是可以继续调用其他widget的组件的,所以在编译时,工具会自动分析所有widget的依赖关系,然后把楼层的模板和所有引用到样式打包到一个文件中。最后在楼层加载的时候去请求这个文件,然后解析加载。这样的抽离工作会在最后的项目编译阶段进行,而进行本地开发预览的时候并不会执行,这样保证了开发的效率。

1
2
3
4
5
6
7
// https://misc.360buyimg.com/mtd/pc/index/home/rec_tpl.min.js
jsonCallBack_rec_tpl({
dom: '{%var i,clstagPrefix = pageConfig.clstagPrefix + o.staticLogTag;var isWide = pageConfig.compatible && pageConfig.wideVersion;%}{% var len = o.list.length; len = Math.min(len, 3); %}{% if (len >= 1) { %}<div class="grid_c1 rec_inner"><ul class="rec_list">{% for(i = 0; i < len; i++){ %}{% var item = o.list[i]; %}{% var imgUrl = isWide ? item.imgUrl : item.imgUrlB; %}<li class="rec_item" fclog="{%= item.clog %}"><a href="{%= item.url %}" class="rec_lk" target="_blank" clstag="{%= clstagPrefix + \'a\' + (i < 9 ? \'0\' : \'\') + (1+i) %}"><img src="//misc.360buyimg.com/mtd/pc/common/img/blank.png" data-lazy-img="{%= imgUrl %}" alt="{%= item.title %}" title="{%= item.title %}" class="rec_img" data-webp="no" ></a></li>{% } %}</ul></div>{% } %} ',
style: ".rec_list{overflow:hidden;height:100px}.rec_item{overflow:hidden;float:left;width:396px;height:100%}.rec_lk{display:block;height:100%}.rec_img{display:block;margin:auto}.o2_mini .rec_item{width:330px}.csstransitions .rec_img{-webkit-transition:opacity .2s;-moz-transition:opacity .2s;transition:opacity .2s}.csstransitions .rec_lk:hover .rec_img{opacity:.8}",
time: 1479195351434,
version: "ff78610a0ef9cdbb"
});

通过上述手段,我们让首屏变得更加精简,从下面的对比中就可以看出

这是过去首页首屏大小

过去首页首屏

这是新版首页首屏大小

新版首页首屏

可以看出优化精简之后,新版的首屏的大小减小了非常之多。

首屏轮播第一帧直出

一直以来轮播都是靠页面最后加载的JS来进行渲染的,因为轮播图有随机渲染图片的逻辑需要依赖JS,但在一段时间的观察之后发现,如果CDN出现抖动,或者用户的网速较慢,那么首屏轮播这一块位置就会一直空着,给人的体验非常不好

轮播

所以在这一版的首页中我们将轮播图第一帧的数据直出在页面上,同时也将第一帧的渲染逻辑也直出在页面上,这样一来,首屏轮播出来得就非常快,减少用户的等待时间。

轮播

楼层按需加载与滚动优化

首屏直出后,非首屏的内容肯定也不会一次性全部加载,因为像首页这样的页面楼层非常之多,一次性加载全部不仅仅慢,而且对接口来说也是一种损耗,所以我们考虑将楼层按需加载。

在我们新的方案中,已经采用了前端模板+数据的开发模式,所以在开发中我们想用直接书写前端模板的方式来进行开发,然后在本地进行预览,而在项目编译时能将我们的模板编译成独立的文件,方便渲染逻辑进行加载。所幸Athena工具已经支持了这样的功能,在开发中我们以编写前端模板的方式去开发整个页面,随后通过编译工具,在代码编译阶段自动将楼层的模板和样式抽离成一个与组件同名的独立JS文件,通过页面加载逻辑去按需拉取模板文件,再进行渲染。

下面例子揭示了楼层模板生成的过程

直接书写前端模板,编写模板时我们给模板加上标记位 o2-out-tpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script type="text/template" class="o2template" o2-out-tpl>
{%
var i,
clstagPrefix = pageConfig.clstagPrefix + o.staticLogTag;
var isWide = pageConfig.compatible && pageConfig.wideVersion;
%}
{% var len = o.list.length; len = Math.min(len, 3); %}
{% if (len >= 1) { %}
<div class="grid_c1 rec_inner">
<ul class="rec_list">
{% for(i = 0; i < len; i++){ %}
{% var item = o.list[i]; %}
{% var imgUrl = isWide ? item.imgUrl : item.imgUrlB; %}
<li class="rec_item" fclog="{%= item.clog %}">
<a href="{%= item.url %}" class="rec_lk" target="_blank" clstag="{%= clstagPrefix + 'a' + (i < 9 ? '0' : '') + (1+i) %}">
<img src="//misc.360buyimg.com/mtd/pc/common/img/blank.png" data-lazy-img="{%= imgUrl %}" alt="{%= item.title %}" title="{%= item.title %}" class="rec_img" data-webp="no" >
</a>
</li>
{% } %}
</ul>
</div>
{% } %}
</script>

在编译时扫描依赖关系,生成模板JS文件依赖组件的关系表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"dependency": {
"elevator_tpl.js": [],
"entry_tpl.js": [
{
"widgetName": "spetit",
"module": "home",
"moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2",
"widgetType": "widget",
"exists": true
}
],
"fbt_tpl.js": [
{
"widgetName": "find",
"module": "home",
"moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2",
"widgetType": "widget",
"exists": true
},
{
"widgetName": "brand",
"module": "home",
"moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2",
"widgetType": "widget",
"exists": true
},
{
"widgetName": "top",
"module": "home",
"moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2",
"widgetType": "widget",
"exists": true
}
]
}

通过关系表去合并处理CSS样式,再和前端模板一起计算出MD5,生成独立的JS文件

1
jsonCallBack_rec_tpl({dom:'{%var i,clstagPrefix = pageConfig.clstagPrefix + o.staticLogTag;var isWide = pageConfig.compatible && pageConfig.wideVersion;%}{% var len = o.list.length; len = Math.min(len, 3); %}{% if (len >= 1) { %}<div class="grid_c1 rec_inner"><ul class="rec_list">{% for(i = 0; i < len; i++){ %}{% var item = o.list[i]; %}{% var imgUrl = isWide ? item.imgUrl : item.imgUrlB; %}<li class="rec_item" fclog="{%= item.clog %}"><a href="{%= item.url %}" class="rec_lk" target="_blank" clstag="{%= clstagPrefix + \'a\' + (i < 9 ? \'0\' : \'\') + (1+i) %}"><img src="//misc.360buyimg.com/mtd/pc/common/img/blank.png" data-lazy-img="{%= imgUrl %}" alt="{%= item.title %}" title="{%= item.title %}" class="rec_img" data-webp="no" ></a></li>{% } %}</ul></div>{% } %} ',style:".rec_list{overflow:hidden;height:100px}.rec_item{overflow:hidden;float:left;width:396px;height:100%}.rec_lk{display:block;height:100%}.rec_img{display:block;margin:auto}.o2_mini .rec_item{width:330px}.csstransitions .rec_img{-webkit-transition:opacity .2s;-moz-transition:opacity .2s;transition:opacity .2s}.csstransitions .rec_lk:hover .rec_img{opacity:.8}",time:1479466862559,version:"ff78610a0ef9cdbb"});

同时会在逻辑脚本入口位置自动加入模板的版本号

1
2
3
4
5
6
7
8
9
10
11
12
{
"elevator_tpl": "e4d5dbaa3ecd12d2",
"entry_tpl": "e3150fce4b2b332a",
"fbt_tpl": "18f8bff18188a453",
"floor_coupon_tpl": "1559694cb962e0d6",
"floor_ract_tpl": "13b92d16fb6e2f7a",
"mod_footer_tpl": "49142394d0e7f24e",
"more_tpl": "d300081dd7f13f78",
"portal_tpl": "68fae801a032cf93",
"rec_tpl": "ff78610a0ef9cdbb",
"seckill_tpl": "f11d04fd7eabc0e6"
}

模板文件通过系统发布到CDN后,我们就需要有一套加载逻辑来进行加载。通过监听滚动事件,我们判断让处于浏览器视窗内的楼层进行加载,由于监听了滚动事件,为了让滚动更加流畅,我们必然要对滚动中做的操作进行优化。为了避免滚动操作不断被触发,需要对滚动进行节流处理。我们的原则是尽量避免在滚动的时候进行DOM操作与复杂计算,所以在渲染逻辑初始化的时候,我们就已经收集好了楼层的相关信息,包括楼层高度、楼层的offsetTop等,这样在滚动的时候就不再需要进行任何DOM操作了,让滚动的效率有所提升。而当楼层的数据例如楼层高度发生变化时,则通过消息通知的机制来实时地更新楼层信息即可。

脚本延后加载执行

除了楼层是按需加载的,页面中用到的一些脚本文件也是尽量延后加载、执行。Athena工具在代码打包的时候,会对每个独立的文件进行单独处理,同时生成一份静态资源的线上对应表,在编译的最后会将引用的资源替换成配置的线上绝对地址。我们可以使用Seajs提供的require.asyncAPI来进行异步加载资源,这样让资源加载更加合理。

1
2
3
4
5
// 开发中的代码
require.async(__uri('APP_JS_ROOT/header.js'))
// 编译后
require.async('//misc.360buyimg.com/mtd/pc/index/js/header.js')

同时,还有业务上一些统计上报等逻辑,可以放到 window onload 事件之后再执行,这样可以避免由于类似统计这样的请求占用到页面加载资源,从而降低页面 onload 时间。

模板、数据分离缓存

每个楼层都按需加载之后,每次去加载这个楼层是否都要重新去请求这个楼层的模板和数据呢?答案当然是否定的。

目前大部分浏览器已经提供了许多前端缓存的解决方案,而其中兼容性最好,易用性最强的非localStorage莫属。利用localStorage我们可以对模板和数据进行缓存,这样当用户第二次加载的时候就可以不用再去请求网络资源,而可以直接从本地获取了。

但缓存之后如何进行更新呢?我们可以通过进行MD5校验版本来实现。

对数据来说,数据是由后端给出的,我们可以让后端同学将可以缓存的接口数据计算出一个MD5值作为版本号,然后直出在页面上,同时在接口中返回这个版本号,这样当前端去加载是首先判断版本号是否一致,以此来判断是直接读缓存还是从网络请求资源。

接口版本号

而对于模板来说,则可以通过Athena工具,在每次编译的时候自行计算出版本号,写入模板文件和入口JS文件中,这样在模板加载的时候也可以进行比对。

单个模板文件

1
2
3
4
5
6
7
// https://misc.360buyimg.com/mtd/pc/index/home/rec_tpl.min.js
jsonCallBack_rec_tpl({
dom: '{%var i,clstagPrefix = pageConfig.clstagPrefix + o.staticLogTag;var isWide = pageConfig.compatible && pageConfig.wideVersion;%}{% var len = o.list.length; len = Math.min(len, 3); %}{% if (len >= 1) { %}<div class="grid_c1 rec_inner"><ul class="rec_list">{% for(i = 0; i < len; i++){ %}{% var item = o.list[i]; %}{% var imgUrl = isWide ? item.imgUrl : item.imgUrlB; %}<li class="rec_item" fclog="{%= item.clog %}"><a href="{%= item.url %}" class="rec_lk" target="_blank" clstag="{%= clstagPrefix + \'a\' + (i < 9 ? \'0\' : \'\') + (1+i) %}"><img src="//misc.360buyimg.com/mtd/pc/common/img/blank.png" data-lazy-img="{%= imgUrl %}" alt="{%= item.title %}" title="{%= item.title %}" class="rec_img" data-webp="no" ></a></li>{% } %}</ul></div>{% } %} ',
style: ".rec_list{overflow:hidden;height:100px}.rec_item{overflow:hidden;float:left;width:396px;height:100%}.rec_lk{display:block;height:100%}.rec_img{display:block;margin:auto}.o2_mini .rec_item{width:330px}.csstransitions .rec_img{-webkit-transition:opacity .2s;-moz-transition:opacity .2s;transition:opacity .2s}.csstransitions .rec_lk:hover .rec_img{opacity:.8}",
time: 1479195351434,
version: "ff78610a0ef9cdbb"
});

JS入口文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// https://misc.360buyimg.com/mtd/pc/index/home/index_focus.min.js
window.tplVersion = {
"1212_tpl": "ce7dcd7cd0beacb2",
elevator_tpl: "e4d5dbaa3ecd12d2",
entry_tpl: "2caa7cd543c322ea",
fbt_tpl: "18f8bff18188a453",
floor_coupon_tpl: "b98cf33be84aae98",
floor_ract_tpl: "13b92d16fb6e2f7a",
mod_footer_tpl: "072072ffc47778be",
more_tpl: "25dcb060800c503a",
portal_tpl: "68fae801a032cf93",
rec_tpl: "ff78610a0ef9cdbb",
seckill_tpl: "4fee56c5b073e5e1"
};

通过上述方式,我们实现了模板、数据的分离缓存,由于楼层类似的关系,页面中的模板大多数是重复,这样子模板缓存起来就能大大提高模板的利用率,当用户第二次访问的时候将不会再产生请求,在加速访问的同时,减少网络带宽消耗,并且如果数据发生更新,用户只需要更新数据即可,大大减少流量消耗。

大量使用WebP格式图片

在这次改版中,很多的图片我们都使用了WebP格式来减小图片大小。

WebP格式,是谷歌开发的一种旨在加快图片加载速度的图片格式,图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。但WebP的兼容性不太好,目前基本只有Chrome浏览器可以支持,不过这对我们的首页来说,使用WebP还是会有很大的收益,因为通过我们的统计数据可知,首页Chrome用户已经占到了60%左右。

体验优化探索

在努力提升页面性能的同时,还要让页面的用户体验有所提升,这需要我们能站在用户和前端的角度提出合理的优化方案。

高清屏适配方案

人类的社会在发展,人类的社会在进步,现如今高清分辨率屏幕的应用已经越来越多,高冷的Mac自不必说,现在许多新型号的Windows电脑也配备了高清分辨率的显示器,所以为了提升这一部分用户的浏览体验,我们需要在高清屏上启用高清素材。

但页面中素材图基本都是运营上传的,如果传两套图对运营来说未免太过麻烦,但如果只传一套高清图,直接展示的话对非高清屏没有必要,会造成流量损耗。这时候京东给力的图片服务就发挥作用了。

图片服务支持按一定规则改变URL来等比缩放图片,例如原图是一张800X340的图片

1
//img13.360buyimg.com/cms/jfs/t3412/357/1332248120/113691/f29c2f1e/58244d4dN08b89f9e.jpg!q90.webp

我们可以通过这样设置来得到一样等比缩放400X170的图片

1
//img13.360buyimg.com/cms/s400x170_jfs/t3412/357/1332248120/113691/f29c2f1e/58244d4dN08b89f9e.jpg!q90.webp

这样的话,运营同学只需要上传一张高清图片,我们通过判断是否高清屏,来动态改变URL,使用图片服务来得到一张等比缩放的非高清素材,而且CDN会根据图片URL进行缓存,也就是说只要第一次访问过缩放的图片就好,这样性能也不会有什么损耗。

强制webkit内核渲染

很多国产浏览器都是双内核,例如360、QQ浏览器等,而它们都提供了强制使用Webkit内核渲染的开启方式,这样可以让用户获得更好的浏览体验。

1
<meta name="renderer" content="webkit" />

OpenSearch

现在很多网站都能实现在浏览器搜索框内直接调用网站内部搜索的功能,这是通过 OpenSearch 来做到的,而京东之前一直是没有的,这样显然是不合适,而且有一些习惯于使用地址栏搜索的用户不能满足。在这次改版中,我们加上了这一功能,使得用户可以在浏览器地址栏就能直达京东搜索。

enter image description here

使用Icon Font

使用Icon Font可以提升设计师的发挥空间,在页面上使用一些特殊字体以提升页面的美观程度,让页面看起来更具有设计感,更加细腻,从而提升用户的浏览体验。

Iconf font

而且Icon Font兼容性非常好,可以让不同浏览器的用户获得一致的浏览体验,并且通过字体压缩工具,压缩后的字体文件也可以非常小,不会有太多的性能损耗。

空闲时间自动加载楼层及图片

前文提到,我们使用了按需加载来提升页面性能,但这样带来的问题就是只有当用户滚动楼层到浏览器视窗内,楼层才会开始加载,这样用户滚动得稍微快一点就会出现很多loading动画。

空闲加载

为了减少这种情况的发生,让用户觉得楼层也加载很快,在不影响页面滚动、加载性能的前提下我们在用户操作的空闲时间自动加载剩余的楼层和图片。

将楼层的加载操作放入一个队列中,我们可以在用户停止滚动操作3s后开始自动加载这个队列中的楼层,而当用户开始滚动的时候清空这个加载队列,停止滚动3秒后又重新开始加载。通过这样处理可以合理利用用户浏览的空闲时间来加载页面,让用户感觉页面加载更快。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var scrollTimer = null;
var isScrolling = false;
$(window).bind('scroll.loadFloor', function (e) {
isScrolling = true;
clearTimeout(autoLoadTimer);
clearTimeout(scrollTimer);
autoLoadingQueue = [];
resourceLoader && resourceLoader.pause();
scrollTimer = setTimeout(function () {
isScrolling = false;
if (pageConfig.idleTimeLoad) {
autoLoadTimer = setTimeout(autoLoad, 3000);
}
}, 200);
});
function autoLoad () {
if (!isScrolling) {
runFloorLoadQueue();
}
}

页面可用性保障和监控

灾备策略

对于像京东首页这种大流量的网站,后端接口可能偶尔会出现错误,或者直接挂掉,特别是在双11这种可能会达到流量峰值的时候,但是不能因为接口出错的原因而使得页面显示出现错误。这就需要前端来配合给出一套合理的灾备方案。

通常,我们通过接口缓存、超时、重试来进行灾备处理。目前首页大部分接口、及所有模板请求,在请求成功后都会存入本地缓存,第二次请求,假如缓存没有过期将直接使用缓存,假如缓存过期将会重新请求,而一次正常的请求,都会经过超时或异常重试的逻辑,来保证用户能尽量访问到正常的数据,在正常接口无法获取数据之后又会有兜底接口来保障数据来源,这样的层层保障,很好地保证了页面的完整性。而且,针对所有接口,前端均有数据校验逻辑,每一个后端接口都要经过前端的数据校验,来验证接口的可用性,假如接口数据异常,前端将主动调用兜底接口来替代,这样来保证页面不至于错乱。

综上所述,首页的接口和模板正常请求流程如下

接口异常

这样一套复杂的流程下,每一个接口、模板请求都是统一的,所以需要对此进行封装,以便调用。首页是通过封装改造$.ajax来实现的,使用$.ajaxPrefilter$.ajaxTransport方法对每个异步请求进行捕获处理,将接口、模板请求的重试、超时、缓存、兜底调用等封装起来,对调用者透明,使用起来变得非常容易,而不需要关心以上灾备策略的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var ajax = require('load_async');
// 本质就是$.ajax方法
ajax({
url: '//f.3.cn/index-floor/?argv=aggr',
jsonpCallback: 'jsonpCallbakcAggr', // jsonp回调函数名
params: {}, // 参数
needStore: true, // 是否需要缓存
storeSign: '3aad2efsdf', //用户判断缓存是否过期的标记
timeout: 3000, //接口超时
times: 2, // 超时重试次数
backup: '//www.3.cn/bak/aggr', // 兜底接口
dataCheck: function (result) { // 接口数据校验,校验接口返回数据,若为true则走正常逻辑,为false则自动调用兜底逻辑
if (result && result.code === 0) {
return true;
}
return false;
}
});

数据统计驱动改进

在这次首页改版项目中我们接入了Athena测速系统用于收集首页各种性能以及用户环境相关的数据,因为有了数据统计,我们才能知道用户端具体的情况信息,有了数据统计,我们才能对页面进行实时监控,有了数据统计,我们才能掌握我们做性能优化的成果,所有的分析都是要基于数据来进行,否则就是在自己在YY了。

目前我们主要收集了,用户网速、操作系统、浏览器分布、分辨率分布等各种信息,同时对于页面加载情况也有一定的监控,如页面测速打点上报、数据接口出现调用兜底接口的情况上报、楼层接口失败导致楼层隐藏的情况上报等。

通过以上数据统计,我们可以灵活地对我们的页面进行优化,同时及时发现问题,避免损失。例如我们通过统计发现用户在网速低于一定值时页面楼层隐藏数增多,这样我们就可以通过设置更长的超时时间来减少这一情况的发生,还有就是假如某时刻开始发现某接口调用兜底请求数暴增,可以判定接口出现问题而及时反馈给后端同学。

更长远的探索

新版首页已经上线小一个月了,表现一直还算良好,我们做出的性能以及体验优化也得到了体现,在此基础上,我们思考了更多的可以做的工作,来提升首页的表现。

静态资源预加载

首页承载着许多页面的入口,如频道页还有活动页,在双11的时候,首页会有很多直达活动的入口,如果我们能在首页预加载某些重要的活动页面的资源的话,当用户去访问这些活动页面就能更加迅速地打开浏览了。

静态资源预加载

架构升级

jQuery + Seajs或许让人感到老旧且沮丧,我们考虑在首页上渐渐使用一些新的技术,例如去Seajs化,提供更优的打包方式,让页面性能进一步提升。

中间层探索

目前首页虽然差不多实现了前后端分离,但是首屏这里前后端依然存在耦合,假如前端可以介入到中间层的开发,那问题就迎刃而解了,接入中间层后,我们还可以将页面部分楼层做服务端渲染,以减少前端渲染的性能损耗,可以在实现前后端分离的基础上,让页面性能更好,还是有一定意义的。


迟到的2015总结

image

拖延癌最晚期了,估计是没治了,今年已经过去整整两个月了,才悠悠打开电脑,开始整理2015年所经历的有关于生活、工作的种种一切。

快到春节了,期盼可以好好休息下了,但总是有很多事缠住我们,憧憬着回家,期待着年终奖,写不完的代码,拯救不了的忧伤,所以每天依然忙碌。而有时想起去年此时,心中不免有些感慨,去年啊,或许正在中关村软件园文思楼里享受暖气的温暖包围敲着代码打着LOL吧,如今却身在几千里之外的地方了。这不免让我花点心思,好好想一想过去的一年。2015年对于我来说是非常重要的一年,这一年的过去意味着许多事已成为历史,被打上时间的印记,许多抉择也尘埃落定,不能改变,不容质疑,只能继续前行去追寻答案。

决定

过去的这一年,我做了许多决定,且大多是影响深远的决定,值得庆幸的是这些决定都是我自己下的,是我思索后的结果,非常走心,所以无论结果如何,都没有什么需要惋惜,也没有什么可以埋怨。

去年年初回北京之后,我迅速地提出了离职的想法,随后沟通,提申请,走流程,交接工作,前前后后一个多月时间,最终4月底离开了百度,5月初即离开了北京。

离别前总有不舍,前进时总有不安,不舍是感情的停留,不安是对未知的迷茫。在百度贴吧工作快2年,在北京待过快2年。回想着,还能记得2012年3月份和文清同学一起在一个刮着强风吹着沙尘暴的时间里,来到北京,为了面试一个实习生的岗位,彼时,对这家如雷贯耳的公司充满敬意与遐想,而对于能否留下来也充满了忐忑,随后面试通过,实习,最后正式入职。时光如梭,那年面试至去年此时已3年,而至现在已4年了,但我还能记得曾经答错了的面试题,还有填登记表时,前台女生职业的微笑。

离开北京南下深圳的决定其实下得非常突然,去年春节仓促间的决定,突然的自我,甚至春节前我还在憧憬着将来在北京的发展。其实我一直是一个非常犹豫的人,面对选择总是很难做出决定,也不是一个很有勇气的人,我总有很多顾虑,很多牵绊,但这一次我有强烈的渴望和冲动,我希望能快速来到深圳,我希望能堂堂正正努力一回,有梦总还是要去追的。

找工作

就这样在来不及规划接下来我该去哪家公司工作,同时对于深圳的了解基本为零的情况下,我提出了离职,提完后一阵茫然惶恐,首要面对的问题就是找工作了。我恐惧面试,害怕水平不够,唯一的面试经历还是几年前实习的时候,如今会面些啥几乎没有感觉。然后仓促准备简历,请教了好几个人简历该怎么写,改了好几版,要感谢段大君同学给我的建议,最后给出了一份还算过得去的简历,然后各个平台上投递,等候面试。

最后实际面试了就4家公司,阿里、腾讯、京东,还有招联消费金融,阿里电话聊完后要我去深圳当场面,我觉得去了深圳工作却没有确定下来没有安全感,于是PASS了。腾讯倒是面了几个部门,流程之漫长简直发指,等我接到offer之后,我已经答应京东UED要入职了,于是作罢。招联消费金融是面试最快的,只有一轮,OMG,最后感觉发不出谈好的17个月,且月薪不是太高,于是没有答应。京东这边面试倒是挺流畅,offer给得挺快,最后也顺利入职了,现在也干得挺开心,这边同事也非常赞,这就是后话了。

西二旗少了一个肥硕奔跑的身影,颐和园少了一个无聊的伪摄影爱好者,无人知晓,不曾来过。

image

就这样,结束了在北京的工作生活,转而南下了。百度、北京带给我的记忆无法磨灭,生命中的黄金时代曾有一部分在这里度过,它带给我的是从学生到社会青年的改变,这身份的转背后,是无数次往返于公司和住所的地铁票,是无数个加班后独自回家的夜,还有心境的潜移默化。只能说,我已经再不是青春少年。

这一年的技术与工作

整理一下过去的一年自我的成长吧。

去年写的最多的用Node来做工具吧,整理了一下过去的经验,结合工作方式,用Node撸出了一个现在组里正在用的一个前端工程化的工具,也就是组里同学“深恶痛绝”听到厌烦的Athena——雅典娜。取名叫Athena是因为她是智慧女神,很能代表工具的特色嘛。这个项目最初的方案是经典的 Yeoman+Gulp 的方式,最后发现安装困难以及编译脚本更新麻烦等原因改成了现在npm包全局安装的方式。最开始的设想也很简单,就想要一个自动化、组件化的工具,没想到后来各种需求加入,代码量急剧膨胀,形势已开始变得有些失控了,加上前期设计不太完善,导致bug略多,现在版本号依然停留在0.0.x的阶段。。。当然年前的期望是能到1.0.0-rc0.0.1,嘿嘿嘿。

image

Athena这个项目中我的收获就是对Node的各类API非常熟悉了,也对Node的版本号变化更加敏感。同时也是对我之前工作经验的总结,让我对前端工程化的概念有了实践的机会,对于构建我自己的前端开发体系帮助非常大。

做Athena之前有一个项目也是希腊神话的神名,叫Hermes,是赫尔墨斯,不是爱马仕。一个静态资源预加载的系统,在这个项目中玩了一下MEAN,也就是Mongo、Express、Angular、Node,感受了一下全栈的魅力,最直观的感受就是,能自己给自己定数据格式,给API的感觉真好。这一套方案配合gulp使用还是非常爽的,开发迅速,调试使用一些工具也很方便,而测试,后端自不用说,Angular配合Karma也可以玩的很6。但Angular给人的感觉还是重,且使用复杂。

其实现在正在做另外一个项目,目前叫做Zeus——宙斯,感觉很快就要把希腊神话神名用光的节奏,哈哈。

去年曾花了一晚上撸了一个赠书的平台 http://book.hamioo.me/ ,在练技术的同时,希望能将我屯了许久的书散出去一部分,有道是买书如山倒,看时如抽丝啊,当然最重要的时能让女神看到我的品位,嘿嘿嘿。后来在学了React,webpack后,已经将前端部分重写了,用到了诸如React、webpack、es6、Redux等的时髦新技术,也算是学以致用。

去年还买了两个域名,一个 hamioo.me 还有一个 diao.li,然后买了一台阿里云服务器,折腾了一些小项目,但我的阿里云后来频繁被组里征用,感觉像是原本一个妙龄少女,最后不断被摧残成了枯枝败叶。

过去的这一年,有对技术的实践,也有主动对新技术的学习探索,总体来说还不错。但是暴露出的问题让我深感忧虑,那就是基础功的不够扎实,诸如算法,操作系统,浏览器内核的不了解,往后会愈来愈遏制我前进的脚步,例如之前看React的domdiff算法都理解起来颇为吃力,所以今年我想我在关注前端新技术的发展的同时,也应该慢慢补充自己的基本功。

去过一些地方

一年做一次长途旅行是我对自己的基本要求,很庆幸去年也做到了,去年国庆一个人去了四川重庆游玩,主要去地方就是成都和稻城亚丁,作为吃货,在成都吃小吃吃到撑,各种逛茶馆不亦乐乎,接着就是去稻城亚丁挑战极限,顶着高反爬山,只为看一眼美如珍珠的海子,在壮丽的风景中抛却烦恼,带着朝圣的心感受自然的力量。

image

image

image

image

image

不过遗憾的是,由于拖延癌最晚期,拍的许多图没有P完,该写的游记也没写完。看来曾经高涨的摄影热情依然衰减了。

然后由于吃的原因分别去了广州潮汕,印象最深刻当属广州的早茶和大排档了,真是一番享受,还有潮汕的牛肉丸牛肉火锅,想想都能流口水,只能期待今年有机会再去。

image

image

image

旅游不是目的,是方式。

看过一些书

《明朝那些事儿》有血有肉的人物,不再是冷冰冰的历史。书中最喜欢的人,于谦,王阳明,戚继光。此心光明,亦复何言。

《流血的仕途》第一帝国宰相李斯波澜壮阔的一生,有梦想的青年,有权某的政客,一只老鼠改变一个小吏的命运。吾欲与若复牵黄犬,俱出上蔡东门逐狡兔,岂可得乎!

《岛上书店》温馨的故事,我们每个人都是一座孤岛,爱是链接孤岛的美丽存在。

《镜花缘》儿时的噩梦啊。

《喻世明言》很多黄色小诗,只能说明朝人的生活还是丰富多彩啊。

《笑林广记》曾经不懂,现在看来活脱脱一本黄色笑话集,真污。

《黄金时代》天色微微向晚,天上飘着懒洋洋的云彩。下半截沉在黑暗里,上半截仍浮在阳光中。那一天我二十一岁,在我一生的黄金时代,我有好多奢望。我想爱,想吃,还想在一瞬间变成天上半明半暗的云。后来我才知道,生活就是个缓慢受锤的过程,人一天天老下去,奢望也一天天消失,最后变得像挨了锤的牛一样。可是我过二十一岁生日时没有预见到这一点。我觉得自己会永远生猛下去,什么也锤不了我。美丽的黄金时代啊。

《解忧杂货铺》治愈系小说。

《虚无的十字架》原罪的问责。

《平凡的世界》尊严与不熄的奋斗,最难以接受的是结局。

《webkit技术内幕》未看完。

《JavaScript性能优化:度量、监控与可视化》

《设计模式之禅》

其他

image

又获得了南方过冬的感受。

离开其实很简单,我的梦不在这里,我要去那个地方找回它。

认识了几个不错的肥佬。

请女神吃过几次饭。

代码,音乐。

参加了一届webrebuild大会,第一次做这种大型的对外分享,格外紧张。

Github提交800多次。

微信运动最多步数34330步。

去过海拔最高的地方4700米。

看了四十三部电影,最感动的是山河故人

开始思考房子,结婚的事了。

不同的年龄段看同一本书总是会有新的感悟,但矛盾的是,年轻时不懂,却有激情,年长之后懂了,却已激情不再。

十八岁和二十八岁时间的长度是不一样的。

有时候需要冲动,认定的事不要再犹豫,所有得羁绊都是自己设想出来的,自己才是最大得障碍。

需要跨界,不能仅局限于自己的圈子,需要跳出来看看别人在做什么。

爱好需要培养成习惯才不会荒废。

依然在追求爱情道路上,砥砺前行。

保持耐心,对代码,对技术,对生活,对人。

饮茶,吃鸡。

你若盛开,蝴蝶自来!你若精彩,天自安排!

将来的你,一定会感谢现在拼命的自己!

只要把专业做好了,你就能乘风破浪。

希望我能认真地说出我能成为现在的我,实在是太好了。

在遇到她以前我不怕死,不惧远行。也不曾忧虑悠长岁月,现在却从未如此真切地思虑起来。——《平如美棠》

不惋惜,不呼唤,我也不啼哭。金黄的落叶堆满心间,我已不再是青春少年。


轻松辨别优惠骗局

今天早上阳光明媚,我刚从睡梦中醒来,拿起手机瞄一眼,猛然间发现发现有姑娘的消息,这时候以垂死病中惊坐起来形容也不过,姑娘问我,某一个优惠消息是不是真的

image

此时的我内心是愉快的

image

我瞄了一眼,哎哟,好像还是京东的优惠信息,带着京东logo,看起来挺真实的,再定睛一看,我勒个去,这魔性的url是什么鬼哦,10.xinghai8.sinaapp.com。。。

image

来自某浪云计算的域名,我心中冷笑呵呵,这手段也太拙劣了,想要欺骗连个相似的域名也不买一个,这么说也要弄一个j-d.com来骗骗消费者啊。

于是我机智地告诉姑娘,相信我,这尼玛就是蒙人的,并解释了下原因。

image

但是,双11临近,这类骗局相比也会越来越多,万一善良的姑娘被骗,那多么令人难过的一件事,于是我想稍稍总结下识别这类骗局的小方法。

看页面是否精致

首先我们可以看页面做得是否精致,比如是否有设计感,页面是否看起来很模糊,是否显得很粗糙,一般伪造的页面看起来都有一种low的感觉,这是能识别出的。

但是现在骗子也都很聪明了,找一个正规公司的网站copy一下,看起来也是真真的,单凭这么观察,还是很难作为判断的依据。

其次要小心输入

如果一个页面要求你输入一些敏感信息,比如身份证号、银行卡号、手机号,甚至是密码的时候,需要千万小心,这90%是一个钓鱼的页面,因为正规的公司一般是不会让你输入这些敏感信息的。千万注意!

看url

这是看这个网页是否靠谱的最好方法,因为url是网页的唯一标识,如果url如果是假的,那这个网页就一定有问题。在微信中为了美观,这个网页的地址是被隐藏的,让用户忽视了页面的url,但是我们任然有办法可以看到url。

我们打开页面后,把页面往下拉一下,就能在顶部条下面看到有这么一行字网页由XXX提供

image

这里的wq.jd.com就是这个网站的域名,京东的域名一般是jd.com或是360buy.com,而这里的wq.jd.com是jd.com的二级域名,所以是属于京东的,那么这个网页就没有问题。一般伪造得像一点的域名有上面说到的j-d.com、jingdong.com等等,这都是坑爹,而sinaapp.com更是坑爹中的坑爹。

另外淘宝、天猫的域名是taobao.comtmall.com,唯品会的是vip.com

有些网站页面会把页面的滚动给禁用掉,这时你会发现下拉页面看到域名,这时候怎么办嘞?还是有办法的,可以选择,在浏览器中打开这个网页

image

然后点击上面的地址栏就可以看到完整的url,一般看最前面的域名就行

image

比如这里就是wq.jd.com。通过这种方式也能识别出来。遇到坑爹的网页,直接点举报,方便你我他。

终极办法

通过以上这些甄别方式,可以避免踩入大部分的坑,但是依然还有漏网之鱼,比如运营商或路由器被劫持,导致url对了,但是被重定向到钓鱼页面去了,等等以及其他。

这个时候,该怎么办呢?

接下来告诉你终极大招!

====================================这里是分割线===================================

====================================这里是分割线===================================

====================================这里是分割线===================================

====================================这里是分割线===================================

====================================这里是分割线===================================

====================================这里是分割线===================================

请姑娘直接找在下即可!

请姑娘直接找在下即可!

请姑娘直接找在下即可!

image


川渝行——前奏篇

image

距离上一次正儿八经写博客整整过去了10个月,这个数字说出来都感觉到羞耻。拖延症和懒癌已经深入骨髓,突破天际,我自己都觉得自己没救了。写作是一个自省的过程,同时也能分享自己的时光,正好这次国庆独自去川渝游玩了数日,所见所玩所吃都还尚可,挣扎了数日,总算可以开笔写点东西了。时光匆匆,你以为的忙碌,其实苍白了生活,苟且了岁月,怠慢了姑娘。

旅行是为了遇见更好的自己。

虽然这句话略显矫情,而且有点伪装文艺的感觉,而且像是在安利。但是一个人在旅途中会遇到和平时生活中截然不同的东西,陌生的人惬意的生活方式让味蕾爆炸的美食前所未见的美景,这些就是我对旅行的全部追求,当然还有人数适中的游客。生活,不只有眼前的苟且,还有诗和远方(高老师装X金句),诗我是读不懂,出去玩还是可以做到的。

四川是我一直想去游玩的神奇地方,这里有太多耳熟能详的风景名胜,像成都、峨眉、九寨,就不一一安利了。成都是我心中圣地,男人都向往的地方,天府之国声明在外,锦官城繁华如故,但最吸引我的是成都的美食,这里有各种我爱吃的东西,谁让川菜馆开遍中国,作为一个没有辣椒吃不下饭的量好少年,麻辣又好吃的食物怎能不吸引我。稻城亚丁,号称蓝色星球上的最后一片净土,香格里拉之魂,当然,这样的宣传语我一般是不太信的,只是蒙蒙人的,蓝色星球上的净土还有很多,大家不要被骗,但是稻城亚丁的风景是毋须质疑的。而且了解到稻城亚丁地处高原,知名度也没有九寨沟啊、峨眉山啊这些景点这么高,本着国庆不去看人山人海的高尚志向,于是就选择稻城和亚丁咯。

美食,美景,我管这叫一个人的朝圣

这次游玩我还是策划了比较久的(其实并没有),而为了更充分的准备,我还特地买了一本旅游指南,Lonely Planet系列的《四川和重庆》,不过我买完之后就发现又出了第二版,呵呵哒,几年都没更新,我一买就出新版。

image

吐槽归吐槽,书的质量还是不错的,内容详实,语言诙谐,而且里面规划了一些旅行路线都还靠谱,可以让你在旅行前可以对要去的地方的风土人情有一个大致的了解。我是基于这本书给的意见定的游玩计划,最后证明规划的还不赖,完美规避了传说中中秋——国庆人山人海,而且吃到了很多当地的美食。

推荐一下

image

展示一下手写的游玩计划

image

image

相信大家都看到了,手写计划只计划到了10月2日,为什么,难道之后的日子里有什么见不得人的秘密?嘿嘿,其实是之后的日子都没有计划,直到10月1日,我依然不知道我10月3日会在哪里,是在峨眉山,还是在四姑娘山,还是在九寨沟,还是在稻城亚丁,尽管我已经买好了10月8号回深圳的机票。四川吸引我的景点很多固然是一个原因,但更重要的是,人生就是需要拥抱变化!

image

游玩路线

那么,还是来看下我最终游玩的路线吧:

  • 9.29,吉安-成都
  • 9.30,上午大熊猫基地,下午天府广场,宽窄巷,人民公园,锦里一路吃喝玩乐,晚上,白夜酒吧喝酒
  • 10.1,上午广汉三星堆遗址博物馆,下午广汉名小吃,鹤鸣茶楼喝茶,晚上蜀中风月川剧演出
  • 10.2,上午睡懒觉,下午杜甫草堂,武侯祠,锦里,晚上和表姐一家吃火锅
  • 10.3,上午成都-稻城,休息,下午兴伊措游玩,晚上出现轻微高反,早早睡了
  • 10.4,稻城-亚丁,亚丁游玩一天
  • 10.5,亚丁游玩一天,晚上回稻城
  • 10.6,稻城-成都,坐了一天车
  • 10.7,上午休息,中午高铁到重庆,下午磁器口,洪崖洞游玩
  • 10.8,早晨飞回深圳,上班

准备和装备

考虑到可能会进入到高原地区,而又考虑到我平时不运动导致的孱弱的身躯,我机智地提前一周开始吃红景天胶囊,红景天的具体功能还是自行百度吧,我就不安利了,胶囊这种东西吸收缓慢,所以一定要提前吃才会有效果,据说还有一种口服液,吸收快,倒可以不提前吃。可能也是提前吃了高反药的原因,在稻城亚丁倒不怎么出现高反,不过还是要提醒一下,心脏病、高血压人群珍爱生命,远离高原。

虽然我不是专业的,不过这次带装备还挺多,当然主要的是衣服,出去玩这么多天,肯定得备上很多很多衣服啊什么的,因为出门在外洗衣晾衣总是很不方便;其次最重的就是已经很久没有用过的相机了(曾经充满摄影激情的我,哎)。为了装下这许多装备,我特意去买了一个58L的登山包(不是在骂人),装满之后背上,那感觉真是酸爽!不过一个包就把所有东西都装完了,出行带一个包就可以了,还是显得很方便,而且感觉充满了逼格,像一个独行的侠客,穿州过省,浪迹于蜀中!☺️

image

好一个登山包(并不是在骂人)

同时,为了保证安全,我还携带了一个防狼强光手电筒,手电筒最好还是备一个,晚上出门有点保障,有备无患啊,当然,晚上最好还是不要出门。

装备list

  • 58L登山包
  • Canon 70D相机,套头
  • 衣服若干
  • 洗漱套装
  • 防狼强光手电筒
  • Lonely Planet《四川和重庆》
  • 钱包,现金若干,银行卡,身份证
  • 手机,pad
  • 充电宝(很重要)
  • 药物,高反药,云南白药,感冒药等

抵达成都

image

月是故乡明,家乡中秋的月,手持拍摄,焦距短小,这是放大数倍的效果,还算能看。之前只发给女神看过😳,这次分享一下

9.29,在家过完中秋,经过21个小时火车的洗礼,终于在下午6点抵达成都,坐在成都的公交车上,似乎空气中都充满了火锅和毛血旺的味道,当天晚上我便迫不及待打车去到了天府广场,可惜并没有拍照片,因为我觉得一个长得并没有什么特色的广场有啥好拍的。

在广场上散了会儿步,欣赏了一下成都中心的夜色,就顺着东城根上街步行去了宽窄巷,虽然到宽窄巷已经晚上9点多了,但人还挺多,我没有多逛,找了个庭院深深的茶馆,叫了碗竹叶青,开始寻找传说中成都安逸的感觉,算是假期放松前的过渡。带着古意的庭院,月光透过桂树洒落斑驳的影子,外面有些喧闹,不过与自己无关,盖碗里茶叶浮沉,茶香溢散开来,旅途的疲惫,心中的躁意,都能在此刻被忘却(容我静静地装一下逼)。

总算是来到成都了,这也是我最想去的国内城市。

旅行就是从自己呆腻的地方去到一个别人呆腻的地方,这句话,我还是不能苟同的。

就先到这里吧。^_^


使用Github Pages和Hexo搭建属于自己的站点

一直以来都想开始自己写点博客,积累点东西,这个想法存在很久了,无奈拖延症实在逆天,直到2014年末了也迟迟未能实现。其实之前也有过好几个博客,有博客园的,有Lofter的,还有自己用WordPress搭的,但俱是无疾而终,域名都过期未续了,再也无法访问了,终究还是太懒。最近在探索全球最大的同性交友社区GitHub,于是乘机想再开一个新的博客,想用来写点字。博客还是要有的,万一想要写了呢~呵呵哒~

以下给大家共勉~

My goal for 2015 is to accomplish the goals of 2014 which I should have done in 2013 because I made a promise in 2012 and planned in 2011

image

Github Pages

image

为什么要用嘞

使用Github Pages来创建自己的博客的原因很简单:

  • 穷,买不起空间
  • 版本控制天然Github,灰常方便
  • 纯静态,没有繁琐的配置,作为前端屌丝表示很开心:-)

Github Pages介绍地址https://pages.github.com/,如果不想看英文,就继续往下看吧~~

Github Pages介绍

首先要知道Github提供了两种类型的站点,User or organization pagesProject pages。这两种主页的创建方式有很大的不同,在开始做之前我们必须要区分开来,否则会踩坑。

  • User or organization pages,或者就叫个人或组织主页吧,它依赖一个比较特殊的github repo来托管代码,首先,必须使用你自己的用户名来给这个repo命名,比如,我的github用户名叫luckyadam,那么这个repo的名字必须是luckyadam.github.io;再者这个项目创建好后将会以master分支的代码来构建站点,构建完后的站点名字是http(s)://<username>.github.io,所以如果master分支里面没有index.html的话会404哦。

  • Project pages,或者就叫项目主页吧,与个人主页不同的是,项目主页的代码将会和项目的代码存在于同一个repo中作为一个完整的项目,所以,你可以创建无数个项目主页,但个人主页却只能有一个。如果是个人账户,项目主页的url将会是http(s)://<username>.github.io/<projectname>,如果是组织账户那么地址是http(s)://<orgname>.github.io/<projectname>。项目主页将会以repo的gh-pages分支来构建站点。

讲了这么多枯燥的东西( ‘-ωก̀ )小伙伴肯定看得不开心了,那么让我们来开始动手尝试一下吧!

当然啦,首先你得有一个Github账号,如果你习惯用终端来使用git的话,别忘了手动生成一个ssh key来让github和你的电脑之间建立信任Generating SSH keys,好处你应该懂的,如果你是客户端用户,那就当我是在废话了。

来愉快构建一个个人主页吧!

首先创建一个repository

image

然后填写相关内容啦,记得名称必须是<username>.github.io

image

然后可以在Repo的设置里可以看到这样一个提示

image

好啦,这样就创建成功啦,让我们打开http://luckyadam.github.io/来看看吧,真是好期待的呢

image

image

What the f*ck!!! image

容老夫想一想,哦,对了!image

许是忘了加index.html了,且让我加上一个试上一试,首先将项目clone下来

1
2
$ git clone git@github.com:luckyadam/luckyadam.github.io.git
$ cd luckyadam.github.io

用vi新增一个index.html文件,内容自己看着办啦,然后push到master

1
2
3
$ git add .
$ git commit -m 'add index.html'
$ git push [origin] [master]

接着看看http://luckyadam.github.io/,哈哈,发现已经有了~

image

现在我们已经创建好一个个人主页啦!

Ps:首次build需要过大约10分钟后才能看到效果,并且无论有没有成功,github都会往你的邮箱中发送一封通知邮件哦

再来愉快地创建一个项目主页吧!

创建项目主页的步骤和创建个人主页有点类似,创建一个普通的Repo,比如我创建了一个叫blog的Repo,给这个Repo创建一个gh-pages分支,然后将静态页面push到这个分支就可以了,当然index.html是必须的。

使用git init初始化一个本地仓库,增加静态文件

1
2
#初始化一个本地仓库
$ git init

经所有改动addcommit,随后直接push到Repo的gh-pages分支

1
$ git push --force --quiet https://github.com/luckyadam/blog.git master:gh-pages

HEXO

简单说说

Github Pages默认是纯静态的方案,但我们总不能每次都手写一个html文件,然后发布吧,那得多蛋疼啊,所以我们会想,要是能有一个软件,我们只用关注写文章,然后这个软件会将我们的文章根据一个模板输出静态html文件,然后我们将它发布出去,这样多爽啊~事实上,市面上已经有了很多这种静态博客生成工具了,比如jekyll、hexo等。Github推荐使用的是jekyll,我使用的是hexo,为什么呢,因为任性,就是这样。

image

Hexo是一个非常优秀简洁的博客生成工具,它支持Markdown,并且有很多的主题和插件可供选择,可以很快地构建出一个看起来不错的博客。并且通过学习官方文档,构建属于自己的主题也非常简单,总而言之,就是用起来挺爽。

安装Hexo前的准备

  • Git 这个当然已经有了,要不然前面我们在忙活什么
  • NodeJs 前端狗说,node你都不用,你太low了

然后我们就可以安装hexo了

1
2
# mac用户可能需要加上sudo来执行
$ npm i -g hexo

我们可以在本地随意找个目录,初始化一个Hexo项目,并且不要忘了安装项目依赖

1
2
3
4
#初始化安装
$ hexo init
#安装依赖
$ npm i

这样在这个目录下会出现如下目录结构

.
├── _config.yml
├── package.json
├── scaffolds
|   ├── draft.md
|   ├── page.md
|   ├── post.md
|   └── photo.md
├── source
|   ├── _drafts
|   └── _posts
└── themes
  • _config.html 是一个YMAL文件,它是Hexo的配置文件,在这个配置文件中我们可以配置网站的标题,作者,网站主题等,具体的配置项,可以参见Hexo配置
  • scaffolds目录中存放着draft.mdpage.mdpost.mdphoto.md 四个模板文件用于生成不同类型的静态页面
  • source目录中存放着用户已经写好的文件,比如用户写的post类型的文件就存放在 source/_posts
  • themes中存放用到得主题

书写文章

我们可以按照以下步骤来新建一篇文章,并且在本地预览

1
2
3
4
5
6
7
# layout 是指文件的类型,如果是要写一篇博文,可以使用post,title是文件标题
# 执行完这个命令后会有提示:
# [info] File created at /Users/username/project/blog/source/_<layout>s/<title>.md
$ hexo new [layout] <title>
# 写完文章保存后,可以在本地预览
$ hexo server

主题

我觉得主题是大家比较关心的东西,毕竟谁都希望自己的博客能非常漂亮嘛~

我们可以在这里找到hexo的各种主题,这里有各种款式,各种姿势,总有一款能满足你!

选择完自己喜欢的主题后,我们可以通过git clone到自己的themes目录下,由于我不想添加git submodules,并且没有更新需求,我clone完后就把主题的.git删了,这样省事很多啊。下载完主题后在项目的_config.yml文件中将 theme 改成想要的主题名字就行了。

当然机智的你会发现在主题的目录下也有一个_config.yml的文件,这个文件是当前主题的配置文件,你可以按照自己的喜好随意调教,同时你也可以修改主题的其他文件,来打造成满足自己口味的风格。

生成静态文件

我们可以通过下面命令来生成静态文件

1
$ hexo generate

发布站点

做完以上配置和操作,并写(bie)出自己的第一篇博客后当然是要优雅地发布到自己的站点,让大家观摩观摩了,这也是非常easy的。

通过修改项目的config.yml文件中的deploy配置,可以轻松实现发布。

deploy:
  type: github
  repo: <repository url>
  branch: [branch]
  message: [message]

如果你创建的是个人主页,那么这里的配置中 branchmaster,如果是项目主页那么为gh-pages

随后通过执行下面一条命令就能直接发布了

1
$ hexo deploy

或者生成静态文件发布二合一通过下面任何一条命令

1
2
$ hexo generate --deploy
$ hexo deploy --generate

当然你也可以自己cd 进入public目录,然后将里面内容push到Repo的 gh-pages 分支上去,就是很蛋疼啦。

发布完后通过访问自己的Github Pages,就能看到效果啦,本博就是这样生成的。