xml地图|网站地图|网站标签 [设为首页] [加入收藏]
Web重构之道,你一定是闲得蛋疼才重构的吧
分类:web前端

Web重构之道

2015/10/25 · 基础技术 · 重构

原文出处: 大漠   

说说大家都熟悉的网页动画技术

2015/12/07 · CSS, HTML5, JavaScript · 1 评论 · 动画

原文出处: 大搜车前端团队博客   

你一定是闲得蛋疼才重构的吧

2018/07/25 · 基础技术 · 重构

原文出处: 奇舞团 - hxl   

随着“发布”进度条走到100%,重构的代码终于上线了。我露出了老母亲般的围笑……

最近看了一篇文章,叫《史上最烂的开发项目长啥样:苦撑12年,600多万行代码》,讲的是法国的一个软件项目,因为各种奇葩的原因,导致代码质量惨不忍睹,项目多年无法交付,最终还有公司领导入狱。里面有一些细节让人哭笑不得:一个右键响应事件需要花45分钟;读取700MB的数据,需要花7天时间。足见这个软件的性能有多糟心。

如果让笔者来接手这“坨”代码,内心早就飘过无数个敏感词。其实,笔者自己也维护着一套陈酿了将近7年的代码,随着后辈的添油加醋……哦不,添砖加瓦,功能逻辑日益复杂,代码也变得臃肿,维护起来步履维艰,性能也不尽如人意。终于有一天,我听见了内心的魔鬼在呼唤:“重构吧~~”

重构是一件磨人的事情,轻易使不得。好在兄弟们齐心协力,各方资源也配合到位。我们小步迭代了大半年,最后一鼓作气,终于完成了。今天跟大家分享一下这次重构的经验和收益。

前言

Web重构之道是今年十月份参加上海Qcon全球软件开发大会的新时代的前端专题的一次分享的主题。这次有幸能跟@达峰、@sofish、@桂川等大神一起同台分享,感到非常的荣幸,也感到无比的压力。还好分享已结束,借此机会重新回忆这次大会上自己分享的主题。在此之前我首先要感谢@贺佬给我上台分享的机会,感谢@winter大大的推荐、建议与鼓励。最后感谢Qcon提供这样的分享平台。

前言

从古代手绘翻书动画,到胶片电影,再到多张静态图合成 gif,
这些都离不开一个术语叫

也就是我们需要绘制每一帧,然后控制一下帧与帧之间的时间间隔。

然而相邻两帧之间的变化并不大,重复绘制浪费体力,
幸亏计算机代码可以复制粘贴,然后修改一下变动的地方就可以了。

等等,好像哪里不对。

计算机代码除了可以复制粘贴,还有抽象能力。
我们可以把需要复制粘贴的代码交给计算机来重复执行。
把需要变动的地方,交给计算机来运算。

而网页中具备运算能力的只有 JS,其他的就只能是定义一下参数,剩下的就交给浏览器了。

这就是 JS 算编程,而 HTML、css 不算编程的原因。
相关讨论,回复内容+关键字#你丫才码农#

挑战

此次重构的对象是一个大型单页应用。它实现了云端文件管理功能,共有10个路由页面,涉及文件上传、音视频播放、图片预览、套餐购买等几十个功能。前端使用QWrap、jQuery、RequireJS搭建,HTML使用PHP模板引擎Smarty编写。

我们选择了Vue.js、vue-router、vuex来改造代码,用webpack完成模块打包的工作。仿佛一下子从原始社会迈向了新世纪,是不是很完美?

图片 1

(图片来自网络)

由于项目比较庞大,为了快速迭代,重构的过渡期允许新旧代码并存,开发完一部分就测试上线一部分,直到最终完全替代旧代码。

然鹅,我们很快就意识到一个问题:重构部分跟新增需求无法保证一致。比如重构到一半,线上功能变了……产品不会等重构完再往前发展。难不成要在新老代码中并行迭代相同的需求?

别慌,一定能想出更高效的解决办法。稍微分析一下,发现我们要处理三种情况:

1. 产品需要新增一个功能。比如一个活动弹窗或路由页面。

