前端工程化

前端工程化

上周末和朋友聊天的时候讨论到了什么是前端工程化,然后大家都各抒己见

最后大家可能都得到了一点点统一的结论

  • 稳定性 (stability)
  • 可用性 (usability)
  • 可维护性 (maintainability)
  • 可扩展性 (scalability)
  • 可访问性 (accessibility)
    看起来好像就是各种ability的集合,当然还有一个niuability,哈哈哈哈。

实际上呢,优秀的技术方案很多,大部分的时候我们感觉只是在现有的技术方案里面做排列组合,选择最优解,做出一个最合适当前项目的方案。就像是尤雨溪在2019 JSConf.Asia上说的主题,在框架设计中寻求平衡。从职责范围,渲染机制,状态机制三个方面总和考虑,才有了现在vue。

那么前端工程化呢,不仅仅是对开发层面的组件化,模块化,规范化等等,更涉及到了构建,部署等等的工程化,甚至是自动化。

编译和构建

编译(compile)和构建(build)概念有一些区别。

编译是指将源代码变为目标代码的过程,从源代码的语言转变为另外一种计算机语言(一般为比源代码语言更为底层的语言)。

构建是指一些列的处理,包括编译。不同的语言构建会有不通的处理步骤,最终产生可在具体特性环境运行的Artifact。

前端的编译。为了更好的编程体验和更高的可维护性,会使用一些超集的语言,然后再转译为浏览器可以运行的语言。

前端构建过程一般包括以下几个过程:

  • 代码检查
  • 运行单元测试等
  • 语言编译
  • 依赖分析、打包、替换等
  • 代码压缩、spirit 图片压缩等
  • 版本生成
  • 构建结果一般生成为一个或多个文件,里面包括直接可以在部署在特定环境中的所有内容。

这里涉及到了几个阶段

第一、库/框架选型

基本上现在没有人完全从0开始做网站,哪怕是政府项目用个jquery都很正常吧,React/Angularjs等框架横空出世,解放了不少生产力,合理的技术选型可以为项目节省许多工程量这点毋庸置疑。

第二、简单构建优化

选型之后基本上就可以开始敲码了,不过光解决开发效率还不够,必须要兼顾运行性能。前端工程进行到第二阶段会选型一种构建工具,对代码进行压缩,校验,之后再以页面为单位进行简单的资源合并。

第三、JS/CSS模块化开发

分而治之是软件工程中的重要思想,是复杂系统开发和维护的基石,这点放在前端开发中同样适用。在解决了基本开发效率运行效率问题之后,前端团队开始思考维护效率,模块化是目前前端最流行的分治手段。

JS模块化方案很多,AMD/CommonJS/UMD/ES6 Module等,对应的框架和工具也一大堆;CSS模块化开发基本都是在less、sass、stylus等预处理器的import/mixin特性支持下实现的。

第四、组件化开发与资源管理

当我们要开发一款完整的Web应用时,前端将面临更多的工程问题,比如:

  • 大体量:多功能、多页面、多状态、多系统;
  • 大规模:多人甚至多团队合作开发;
  • 高性能:CDN部署、缓存控制、文件指纹、缓存复用、请求合并、按需加载、同步/异步加载、移动端首屏CSS内嵌、HTTP 2.0服务端资源推送。

这些无疑是一系列严肃的系统工程问题。

由于先天缺陷,前端相比其他软件开发,在基础架构上更加迫切的需要组件化开发和资源管理,而解决资源管理的方法其实一点也不复杂:

一个通用的资源表生成工具 + 基于表的资源加载框架

近几年来各种你听到过的各种资源加载优化策略大部分都可以在这样一套基础上实现,而这种优化对于业务来说是完全透明的,不需要重构的性能优化——这不正是我们一直所期盼的吗?正如魏小亮博士所说:我们可以把优秀的人集中起来去优化加载。

如何选型技术、如何定制规范、如何分治系统、如何优化性能、如何加载资源,当你从切图开始转变为思考这些问题的时候,我想说:

