百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

你一定是闲得没事才重构的吧(你要是闲的没事)

sinye56 2024-10-24 16:49 39 浏览 0 评论

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

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

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

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

挑战

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

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

(图片来自网络)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 在topPanel.inc里写上自定义标签,对应到vue组件,比如下面代码里的<import-button>。当JS未加载时,会立即渲染导航栏的常规标签以及自定义标签。
<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元素中,是不是很机智~
// topPanel.js
new Vue({
 el: '#topPanelMountee',
 template: '#topPanelMountee',
 components: {
 ...
 ImportButton
 }
})

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

进一步优化

1. HTML瘦身

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

<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>

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

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

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

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

2. 渐进渲染

首屏想要更快渲染,还要确保文档加载的CSS和JS尽量少,因为它们会阻塞文档加载。所以我们尽可能延迟加载非关键组件。比如:

  • 延迟非默认路由

单页应用有很多路由组件。所以除了默认跳转的路由组件,将非默认路由组件打包成单独的chunk。使用import()的方式动态加载。只有命中该路由时,才加载组件。比如:

const AsyncComp = () => import(/* webpackChunkName: "AsyncCompName" */ 'AsyncComp.vue')
const routes = [{
 path: '/some/path',
 meta: {
 type: 'sharelink',
 isValid: true,
 listKey: 'sharelink'
 },
 component: AsyncComp
}]
...
  • 延迟不重要的展示型组件

这些组件其实可以延迟到主要内容渲染完毕再加载。将这些组件单独打包为一个chunk。比如:

import(/* webpackChunkName: "lazy_load" */ 'a.js')
import(/* webpackChunkName: "lazy_load" */ 'b.js')
  • 延迟低频的功能

如果某些功能属于低频操作,或者不是所有用户都需要。则可以选择延迟到需要的时候再加载。比如:

async handler () {
 await const {someFunc} = import('someFuncModule')
 someFunc()
}

3. 优化图片

虽然代码做了很多优化,但是动辄几十到几百KB的图片瞬间碾压了辛苦重构带来的提升。所以图片的优化也是至关重要滴~

1. PNG改成SVG

由于项目曾经支持IE6-8,大量使用了PNG,JPEG等格式的图片。随着历史的车轮滚滚向前,IE6-8的用户占比已经大大降低,我们在去年放弃了对IE8-的支持。这样一来就能采取更优的解决方案啦~

我们的页面上有各种大小的图标和不同种类的占位图。原先使用位图并不能很好的适配retina显示器。现在改成SVG,只需要一套图片即可。相比PNG,SVG有以下优点:

  1. 压缩后体积小
  2. 无限缩放,不失真
  3. retina显示器上清晰

2. 进一步“压榨”SVG

虽然换成SVG,但是还远远不够,使用webpack的loader可以有效地压缩SVG体积。

  • 用svgo-loader去除无用属性

SVG本身既是文本也是图片。设计师提供的SVG大多有冗余的内容,比如一些无用的defs,title等,删除后并不会降低图片质量,还能减小图片体积。

我们使用svgo-loader对SVG做了一些优化,比如去掉无用属性,去掉空格换行等。这里就不细数它能提供的优化项目。大家可以对照svgo-loader的选项配置。

  • 用svg-sprite-loader合并多个SVG

另外,SVG有多种用法,比如:img,background,inline,inline + <use>。如果某些图反复出现并且对页面渲染很关键,可以使用svg-sprite-loader将多个图合并成一个大的SVG,避免逐个发起图片请求。然后使用内联或者JS加载的方式将这个SVG引入页面,然后在需要的地方使用SVG的<use>标签引用该图标。合并后的大SVG如下图:

使用时:

<svg>
 <use xlink:href="#icon-add"></use>
</svg>

即可在使用的位置展现该图标。

以上是一些优化手段,下面给大家分享一下重构后的收益。

重构的收益

以下是重构带来的收益:

收益项重构前重构后组件化无100%模块化50%100%规范化无ESLint 代码规范检查语法ES5ES6+首屏有效渲染时间1.59s1.28s(提升19%)首次交互时间2.56s1.54s(提升39%)

  • 组件化:从0到100%
  • 老代码没有组件的概念,都是指令式的编程模式以及对DOM的直接操作。重构后,改为组件化以后,可以充分利用组件的高复用性,以及虚拟DOM的性能优化,带来更愉悦的开发体验。
  • 模块化:从50%到100%
  • 老代码中也用RequireJS做了一定程度的模块化,但是仅限于业务模块,没有解决第三方依赖的安装和升级问题。重构后,借助webpack和npm,只需要npm install安装第三方依赖,然后用import的方式加载。极大地提高了开发效率。
  • 规范化:从0到1
  • 老代码几乎没有代码规范,甚至连同一份文件里都有不同的代码缩进,强迫症根本无法忍受。重构后,使用ESLint对代码格式进行了统一,代码看起来更加赏心悦目。
  • ES6+语法:从0到大量使用
  • 老代码所使用的库因为历史悠久,加上没有引入转译流程,只能使用ES5语法。重构后,能够尽情使用箭头函数、解构、async/await等语言新特性来简化代码,从而提升开发体验。
  • 性能提升
  • 根据上线前后Lighthouse的性能检测数据,首次有效渲染时间(First Meaningful Paint,FMP)提升 19% 。该指标表示用户看到有用信息的时间(比如文件列表)。首次交互(First Interactive,FI)提升 39%。该指标表示用户可以开始跟网页进行交互的时间 。