解决方法:新功能用vue组件实现,然后手动加载到页面。比如:

JavaScript

const wrap = document.createElement('div') document.body.appendChild(wrap) new Vue({ el: wrap, template: '<App />', components: { App } })

1
2
3
4
5
6
7
const wrap = document.createElement('div')
document.body.appendChild(wrap)
new Vue({
  el: wrap,
  template: '<App />',
  components: { App }
})

如果这个组件必须跟老代码交互,就将组件暴露给全局变量,然后由老代码调用全局变量的方法。比如:

JavaScript

// someApp.js window.someApp = new Vue({ ... methods: { funcA() { // do somthing } } })

1
2
3
4
5
6
7
8
9
// someApp.js
window.someApp = new Vue({
  ...
  methods: {
    funcA() {
      // do somthing
    }
  }
})

JavaScript

// 老代码.js ... window.someApp.funcA()

1
2
3
// 老代码.js
...
window.someApp.funcA()

注意:全局变量名需要人工协调,避免命名冲突。PS:这是过渡期的妥协,不是最终状态

新增一个路由页面时更棘手。聪明的读者一定会想到让新增的路由页面独立于已有的单页应用,单独分配一个URL,这样代码会更干净。

假如新增的路由页面需要实现十几个功能,而这些功能已经存在于旧代码中呢?权衡了需求的紧急性和对代码整洁度的追求,我们再次妥协(PS:这也是过渡期,不是最终状态)。大家不要轻易模仿,如果条件允许,还是新起一个页面吧,心情会舒畅很多哦。

2. 产品需要修改老代码里的独立组件。

解决方法:如果这个组件不是特别复杂,我们会以“夹带私货”的方式重构上线,这样还能顺便让测试童鞋帮忙验一下重构后有没有bug。具体实现参考第一种情况。

3. 产品需要修改整站的公共部分。

我们的网站包含好几个页面,此次重构的单页应用只是其中之一。它们共用了顶部导航栏。在这些页面模板中通过Smarty的include语法加载:

JavaScript

{%include file="topPanel.inc"%}

1
{%include file="topPanel.inc"%}

产品在一次界面改版中提出要给导航栏加上一些功能的快捷入口,比如导入文件,购买套餐等。而这些功能在单页应用中已经用vue实现了。所以还得将导航栏实现为vue组件。

为了更快渲染导航栏,需要保留它原有的标签,而不是在JS里以组件的形式渲染。所以需要用到特殊手段:

  • 在topPanel.inc里写上自定义标签,对应到vue组件,比如下面代码里的``。当JS未加载时,会立即渲染导航栏的常规标签以及自定义标签。

<div id="topPanelMountee"> <div id="topPanel"> <div>一些页面直出的内容</div> ... <import-button> <button class="btn-import"> 导入 </button> </import-button> ... </div> </div>

1
2
3
4
5
6
7
8
9
10
11
12
<div id="topPanelMountee">
  <div id="topPanel">
      <div>一些页面直出的内容</div>
      ...
      <import-button>
        <button class="btn-import">
          导入
        </button>
      </import-button>
      ...
  </div>
</div>
  • 导航栏组件:topPanel.js,它包含了ImportButton等子组件(对应上面的<import-button>)。等JS加载后,ImportButton组件就会挂载到<import-button>上并为这个按钮绑定行为。另外,注意下面代码中的template并不是<App />,而是一个ID选择器,这样topPanel组件就会以#topPanelMountee里的内容作为模板挂载到#topPanelMountee元素中,是不是很机智~

JavaScript

// topPanel.js new Vue({ el: '#topPanelMountee', template: '#topPanelMountee', components: { ... ImportButton } })

1
2
3
4
5
6
7
8
9
// topPanel.js
new Vue({
  el: '#topPanelMountee',
  template: '#topPanelMountee',
  components: {
    ...
    ImportButton
  }
})

彻底重构后,我们还做了进一步的性能优化。

分享感觉

