【每日一题NO.80】说说你对前端工程化的理解
目录:
什么是工程化?
前端,是一种 GUI 软件
前端工程化需要考虑哪些因素
模块化
组件化
性能优化
规范化
自动化
典型的前端工作流程分为 5 个步骤
开发阶段
测试阶段
构建阶段
部署阶段
监控阶段
总结
什么是工程化?
前端工程本质上是软件工程的一种。软件工程化关注的是性能、稳定性、可用性、可维护性等方面,注重基本的开发效率、运行效率的同时,思考维护效率。一切以这些为目标的工作都是前端工程化。
工程是一种思想而不是某种技术。
举一个通俗的例子:要盖一栋大楼,假如我们不进行工程化的考量那就是一上来掂起瓦刀砖块就开干,直到把大楼垒起来,这样做往往意味着中间会出现错误,要推到重来或盖好后结构有问题但又不知道出现在哪里谁的责任甚至会在某一天轰然倒塌,那我们如果用工程化的思想去做的话,就会先画图纸、确定结构、确定用料和预算以及工期,另外需要用到什么工种多少人等等,我们会先打地基在建框架再填充墙体这样最后建立起来的高楼才是稳固合规的,什么地方出了问题我们也可以快速找到源头和负责人。
前端,是一种 GUI 软件
我们知道前端技术的主要应用场景并非知识高大上的基础库、框架,狂拽炫酷的宣传页面或者吊炸天的小游戏等这些一两个文件的小项目,更具有商业价值的则是复杂的 Web 应用,它们功能完善、界面繁多,为用户提供了完整的产品体验。
从本质上来讲,所有 web 应用,都是一种运行在网页浏览器中的软件,这些软件的图形用户界面即为前端。如此复杂的 Web 应用,动辄几十上百人共同开发维护,其前端界面通常也颇具规模,工程量不亚于一般的传统 GUI
软件。
前端工程化需要考虑哪些因素
模块化
简单来说,模块化就是将一个大文件拆分成相互依赖的小文件,在进行统一的拼装和加载,方便多人协作。分而治之是软件工程中的重要思想,是复杂系统开发和维护的基石,这点放在前端开发中同样适用。模块化是目前前端最流行的分治手段。不管你将来是否要复用某段代码,你都有充分的理由将其分治为一个模块。
JS 模块化方案:
AMD
、CommonJS
、UMD
、ES6 Modules
等等
CommonJS
的核心思想就是把一个文件当作一个模块,要在哪里使用这个模块,就在哪里 require
这个模块,然后 require 这个方法开始加载这个模块并且执行其中的代码,最后返回你指定的 export 对象。
CommonJS
加载模块是同步的,所以只有加载完成才能执行后面的操作,不能非阻塞的并行加载多个模块。
AMD
异步模块加载定义,Asynchronous Module Definition
,特别是可以实现异步加载模块,等所有模块都加载并且解释执行完成之后,才会执行接下来的代码。
// 通过AMD引入模块
define(
module_id, // 可选
[dependencies], // 可选
definition function, // 回调 用来初始化模块或对象的函数
)
// 使用
define(['myModule','myOtherModule'],function(myModule,myOtherModule){
console.log(myModule.say());
// 会并行加载所有的模块,这里是a、b模块,并执行其中模块的代码后,再逐步执行下面的console
require("a");
console.log('a required');
require("b");
console.log('b required');
console.log('all modules have been required');
})
UMD
在一些同时需要 AMD 和 CommonJS 功能的项目中,你需要使用另一种规范:Universal Module Definition
(通用模块定义规范)。
UMD 创造了一种同时使用两种规范的方法,并且也支持全局变量定义。所以 UMD 的模块可以同时在客户端和服务端使用。
ES Modules
幸运的是在 JS 的最新规范 ECMAScript6 中,引入了模块功能。
ES6 的模块功能汲取了 CommonJS 和 AMD 的优点,拥有简洁的语法并支持异步加载,并且还有其他诸多更好的支持---导入是动态只读的,CommonJS 只是相当于把导出的代码复制过来。
css模块化方案
预处理器
在less、sass、stylus等预处理器的import/mixin
特性支持下实现 css modules。
虽然Sass、Less、Stylus等预处理器实现css的文件拆分,但没有解决css模块化的一个重要问题---选择器的全局污染问题
css in JS
css in JS
是彻底抛弃css,使用js或json来写样式。这种方法很激进,不能利用现有的css技术,而且处理伪类等问题比较困难
css Modules
css Modules
原理:使用JS来管理样式模块,它能够最大化结合css生态和js模块化能力,通过在每个class名后带一个独一无二hash值,这样就不有存在全局命名冲突问题
组件化
前端作为一种 GUI 软件,光有 JS/CSS 的模块化还不够,对于 UI 组件的分支也有着同样迫切的需求,分治的确是非常重要的工程优化手段。
由于系统功能被分治到独立的模块或组件中,粒度比较精细,组织形式松散,开发者之间不会产生开发时的依赖,大幅度提升并行的开发效率,理论上允许随时加入新成员认领组件开发或者维护工作,也更容易支持多个团队共同维护一个大型站点的开发。
性能优化
模块化/组件化开发之后,我们最终解决的是:模块化或组件加载的技术问题。
然而前端与客户端 CUI 软件有一个很大的不同:前端是一种远程部署,运行时增量下载的 GUI 软件。
如果用户第一次访问页面就强制其加载全站静态资源在展示,相信很多用户因此会失去耐心而导致的流失。
根据增量的原则,我们应该精心规划每个页面的资源加载策略,使得用户无论访问哪个页面都能按需加载页面所需资源,没访问过的无需加载;访问过的可以缓存复用,最终带来流畅的应用体验。
这正是 Web 应用免安装的魅力所在。
由增量原则引申出的前端优化技巧几乎成为了性能优化的核心。
有加载相关的按需加载、延迟加载、预加载、请求合并等策略。
有缓存相关的浏览器缓存利用、缓存更新、缓存共享、非覆盖式发布等方案
还有复杂的 BigRender、BigPipe、Quickling、PageCache 等技术
这些优化方案无不围绕着如何将增量原则做到极致而展开。
一种静态网页资源管理和优化技术
静态资源管理=资源表+资源加载框架
资源表是一份数据文件(例如 json 文件),是项目中所有静态资源(主要是 css 和 js)的构建信息记录,通过构建工具扫描项目源码生成,是一种 k-v
结构的数据,以每个资源的 id
为 key,记录了资源的类别、部署路径、依赖关系、打包合并等内容。
{
"res": {
"widget/a/a.css": "/widget/a/a_1688c82.css",
"widget/a/a.js": "/widget/a/a_ac3123s.js",
"widget/b/b.css": "/widget/b/b_52923ed.css",
"widget/b/b.js": "/widget/b/b_a5cd123.js",
"widget/c/c.css": "/widget/c/c_03cab13.css",
"widget/c/c.js": "/widget/c/c_bf0ae3f.js",
"jquery.js": "/jquery_9151577.js"
},
"pkg": {
"p0": {
"url": "/pkg/lib_cef213d.js",
"has": ["jquery.js"]
},
"p1": {
"url": "/pkg/widgets_22feac1.js",
"has": ["widget/a/a.js", "widget/b/b.js", "widget/c/c.js"]
},
"p2": {
"url": "/pkg/widgets_af23ce5.css",
"has": ["widget/a/a.css", "widget/b/b.css", "widget/c/c.css"]
}
}
}
在查表的时候,如果一个静态资源有 pkg
字段(用来记录 web 应用中一个页面加载过的静态资源,当下个页面用到这个资源就无需加载了,有效利用缓存),那么就去加载 pkg 字段所指向的打包文件,否则加载资源本身。
规范化
规范化其实是工程化很重要的一个部分,项目初期规范制定的好坏会直接影响到后期的开发质量。
目录结构的制定 编码规范 前后端接口规范 文档规范 组件管理 Git 分支管理 Commit 描述规范 定期 CodeReview 视觉图标规范 …
自动化
任何简单机械的重复劳动都应让机器去完成。
图标合并 持续集成 自动化构建 自动化部署 自动化测试
典型的前端工作流程分为 5 个步骤
开发阶段
开发阶段的首要任务是创建样板项目(一并选择前端框架、类库),接着开始修改-验证的主循环,主要设计这些工程化设施:
脚手架
:创建前端应用的目录结构,并生成样板代码
公共库
:维护着可复用的 UI 组件、工具模块等公共资源
包管理器
:引入第三方库/组件,并跟踪管理这些依赖项
编辑器
:提供语法高亮、智能提示、引用跳转等功能,提升开发体验
构建工具
:提供语法校验、编译、打包、DevServer 等功能,简化工作流程
调试套件
:提供预览、DevTools、Mock、性能分析诊断等调试功能,加速修改-验证的主循环
测试阶段
开发完成,进入测试阶段,先要对整体功能进行充分自测,在移交给专业的测试人员验证,过程中需要用到工程化设施有:
单元测试框架
:提供针对组件、逻辑的测试支持
静态扫描工具
:从代码质量、构建产物质量、最佳实践/开发规约等多个维度做静态检查
自动化测试工具
:针对 UI 效果和业务流程,提供测试支持
性能测试工具
:监测并统计出相对准确的性能数据
构建阶段
不同开发阶段,在构建阶段要做更多的极限优化和流程联动,涉及:
打包脚本
:在语法校验、编译、打包的基础上,进行合并、压缩、代码拆分、图片处理、ssr 等极限优化
构建服务
:支持多任务并行打包、通知
部署阶段
最后将经过充分测试的前端应用程序部署到生产环境,需要这些工程化工具:
发布平台
:将前端资源上传到 CDN 或 SSR 渲染服务,或者以离线包的形式集成到移动客户端
迭代管理平台
:提供 CI/CD 支持
监控阶段
前端应用程序上线之后,还需要持续关注线上的实际效果和异常情况,依赖这些工程设施:
埋点平台
:统计、分析业务数据,跟踪性能指标
监控平台
:观察线上的异常信息,包括报错、白屏、流量异常等等
总结
最理想的,前端工程不仅要持续优化协作模式、提升开发效率、还应该保障开箱即用的上手体验、一气呵成的衔接体验、无缝切换的升级体验。
从面向过程的角度来看,前端工程是贯穿前端应用生命周期的一系列工程设施,用来保障前端应用的开发体验、质量和交付速度。
具体的,前端工程围绕开发
、构建
、发布
3 条主线展开,以工程化
、自动化
等手段解决各个环节所遇到的问题。
一方面控制前端开发的复杂度,提高前端生产效率;另一方面降低架构升级,协作模式变化等带来的迁移,适配成本、提升开发体验。
所有《每日一题》的 知识大纲索引脑图 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
你也可以点击文末的 “阅读原文” 快速跳转