以上就是这次重构的总结。不要容忍代码里的坏味道,更不要容忍低效的开发模式。及时发现,勇敢改进吧~

希望本文能帮助到您!

点赞+转发,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓-_-)

关注 {我},享受文章首发体验!

每周重点攻克一个前端技术难点。更多精彩前端内容私信 我 回复“教程”!

本文链接:https://75team.com/post/yunpan_refactoring

作者:hx1

相关推荐

6个接私活的网站,你有技术就有钱

如果觉得有帮助,还请大家帮忙多多转发,点个关注作者:发哥链接:GitHubDaily本篇文章会向大家推荐国内外几个接外包比较靠谱的平台,主旨是贵精不贵多。因此,像「猪x戒」这种会让程序员自贬身价,扰乱...

Java开源可商用的CMS建站系统_java建站源码

Java研发的CMS内容管理系统具有许多优势和特点,包括以下几个方面:跨平台性:Java是一种跨平台的编程语言,可以在不同的操作系统上运行,包括Windows、Linux、Mac等。这意味着Java...

SEO新手建站必看&quot;干货&quot;优质空间和功能选择技巧!

一.空间的分类服务器:远程的高级大型计算机。vps:虚拟服务器。虚拟空间:也称虚拟主机云主机:是在一组集群主机上虚拟出多个类似独立主机的部分,集群中每个主机上都有云主机的一个镜像,从而大大提高了虚拟主...

千字长文教你使用 宝塔面板 快速搭建网站

本文将教大家使用宝塔面板快速搭建网站,云服务器购买以及域名注册部分请自行上网搜索了解,亦可留言联系小编进行咨询。如果是和下方一样本地搭建演示的话,则不需要付费购买域名和主机。宝塔面板的是...

BlueHost香港虚拟主机建站的5个优点

应该是从2006年左右开始,如果我们建站选择国内的主机需要备案手续,而且比较繁琐,且根据各地的不同政策还需要到接入点拍照登记个人信息等,一来比较繁琐,二来我们担心万一网站可能存在的信息问题导致不必要的...

10款好用的Linux服务器网站管理面板推荐

如今在建站时,很多人都会使用管理面板来辅助建站,因为相对于手动安装软件,面板更加简单而且高效,即使新手也能很快学会搭建网站,在本文中我们来推荐几款好用的网站管理面板宝塔面板宝塔面板是一款简单好用的网站...

小白拥有一台云服务器到底能干些什么?成就感爆棚的简单方案!

?云服务器是什么?云服务器(比如阿里云、腾讯云等)是提供给用户的一种虚拟服务器资源,你可以把它看作一台“rent的电脑”,只需要支付少量费用就可以拥有一个功能强大的网络设备。对于小白来说,拥有一...

苹果CMS,苹果CMS采集插件,苹果CMS快速建站(图文教程)

苹果CMS,有着强大的管理功能,管理后台界面大方、操作简单、功能齐全、模块众多、双端管理。苹果CMS加上丰富的系统标签,系统内置了丰富的cms标签并支持thinkphp框架标签完美融合,可以调取系统内...

新手搭建网站、小程序、APP等系统,如何选择服务器?

今天和小蔡和大家说说,新手搭建网站,如何选择服务器?废话不多说,直接来干货。服务器是存放网站源代码的容器,也是运行网站程序的工具,所以是不可或缺的。新手刚接触搭建网站,若不知道怎么去选择一台适合自己...

在海外VPS服务器(Hostinger)上配置宝塔面板的操作步骤

不得不说,宝塔面板是真的好用啊~用上就放不下了,一些海外的免费开源的服务器集成面板(比如CloudPanel)我也用了,不喜欢,真的不如宝塔面板方便耐用。今天聊一下在海外服务器(也包括国内服务器,没有...

干货盘点:每个wordpress站长都推荐完成的60个任务清单

构建和运营wordpress网站包含了很多重要任务,遗漏哪一方面都可能造成或大或小的不良后果,因此我们特别整理了这个任务清单,为你查漏补缺,希望能对您现在运营或者即将开始构建的wordpress网站有...

为什么站长喜欢选择BlueHost主机建站

BlueHost正式成立于2003年,从事主机(虚拟主机)业务至今已经将近十余年,无论从口碑还是用户的评价,我们基本很少看到关于Bluehost主机产品和商家负面的评论信息。从2014年开始,Blue...

自助建站时代来临 半小时成建站达人

“H5”意为第五代HTML,即第五代网页编写语言。自从1991年第一代HTML开始研发以来,网页编写、网站建设一直都属于高端技术行业,网站建设人员都是一些专业型人才,这也意味着网站的建设和维护都需要不...

现代化、开源的 Linux 服务器运维管理面板

1Panel是一个现代化、开源的Linux服务器运维管理面板。1Panel的功能和优势包括:快速建站:深度集成Wordpress和Halo,域名绑定、SSL证书配置等一键搞定;高效管理...

[1Panel]开源,现代化,新一代的 Linux 服务器运维管理面板

测评介绍本期测评试用一下1Panel这款面板。1Panel是国内飞致云旗下开源产品。整个界面简洁清爽,后端使用GO开发,前端使用VUE的Element-Plus作为UI框架,整个面板的管理都是基于do...

取消回复欢迎 发表评论: