本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2014年01月08日 统计字数: 5839字 阅读时间: 12分钟阅读 本文链接: https://soulteary.com/2014/01/08/rebuild-tips.html ----- # 前端重构相关细节贴士 一家之言,不一定正确,如果发现问题,请指出,多谢。 这篇不包含太多脚本,因为聊脚本我们需要上下文,需要篇幅。 ## 关于重构 重构其实分为两类,一类是彻底的换血,破坏后的重生,另一类是细节调整,慢慢迭代。 完全换血的重新设计相当消耗时间,因为所有的组件都需要重新书写,这里不仅仅包括那些被管理者看着不满的东西,还包括许多良好的组件,以及数据交换方式等,这里面隐患相当大,如果历史很久的项目去做完整重构,需要考虑到的事情就更多了,包括并不限于新的路由,存储,api,加载,跨域,异步,模版实现,但是好处就是接下来的开发都会基于一个新的标准,流程便捷,简单点说,除了看着爽外,写码也爽快。 细节调整,慢慢迭代,这种方式比较保守,一般会使用适配器的方式来实现,也就是我们常说的“兼容”,也符合“优雅降级”的理念。 这种实现如果同样的项目,相比较完全换血是比较快的处理,因为是分期,按部就班,所以每次调整都不会太大,甚至有可能使用灰度(AB)来进行发布,同时支持新旧规则。这种的好处是化解风险,尤其是时间成本。 不好的地方是,代码冗余,过度代码很多,需要额外设计一套过度时期的规则,而这套规则事后会被全部移除。 ## 时间点 完全重构: 在你项目膨胀到原有设计不足以展示你的内容的时候,也就是每次开发都需要完整的重新做一边的时候,以及新的技术变成了成熟技术,可以大幅提高性能,节约开销成本的时候,你们团队人多力量大的时候,或许你应该重构你的项目了。 细节调整: 仅仅内容增加,原来的展示方式需要随着改变的时候,新终端出现,需要适配的时候,新接入功能的时候。如同你电脑上的USB键盘或者外接显示器一样,为了你和用户的舒适,为了阐明你的产品的意图,当然,还有完成功能,纯粹吐混合的结构是无法给移动端大规模使用的。 ## 准备工作 技术调研: 了解你将会用到的技术和工具,了解这些技术的依赖以及支持程度,尤其是你项目中那些组件会使用到,使用到会有什么问题,替代方案是什么,代价是什么。 时间评估: 用自己对工具和语言还有和其他人协作的平均值来预估时间,以及最重要的,这个新的(重构||调整)的项目可以持续使用多久。 ## 执行注意 * 从现有积累中总结归纳出好的细节,在之后复用。 * 使用简单的外部组件,利于二次封装。 * 不同情况下的测试工具的使用,何时用debug 工具,何时用抓包工具,何时用假数据。 * 预留空间,写之前想清楚,这个东西以后会变成什么样子,或者可能是什么样子的,预留各种功能,诸如i18N,扩展功能,响应展示等。 * 编写语言选择,web是开放的,你可以选择很多你熟悉的语言去使用,但是请尽可能是使用和团队大多数人一样的语言,为了后期的维护和协同。 * 配置自己顺手的开发环境,我这里有一个不好的体验,实现是使用php后台执行git push(环境因素,这样会频繁出现解决冲突,阻碍开发,并且git commit会刷屏,不妥),但是其实应该使用git 的post-receive hooks,如我之前所写过的,这样可以快速预览你的修改,当然还有更快的方式,使用假数据和纯静态模版,前提你有时间抽象一套简单的系统出来用(值得,相信我)。 * 使用版本控制工具,你gg思密达的时候会删除错东西,或者你会想恢复猴年马月写的一段功能,还在用打rar压缩包的方式备份文件嘛,低效且不好找,所以投入版本控制软件的怀抱吧。 * 不要盲目使用最新的技术,没有那个必要,成熟稳定,实现难易,这些才是应该优先考虑的,实现结果比实现过程重要。 * 编码问题,请一致化编码,外部接口请尽量转换编码,减少不必要的问题,这里包括html meta,css&&js charset,文件存储编码等。 * 资源合并,动态编译。把这两个东西放一起是因为他们都是该一起做的,按照一定策略去打包合并你的文件,减少HTTP请求数是有效的提高响应速度的方法,并且压缩率也会相应提高。关于动态编译,这个也可以在打包的时候做,比如你用coffee或者less等... * 资源存储,尽可能少的使用cookies,把它留给http only的cookies,因为每次请求都会带着一坨cookies会让服务器消化不良(玩笑,如果后端实现有问题,值会覆盖,以及传输数据量不必要消耗),我们可以使用其他的方式来保存数据,localstorage,userdata,sessionstorage,indexdb,websql,服务端存储等。 * css正确使用,我常常看到有人会这样使用css: ```css #li.soulteary{ display: block; float: left; } ``` 或者这样使用: ```css #span.soulteary{ width:100%; height: 100%; line-height: 1; } ``` 错误的属性和多余的属性对于后来的人会造成迷惑,(前辈是不是使用了什么牛逼闪闪的兼容hacks?为啥这么写,搜索不到啊。)而且浪费篇幅! * 网站页面一致性,我们尽可能保证页面在所有的终端展示一致,但是没有必要去扣非特别细节的像素级别的差异,2080法则,这个应该是1/99法则了吧...吃力不讨好,优化真正该优化的,好吗? * 字体选择,如果你的网站是双语或者印欧语系的,用web font吧,如果你要照顾中文,好吧,我们使用部分图片,部分svg,其他的我们尽量保持一致好看就成,因为中文全字库做web font或者svg太沉重了,我们的网络还不足以承受。 * 流体栅格布局,这里我建议不要使用一种纯粹的布局,可以适当的混合,比如固定宽度内的流体栅格,固定宽度可以随着媒体的视区宽度来变化,这样里面的内容就可以自适应了,如果你全部都流体布局,那么希望耶稣爱你。额外说以下,定宽布局的话,有几种固定的比例,以前的940,960,980或许有点过时(参考之前说的话,过时贴近你的项目一样搞起),现在普遍1000+。 * 关于居中,你可以用绝对定位,边距,inline元素排版特性,古老的center标签,区域堆叠等来实现,当然,如果你的观众都很潮,我们用flex-box吧。 * 背景图,H5新特性是多重背景图,但是别忘记可以使用sprite,不过这里在ie6有bug(背景图会一直抓取,这里可以利用做一些其他的事情的,呵呵呵),机智的童鞋应该记得解决方案吧: ```html 另外,现在retina普及度很高了,可以使用-webkit-image-set来提高高富帅/白富美们的体验。 ``` * 关于图片,需要高保真,请使用png,较低要求且纯色无透明jpg,特别简单或者需要动画gif,这里细节,比如sprite,缩放,下载支持Progress等需要展开说吧,尤其是background-size这些,嗯,有兴趣可以看一下之前总结的caniuse的css 支持度的相关属性,反正杯具。 * 边框实现,曾几何时我们都用图片,后来使用上中下布局组件,头部和尾部的组件又可以分成左中右实现转角的特别设计。现在的话,如果没有特别要求的圆角,可以考虑使用border-radius,那种圆圆的头像,一个50%数值上去,就实现了,多便捷,当然,需要不脑残浏览器支持。否则还是老实的使用图片mask+z-index来搞吧。 * CSS选择器,这里有版本差别,2.1和3.0,他们有的一部分在早先的浏览器就被实现,但是有一些又存在细节差异,可以看之前总结的caniuse的图...额外说一下性能和维护的取舍。css的选取, 规则越简单越快,所以下面的写法不太好(渲染效率),但是维护却简单(less管理): ```css body.front-page #main ul.data-list li.active a{ color:#000; } ``` * 这里说一下js的选择器,相对css而言,越具体越快速(相比较类似状况,一层一层进filter,筛选更小的范围内的合格的家伙们,见代码),比如jquery实现: ```html ``` 这个效果在你绑定一堆事件之后,以及层级特别多,同类元素特别多的状况下,会越来越突出,选择所有,还是一点一点缩小口袋的范围去捕捉呢?显而易见。 * 一定要维护全部100%的jquery/mootools/...代码吗,不见得,某些地方原生就不错,比如你的iframe中想定时器去做token授权或者验证(sso),原生性能更好,加载更少。或者大循环中,对于动态生成的新元素,可以考虑使用document.getElement(s)ByXXXX来搞定,然后具体的可以使用类库方法等。 * HTML属性的改变,有了jquery后,我们基本都是attr,data,prop,但是如果你就是固定的一个元素的话,不妨使用下面的操作来代替类库,比如setAttribute,classList,style等等。 * 事件的数量,我们知道事件是基于句柄的,而这些都存在一个数组中,而且是独立快照的,参考我们的注册和解除事件。你说jquery可以直接off/unbind掉所有的,好吧,你有看到jquery全局对象的jQuery.cache嘛,所以呢,我要说的就是,尽量使用委托事,减少可能产生的句柄存储,出来混一直绑定事件不卸载,迟早要还。 * 关于冒泡,为了考虑一致性,或者对事件委托容器内的容器特殊处理,e.stopPropagation();比返回空优雅。 * 关于默认行为,你创建了一个表单,明明什么都没填写,但是点击了一个按钮,你发现页面刷新了(空提交);你写了一个链接,但是没有写内容,你点了一下,你发现,你的页面地址变了。如果我们不喜欢他们的默认行为,请在函数绑定入口做取消默认行为动作:e.preventDefault(); * css的class的使用,class除了做有实际规则的页面渲染依据之外,还可以作为变量或者临时状态量用于程序判断,比如文档的body添加一个loading的class,点击按钮,先判断body是否有这个class,有的话如何,没有的话如何,或者操作需要一步一步的来,就可以加过程class,这里命名可以考虑语义化,以及组件化两种,比如active,opened,或者js-opened。 * 关于边距,曾经有一种说法叫做,你的页面好小气/不大气,现在我们会说,页面是不是会引起聚集恐惧症,一般来说,你需要给你的页面的元素们一些边距。如果考虑一致性,可以设定padding-bottom统一数值,来减少维护开销。另外关于外边距合并,请不要总是设置绝对定位啊,浮动啊神马的,请活用内边距,他俩不搞基。 * 响应式设计,机智的你一定使用过media queries,如果你要说兼容性,那么好吧,对于IE6-8的老盆友,我们使用Respond.js来做额外支持。如果你的网站希望多种viewport的设备访问,又不想缩放,想进行细节定制,比如展示那些,不展示那些,横向排开,还是纵向一列,用这个家伙准没错,代码改动量极少。但是也有一些需要斟酌的,比如广告,或者个别功能文件较少,如果尺寸过宽,看起来很空洞。 * 或许应该提一下雅虎军规,不过我觉得大家都烂熟了吧,常规我们接触到的,有free cookies,减少重复代码(按需加载),压缩数据(文档,图),前端模版(rest 化,数据分离加减少交换),合并数据(提高压缩,减少http请求),延迟加载,预加载(sprite,组件模版预加载),以及chrome等浏览器支持的dns预加载。 _睡觉前补充一点东西:_ * 关于动画,如果是js实现,频繁触发的话,记得使用setTimeout进行函数节流。js改变元素的盒子属性(宽高)速度占优,css 动画移动元素位置效率占优势。 * 数据分离相关参考另外一篇日志,js编码细节贴士。 * 关于浮动,建议使用bootstrap v3的解决方案: ```less .clearfix() { & : before, & : after { content: " "; display: table; } & : after { clear: both; } } //for ie6&&ie7 .clearfix { * zoom: 1;} ``` 这个方式的好处是before生成content-box,放置内部无内容高度塌陷,after清除浮动,相比之前的hidden流,或者br流,或者额外元素添加clear,或者overflow流,和谐。 34.如果你还在纠结IE的css兼容问题,那么很大概率你是卡在了haslayout问题上,这货有一些属性可以触发,比如: ```css .obj{ display: inline-block; width: 100px; /* expect auto */ height: 100px; /* expect auto */ position: absolute; /* expect static */ float:left; /* expect none */ zoom:1; /* expect normal||none, ONLY IE */ writing-mode: tb-rl;/* ONLY IE */ } .obj-ie7{ min-height: 100px; /* expect none */ max-height: 100px; /* expect none */ min-width: 100px; /* expect none */ max-width: 100px; /* expect none */ overflow: hidden; /* expect visible */ overflow-x: hidden; /* expect visible */ overflow-y: hidden; /* expect visible */ position: fixed; /* expect static */ } ``` 如果你要问怎么判断是不是hasLayout的问题,好吧,你的元素是不是在页面中丢了?或者大小不合适,跑偏了呢? * 使用靠谱的框架会节约你很多精力,根据项目的起步程度选择不同的框架,比如刚起步,需要快速成型,要富插件的,稳定成熟的项目,需要严谨可定制的框架。 * 最后就是即使你的网站全js化了,小到数据分离,大到前端模版,也不要忘记给老的浏览器(适可而止啊,IE6滚粗)留活路。 * 还记得当年大明湖畔的z-index吗,它的使用场景是动态定位中,所以static用z-index是在刷存在感嘛,层叠原则嘛,当然是先拼爹(父级),后拼自己(同级)。 _感谢 @吕毅, @世江, @利仁 在方向上的指导(orderby time)。_