虽然技不如人,而且懂得知识也少,干货不多,但我是一位爱于分享的人。也是第一次到QCon这种高大上的会议上分享,加上@sofish、@达峰、@桂川、@王沛和@佳辰几位嘉宾分享的主题都是一些高大上的话题,让我感觉压力很大。加上自己国语不标准(被@点头猪称为鞋城国语“最好”一位),生怕把@贺佬的场子给砸了。不过好在一点,以前也经历了一些“场子”,学会一些自黑,不会怯场,最主要的还是自己的脸皮厚如城墙(毕竟是人老了,皮糙肉厚)。

开始

网页动画可以通过以下几种方式实现(gif、flash 除外),

作者知识面有限,如有遗漏,请留言通知我。
相关讨论,回复内容+关键字#网页动画实现方式#

  • css3 动画
  • SVG 动画
  • JS 动画(包括 css、SVG 的属性修改实现的动画)

作者认为 canvas、webGL 只能算是一种绘图方式。
他们的动画也都是通过 JS 修改参数来实现的。
相关讨论,回复内容+关键字#canvas动画#

最早 JS 通过 setTimeout() 或者 setInterval() 方法设置一个时间,
来控制帧与帧之间的时间间隔。

  • setTimeout() 直接用跳出来终止下一帧。
  • setInterval() 使用 clearInterval() 来取消周期执行。

但是这样效果可能不够流畅,且会占用额外的资源。
相关讨论,回复内容+关键字#你ST设置几毫秒#
参考:

后来,有了一个requestAnimationFrame(),让浏览器决定最优帧速率选择绘制下一帧的最佳时机
requestAnimationFrame()cancelAnimationFrame() 来结束。