你好,工程师!

Artifacts

每一次成功构建后产出的结果,被称为 Artifacts。Artifacts 可以直接部署到特定环境中并正常运行。每个构建结果一般都会版本保存,为后续部署、回滚、灰度等。

可能是因为构建的速度,后端都会有一个 Artifacts 制品库。而早期一般的前端项目对 Artifacts 的概念比较弱化(更早的前端项目直接没有构建的概念)一般会从 Code 直接构建部署到指定的环境。现有的规范化的项目都会有对构建产物所有版本的保存,一般都提供CND来访问。

部署、发布

部署(deploy)是指把构建后的新版本应用或服务“安装”到目标环境(开发、测试或者生产)中。这时候部署好的应用或服务应该是在目标环境中正常运行着(或者待着),但是不会有任何访问的流量。

发布(release)则是把新版本应用或者服务交付给最终用户使用的过程。相当于把流量切到部署好的新版本的过程。

前端项目部署一般是指文件的增量替换或全量替换。根据项目按需决定,部署和发布可以同时进行,也可以分开进行,前提是在不影响用户访问的同时,把前端的代码更新到相应的版本。

CI/CD

CI,Continuous Integration,持续集成。指代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。目的就是让产品可以快速迭代,同时还能保持高质量。

CD 对应有两个意思:
CD,Continuous Delivery,持续交付。指的是任何的修改都经过验证,可以随时实施部署到生产环境。
CD,Continuous Deployment,持续部署。持续部署是持续交付的更高阶段,指的是任何修改后的内容都经过验证,自动化的部署到生产环境。

两者的区别,在于是否自动部署到生产环境。持续交付,需要用户手动点击“部署”按钮才能部署到生产环境。

前端项目的 CI/CD ,因为一定的特殊性,对通用化的库或框架可以有覆盖率很高的单元测试/自动化测试,然而对业务代码的单元测试、端对端测试则成本很高,所以 CI 的过程一般就是运行构建脚本,未报错的生成静态文件则为成功。一般不会有 CD(持续部署) 的过程,合并到 develop 分支触发 CI 成功后快速发布部署到测试或预发布环境通过测试人员的测试和一定的自动化测试。到需要发布的时间点,再拉取 master 分支来构建部署发布。

前端集成和解决方案

  1. 规范开发: 代码规范,命名规范,目录规范,部署规范等等。都规范化了之后,可以极大的提升开发效率,快速定位问题
  2. 模块化: 针对js、css,以功能或业务为单元组织代码。js方面解决独立作用域、依赖管理、api暴露、按需加载与执行、安全合并等问题,css方面解决依赖管理、组件内部样式管理等问题。是提升前端开发效率的重要基础。
  3. 组件化开发: 在模块化基础上,以页面小部件(component)为单位将页面小部件的js、css、html代码片段放在一起进行开发、维护,组件单元是资源独立的,组件在系统内可复用。通常会行成一部分的业务代码,可以通用使用
  4. 组件仓库: 组件化之后,我们希望把这些可以公用的组件抽出来,提供给别的团队使用,不用重复造轮子,这个时候就行成了组件厂库,比如antd
  5. 性能优化: 这里指的是工程化方面的性能优化。性能优化一定是一个工程问题和统计问题。
  6. 项目部署: 请看部署方式
  7. 开发流程: 请看前端灰度、A/B测试方案
  8. 开发工具: 构建与优化工具、开发-调试-部署等流程工具,以及组件库获取、提交等相关工具,甚至运营、文档、配置发布等平台工具
以上8项,1-3是技术和业务相关的开发需求,4是技术沉淀与共享需求,5-8是工程优化需求。

这个时候,各种前端集成解决方案来了

就像我经历过的,从create-react-app开始,后来公司不满足于这套方案,于是在create-react-app上面开始开发符合公司开发规范的集成方案。

现在很火的Umi.js,可扩展,开箱即用,大量自研,完备路由等等方面,收到极大的好评

部署方式

蓝绿部署

蓝绿部署中,绿色代表代表正在给用户提供正常服务的系统;蓝色代表另外一套准备发布的系统,还未对外提供,可以做线上测试。

二套系统必须有相同的基础设置和配置环境,当蓝色系统测试通过,达到上线标准,就把绿色系统的流量全部切到蓝色系统中,一旦蓝色系统出现问题,把所有流量切回到绿色系统中,待蓝色系统稳定后就成为新的绿色系统,之前的绿色系统资源就可以释放用于下一个蓝色系统。

蓝绿部署能够简单快捷实施的前提假设是目标系统是非常内聚的,如果目标系统相当复杂,那么如何切换、两套系统的数据是否需要以及如何同步等,都需要仔细考虑。

滚动部署

有多个集群实例的服务中,在不影响服务的情况下,停止一个或多个实例,进行版本更新,再启动加入到集群中提供正常服务,直到所有实例都更新到最新版本。

比起蓝绿部署不需要准备二套一样的集群,通过现有的机器或增加少量的机器就可以做到版本升级。但也引入了复杂度,需要控制好更新过程中服务会有新老版本用户共存的兼容情况、防止部署过程中自动伸缩的触发导致实例中版本的不确定、部署过程中出错的回滚策略等。

金丝雀发布(灰度发布)

一种比滚动部署更有控制力度的发布策略。

准备一个或多个服务实例(使用新机器或已有的机器都可),并确保该实例服务没有服务于线上的用户,在上面部署新版本的服务,并经过测试的验证。

通过定制好的策略,只更新部分服务实例到最新,使一部分用户使用到最新版本,如果服务正常,逐渐更新所有服务实例到最新。

发布过程中,需要有一些流量控制的策略跟随部署的过程,一般可以在负载均衡、路由、应用程序中做处理。

针对用户级别分流。比如先部署给内部用户,在逐渐根据外部用户的分类等级扩散。
地域、IP 级别分流。只部署新版本到某地理地域,慢慢扩大到全量发布。
应用程序中判断特性分流。比如通过什么渠道使用服务的、浏览器特征分析、某个使用触发才使用新版本。

A/B测试

A/B测试指的是效果测试,同一时间有多个版本的服务在线上运行,并通过一定的策略控制多个版本的流量分配,最终通过信息的收集,分析各个版本服务的实际效果,选出效果最好的版本。

A/B测试强调的是通过不同版本对比效果来选择出最好的版本,而然金丝雀发布(灰度发布)的方式正好可以满足A/B测试的需求。

构建和部署

现有的 CI/CD 方案都已经很成熟,Jenkins、Travis、Gitlab-CI 等。docker、k8s 让这些工具简直带上了无限手套,因为构建部署是需要机器资源的,相比之前固定的资源抢占和空置,k8s 让资源动态创建、销毁,提升资源利用率。

方案的选择应该都是需要具备上下文的,根据项目的规模、当前的境况。一个前端项目,发展越来越庞大之后,自然而然都会出现需要重构来更新技术方案和更适应已有的项目规模。之前简单的构建、部署、灰度方案的弊端会越来越显现。

前后端项目,对构建和部署的流程可能会有所不同,后端程序的发布上线相对来说复杂度更高。

相比之前手动的构建部署,现有相对优的方案步骤是这样的:

  • 提交代码,合并到具体分支
  • 自动触发 CI/CD
  • 通知结果到相关人员
    流程很简单清晰,Jenkins、Gitlab-CI/CD 等工具完全可以快速上手和实践。

但隐藏了很多的细节,具体以下几点来看:

耦合 CI/CD 系统。一些构建的脚本是否和当前的 CI/CD 系统有较强的耦合,如果现有的 CI/CD 系统做迭代或替换时候,是否可以做到最小工作量的升级,应该有一个清晰的构建脚本和流程规范在项目中,通过 CI/CD 系统做执行,保持良好的规范和独立性。
构建和部署解耦和打通。前端项目的构建最终都是会产出 Artifacts(html/css/js/images/etc.) 的,这也就是 CI 系统负责的部分,部署发布过程根据公司的流程制度一般有多种角色介入,开发、测试、运维等人员,构建生成 Artifacts,由其他角色或系统部署上线。构建的过程一般到产生 Artifacts 为止,上传或通知到部署系统,部署的过程则有对应的人员或系统拉取对应的 Artifacts 进行发布部署。
部署环境的管理。项目中应该隐藏对具体部署环境的细节,具体由部署平台来接管部署到具体环境中。
部署策略。涉及上述提到的几种发布部署策略,需要对部署过程中出现的问题做预测和对应的方案,如部署过程中程序出错、网络问题导致不同步等。不过 k8s 集群的出现,让部署发布过程安全可靠了很多。
构建部署数据收集。构建成功/失败次数,部署频率等 CI/CD 的数据,这些数据可做各种支撑,也是项目的一部分。具体可调用相应的 API 把数据统一保存到数据库中,做进一步的分析、查看、整合。
构建由 CI 工具来具体负责,只要定制好具体的规范和脚本。后端项目部署的过程由 k8s 的出现变成风险可控。我们要做的就是把构建和部署做规范的制定、系统的打通、数据的整合。

前端构建后产物都会带版本号,采用hash指纹,构建出来的文件没有改变则hash也不会变,可以先发布新版的静态资源文件,旧版同时存在。控制好入口文件(一般为html)的发布的顺序,一般就可正常上线。如果有依赖的后端接口的更改便需要先上线新版本的接口(向上兼容或新版本),再上线前端项目。

静态资源大多会使用 CDN,发布到源站后,CDN 则会自动拉取源站的文件。

前端灰度、A/B测试方案

后端服务一般都会在负载均衡层(nginx居多)做灰度方案等,业界成熟的方案大多为:

简单灰度逻辑通过 nginx 配置做规则判断(路由、参数、ip等)然后 upstream 到不同的服务器。
复杂灰度逻辑通过 nginx+lua 结合自己业务来做流量的灰度、切换等。

前端静态资源要做到灰度或 A/B 测试的效果,几种方案为:

  1. JS 代码中做埋点判断,通过对用户特征(UA、cookie、或后端提供字段等)的判断显示为不同功能等。
  2. 服务端渲染,在服务端通过特征规则判断显示不同分支版本或功能。
  3. 控制入口文件,使用 nginx 判断变量 upstream 到不同 server。
    方案1,对业务的入侵性大,灰度规则与代码耦合,增加了文件的大小。
    方案2,因为控制了渲染入口,能做的很多,但也增加了服务端渲染的复杂度,使用场景有限。
    方案3,前端入口一般都是一个 html 文件,后再去加在各种静态资源,通过对入口文件的控制,结合 nginx 方案可以做到很好的灰度和 A/B 测试方案。

上述提到的后二种方案大同小异,增加一个中心服务,通过特征规则匹配判断来实现不同版本的灰度控制和 A/B 测试。对复杂度高和业务逻辑比较多的,比如微前端方案、有较多独立的功能和页面需要做到灰度和 A/B 测试的项目则会比较合适。

二种方式可结合使用在微前端方案中。

最后

不断在发展的中沉淀自己,向社区反馈属于自己idea,好好加油

参考文献

https://zhuanlan.zhihu.com/p/71562853
https://github.com/fouber/blog/issues/1
https://github.com/fouber/blog/issues/6
https://github.com/fouber/blog/issues/10
https://cloud.tencent.com/developer/news/412026
https://www.infoq.cn/article/LEI4vSFPiw5A6eN-ASo4
https://www.lijiaocn.com/%E6%96%B9%E6%B3%95/2018/10/23/devops-blue-green-deployment-ab-test-canary.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值