所以我们来改变一下思维方式,既然帧与帧之间的时间间隔不用考虑了,那就关心一下变化速率吧。

  • Partial support refers to lacking cancelAnimationFrame support.
  • Supports webkitCancelRequestAnimationFrame rather than `webkitCancelAnimationFrame.

— caniuse.com

好了,动画讲完了,你去找个教程看《canvas 绘图》?

别介,这才刚刚开始。

慢慢的,我们发现一些简单动画只是在修改几个 css 属性,而且只是在两三个状态之间来回变换。
大量的体力却浪费在两个状态间的补间状态函数上,而且质量良莠不齐。

来来来,这种事情就交给浏览器嘛。

进一步优化

分享的主题

图片 2

这次分享的主题是“Web重构之道”。重构在当今的Web时代是一个含“金”量最低的职位,而重构之道是具有历史的一个话题,做为所谓的“Web重构工程师”(其实就是一“页面仔”)我想借这样的机会分享自己对重构是怎么样的理解。选择“重构”并不证明我是有多爱重构,其实我也很恨它,想做别的,只不过是自己学有所限,做别的无法做,也做不了。那么既然无法做别的,只能努力好做。不是自古有人说,“三百六十行,行行有状元”。虽称不上状元,但我想通过分享告诉正在做重构的从业人员能重新思考与定位重构。或者说自己的职业规划。

这个话题分享我主要分为了三个部分:

  • 我是怎么理解曾经的重构(前世
  • 我是怎么看现在的重构(今生)
  • 将来的重构又是什么(未来)

简单点说就是:以前的重构,或者以前做Web网页的人,对重构是怎样一个看法?然后今生,你们玩高大上的时候还有多少人在苦逼的切图?然后未来,像我这样的伪前端,将来要怎样生存,或者有怎样的思考?。

css3 动画

能够执行补间状态的条件是,属性值能够转换成数值,这样就能参与运算。如:

  • 颜色(color,background-color,border-color…)
  • 长度/大小(width,height,font-size,border-width,border-radius…)
  • 透明度(opacity)
  • 堆叠顺序(z-index)你吖补间它有毛用

而不能参与运算就意味着不能拿来补间状态,也就是没有中间状态,如:

  • position(absolute、fixed、relative…)
  • background-image(一个确定的 url)

一拍脑门就能想到,创建一个补间动画的条件有:

  • 开始状态
  • 结束状态
  • 执行时间
  • 补间效果

假如有个方块,宽度从 10px 变成 100px。

开始状态呢,在原 css 里就可以定义了 width: 10px

结束状态呢,我们可以通过用 JS 直接修改 width 值,或者增加一个 class 选择器的方式,
或者是 :hover 等其他表示状态的伪类,让 width: 100px

而这时,你需要一个补间动画属性来声明 执行时间补间效果
它就是 transition,中文译作 过渡,就是我所说的补间的意思。

transition 为以下属性的简写

  • transition-property 规定哪个属性应用过渡
  • transition-duration 执行时间
  • transition-timing-function 补间效果,默认为 ease
  • transition-delay 延迟多少时间开始

参考:

Support listed is for transition properties as well as the transitionend event. The prefixed name in WebKit browsers is webkitTransitionEnd

— caniuse.com

css3 还提供了一个 animation 属性来创建更丰富的自定义动画,而减少 JS 的介入。

比如:

  • 你想一个动画中拥有多个状态
  • 每个状态修改的属性值较多
  • 循环播放
  • 逆向播放
  • 可自动开始,可中途暂停

animation@keyframes 配合使用。

@keyframes 用来定义动画,animation 则可以多处应用,他们通过一个 name 来连接彼此,
因此 @keyframes 必须要起个名字,而 animation 则有个 animation-name

animation 在应用时,你可以自定义它:

  • animation-duration 执行时间
  • animation-time-function 补间效果,默认是 ease
  • animation-delay 延迟多少时间开始
  • animation-iteration-count 循环播放次数
  • animation-direction 是否在下一周期逆向播放
  • animation-play-state 动画是否暂停,通过它,可以实现是否自动播放。要中途暂停的话,就要修改值,通过伪类或 JS 实现
  • animation-fill-mode 这个属性倒是有点出乎意料之外,请自行研究使用场景

可见 w3c 规范制定者们考虑到我们要用起来简单呢,基本上和我们思维方式一致。

实现动画的多个状态是在 @keyframes 定义时完成的。

采用 0%~100% 的分割方式,我们就不用在 执行时间 之外考虑时间问题了。

参考:

Partial support in Android browser refers to buggy behavior in different scenarios.

–caniuse.com

1. HTML瘦身

在采用组件化开发之前,HTML中预置了许多标签元素,比如:

JavaScript

<button data-cn="del" class="del">删除</button> <button data-cn="rename" class="rename">重命名</button> ...

1
2
3
<button data-cn="del" class="del">删除</button>
<button data-cn="rename" class="rename">重命名</button>
...

当状态改变时,通过JS操作DOM来控制预置标签的内容或显示隐藏状态。这种做法不仅让HTML很臃肿,JS跟DOM的紧耦合也让人头大。改成组件化开发后,将这些元素统统删掉。

之前还使用了很多全局变量存放服务端输出的数据。比如:

<script> var SYS_CONF = { userName: {%$userInfo.name%} ... } </script>

1
2
3
4
5
6
<script>
    var SYS_CONF = {
        userName: {%$userInfo.name%}
        ...
    }
</script>

随着时间的推移,这些全局变量越来越多,管理起来很费劲。还有一些已经废弃的变量,对HTML的体积做出了“贡献”。所以重构时只保留了必需的变量。更多数据则在运行时加载。

另外,在没有模板字面量的年代,HTML里大量使用了script标签存放运行时所需的模板元素。比如:

<script type="text/template" id="sharePanel"> <div class="share"> ... </div> </script>

1
2
3
4
5
<script type="text/template" id="sharePanel">
    <div class="share">
        ...
    </div>
</script>

虽然上线时会把这些标签内的字符串提取成JS变量,以减小HTML的体积,但在开发时,这些script标签会增加代码阅读的难度,因为要不停地切换HTML和JS目录查找。所以重构后删掉了大量的<script>标签,使用vue的<template>以及ES6的模板字面量来管理模板字符串。

本文由澳门新葡亰手机版发布于web前端,转载请注明出处:Web重构之道,你一定是闲得蛋疼才重构的吧

上一篇:图像旋转与翻转姿势解锁,企业开发 下一篇:没有了
猜你喜欢
热门排行
精彩图文