Open fouber opened 9 years ago
说一句题外话,是否可以开一个频道,讨论一下App的编译打包,以及App中引用静态资源的一些方案?
@feifeipan
这篇文章其实也说出了web工程与native工程的根本差异——资源部署位置的不同。app的编译打包我觉得主要还是在传统native的打包之前加入前端的构建过程,最终资源还是部署在用户本地,资源管理问题少很多,采用常规的模块化框架使用其中的资源加载器应该就足够了。不存在诸如按需加载、CDN部署、文件指纹、本地缓存等web端才有的工程问题
mark
很赞的一个前端比较系统的的一个基础知识框。值得多看看
感觉好优美啊
有一个不情之请,能不能请童鞋们不要简单的回复 “mark” “赞”。。因为这样的话,那一千多个 watch
这个repo 的同学就都会收到notification。对于有强迫症,手动点已读的人太痛苦。如果对这个话题和讨论感兴趣,可以点击上面的watch
按钮或者点star
。
抱歉回复这条与话题的无关的回复
mark
@ruanyl 这些都是从中文社区带来的恶习。我都是手动删除并 @
到每一位被删的人,告诉他们可以点击右侧的“subscribe”按钮来订阅这个话题。
内容写得很不错,受益匪浅。
表示一下不解:
其一,组件化开发看过类似于BS等的多个框架的源码都是把CSS和JS放在不同的目录,您的方法是按组件放在各自的目录,相对而言更便于管理,但使用grunt等工具构建的时候会不会比较麻烦?
其二,对您后面所讲的资源管理理解有些模糊,所述方法是通过一个后端PHP根据场景动态生成资源文件并缓存吗?
@jerrybendy
你的这两个问题其实本质上是一个问题。
php没有『动态生成』资源文件,是构建工具先处理(压缩、合并)好所有资源之后,额外还生成了一个json文件,里面记录了资源的路径(url),而php在服务端拼装html的时候,可以查这个表知道各个资源的url,然后动态生成资源的加载标签(script/link),有关这部分内容,更详细的说明我会在近期发布第二篇文章解释清楚,或者你也可以看我其他文章中的相关介绍:https://github.com/fouber/blog/issues/3
构建工具的重点是扫描和记录文件间的依赖关系,以及资源合并情况,将这些信息组织为一份json数据,项目构建之后生成多一份json文件而已。这个json文件,就用于第一个问题中的php框架查询使用了。
嗯,原理大致是明白了,具体还要在实践中去摸索实现,谢谢指教 :smile:
看到了与心中暗合的前端未来走势,前端已经慢慢成为软件工程师了
我从 Rails 的 Asset Pipeline 里学到不少东西。大家也可以去看看 http://guides.rubyonrails.org/asset_pipeline.html
资源就像仓库,现在放在仓库里的东西太多了,然后就立一张表(资源表),用于标记资源A放在仓库哪个角落以及还需要什么零件。然后来装货的卡车就只要用手中的识别装置(加载框架)扫描这张表,以得到正确的获取方式和流程
谢谢楼主的分享!受益匪浅!但是能否更新下下边文章的链接?万分感谢!
前端工程其实是一个很大的话题,开发仅是其中的一部分。 相关文章: 前端工程——工具篇 前端工程——框架篇 前端工程——架构篇 前端工程——流程篇 前端工程——监控篇 前端工程——测试篇
受益匪浅。
受教
mark
@fouber 首先赞一下,写的太好了。
然后有几个问题:
@AxisWhistle
所以,我们并不需要关注img是否公用,而是关注img是以怎样的方式被使用,无非三种情况:
不难发现,无论是哪种情况,最终图片都会被汇入到模块化体系中使用,那我们就可以不使用直接的方式使用图片资源,而是转成模块间的相互调用复用资源。
最常见的情况就是系统中的一些小图标,好多地方都用,一种方案或许是你这里提到的『提取公共图片到一个目录,然后别的资源引用』,但其实还有另外一个方案
创建一个叫icon的CSS或JS模块,其他需要的地方用模块化的方式复用。
比如我有a.png,b.png,c.png,d.png四个所谓的公共图片,合理的做法是可以把这四个图片放到一个icon模块目录下:
─ components
├─ icon
│ ├─ img
│ │ ├─ a.png
│ │ ├─ b.png
│ │ ├─ c.png
│ │ └─ d.png
│ ├─ icons.js
│ └─ icons.css
├─ ...
我们就可以在 icon.css
中这样使用资源:
.icon { ... }
.icon-a { background-image: url(img/a.png); }
.icon-b { background-image: url(img/b.png); }
.icon-c { background-image: url(img/c.png); }
.icon-d { background-image: url(img/d.png); }
还可以在 icon.js
中这样使用资源:
exports.a = __uri('./img/a.png');
exports.b = __uri('./img/b.png');
exports.c = __uri('./img/c.png');
exports.d = __uri('./img/d.png');
而在其他模块中要使用这些图片,可以走css模块化方案复用:
/**
* file: components/foo/foo.css
* @require icon
*/
.foo .icon {
width: 30px;
}
也可以走js模块化方案复用:
// file: components/bar/bar.js
var icon = require('icon');
var img = new Image();
img.src = icon.a;
$('#bar').append(img);
如果是模板中的标签复用也是类似的道理,不一一举例了。总之,别忘了模块化的代码组织,复用已经有了另外一条更加『优雅』的途径。
所以,回到你的问题,当我们在模块化体系中维护资源,还有必要出现 app/img
这样的目录么?我觉得每个资源都应该隶属一个模块,就近使用,就近维护,而复用其实是来自模块化的组织。
组件划分上,我觉得 BEM 其实是一个很好的策略:如果你发现TodoList不能脱离Todo组件单独存在,那么TodoList实际上不是一个独立的『Block』,而是Todo这个组件的一个『Element』,类似的还有tab组件的标签部分结构,也是tab组件的Element,而不是独立的Block。
注意,我比较推荐的是BEM的划分策略,至于命名规范,这个自己怎么喜欢就怎么约定,不用强求。
components下应该都是独立的『Block』,彼此平级,没有依赖关系,只有组合关系,而每个组件中可能有自己的『Element』,仅是组件内一些结构的划分,并不独立出现。Block和Element的界限或许有那么一点不太清晰,但实际操作起来感觉还好,能独立使用的部分大多分治为独立组件,Element仅出现在一些比较复杂的大组件中。
@fouber 我在尝试使用这种解决方案,但是过程中又很多实践性的问题。 我使用React开发组件。
前端工程——工具篇
@AxisWhistle 我在实践中遇到的和你是一样的问题,其实我目前遇到2种加载组件的方式:
页面同步加载,比如header,footer,这些属于和view(页面)一起load的部分,我是使用了一个自定义的link标签来定义模块的,开发时指定目录名即可,我在fis插件中把“目录名.css,目录名.js"自动添加到页面的依赖中,并且将”目录名.html“嵌入link所在的位置。
异步加载的组件,这时候涉及一个问题,就是你用什么来管理组件,其实fis有资源嵌入的能力,然后js的loader一般也都是标配,所以使用js来管理组件的其他资源的想法就很自然了,我们也是这么做的
当然应该也有其他的做法,看看大神怎么说 @fouber
@AxisWhistle
当在pages中引用组件时,require的路径改如何填写,使用'../../components/XX/XX.js'么?感觉这种加载的相对路径很不友好。有什么好的解决方案么?
其实,require这个函数不是我们自己掌握的么?那我加个规则:
var require = function(id){
if(id[0] !== '.') { // 如果id参数不以 '.' 开头,就认为是短id,通过拼装生成完整id
id = 'components/' + id + '/' + id + '.js';
}
....
};
id系统可以有别名,可以有缩略名。这些定制的主动权都是掌握在开发者手中,上面介绍的方案是自己控制require函数,拼接完整id,等于约定了缩略名规范。如果使用的是第三方模块化框架,一般都有别名功能,我通常会写一个构建工具插件,把符合别名条件的js注入到模块化框架的别名表中达到缩短id的目的。
前端的工程化,工具应该是很重要的一部分。目前React支持组件化的开发,出现Flux,Redux类似管理组件的架构,也有webpack这种打包工具。刚刚接触前端模块化这部分知识,零零散散得看了React,Redux,webpack等,可是实际动手做时,就发现这些工具给出的示例都关注自身内部特性,对于相互之间的如何协作,如何使用这些工具搭建一个比较完整的开发环境涉及很少。能够就关于文中的理论结合目前的前端工具如React,webpack等讲一下如何实现一个工程化开发流程或者说开发环境?很期待文末的前端工程——工具篇
在工具篇,可能并不会介绍具体的某个工具。我只想描述整个系统是如何运作的,而工具又在系统中发挥了怎样的作用。从基本原理切入,介绍前端构建工具的本质,这些基础的内容只要浏览器的渲染机制不发生改变,其核心就不会发生改变。为什么有人会困惑工具与框架的组合使用问题呢?我觉得唯有解释清楚这些基本原理,才能让人明朗工具、框架、规范、部署之间的内在联系,随心所欲的组织前端架构,应用新技术,适配各终端,做到游刃有余,手到擒来。
吐槽一下,前端很浮躁,这些年变化很快吗?我个人表示怀疑,浏览器还是如7、8年前一样的工作方式,整个解析与渲染过程根本没有发生过变化,而那些所谓的改变,其实都只是表象。
重新看了一遍,非常感谢作者的分享。
受益良多!
NB. 虽然寡人完全不懂前端~
@fouber 问个组件问题,我用的fis3 + jsp. 我把图片验证码作为一个组件
└── vercode
├── vercode.js
└── vercode.jsp
vercode.js
define([], function(){
var vm = avalon.define({
$id: "vercodeCtrl",
_time: getTime(),
changeVerCode: function(){
vm._time = getTime();
}
})
function getTime() {
return new Date().getTime();
}
})
vercode.jsp
<%-- JSP标签,每个JSP必须引入 --%>
<%@ page contentType="text/html;charset=utf-8" %>
<%@ page import="java.util.*" %><%@ taglib uri="/fis" prefix="fis"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<div class="${wrapClass}" ms-controller="vercodeCtrl">
<img alt="验证码" ms-attr-src="${ctx}/comm/pcrimg?_t={{_time}}" title="点击刷新" ms-click="changeVerCode()">
</div>
<fis:script>
require(["./vercode"],function(){
avalon.scan();
});
</fis:script>
使用时
<fis:widget name="widget/vercode/vercode.jsp" wrapClass="rg-check-code"/>
疑问: vercode.js的引入,我是像现在这样 直接在组件内引用? 还是让业务页面js使用时依赖vercode.js?
@codering 业务页面js需要vercode.js的什么接口么?
@fouber 不需要。 哦,明白了。 如果不需要组件接口,就可以直接在组件内引用; 如果需要组件接口,则让页面js依赖该组件,使用组件接口。
但是以后 从不需要 变成 需要了,就得改业务js的依赖。这样看来,不管需不需要组件的接口,都让业务js依赖组件js,是不是好些?
本来写了“赞”的... 想想还是建议大家单纯的“赞”就不用写啦,直接watch+star就好,提高效率,节省大家的时间... ~= ̄ω ̄=~
觉得前端和NODE的资源管理好头疼, rails的Asset pipeline 非常好,根本不要写啥task
command
+ ↑
gotop
厉害,厉害,厉害!
受教了!!!
牛~~
mark,最近正在实践相关的东西。
赞,很不错的文章
看得爽!!!!!!!!!!!!!!!!!!!
非常感谢这篇文章,让我认清前端工程的重要性,作为一个学生党我的项目还一直停留在GUI设计的理念上,看完这些文章我一定要动手尝试下 :)
hi,云龙,提个问题: 如果采用非覆盖式发布,线上public目录编译的静态资源会越来越多,怎么去解决这个问题呢?
/static/css/button.de33108.js
应该是 /static/css/button.de33108.css
吧? @fouber
@ourai 看得好仔细。。。
学习了,多谢。
启发很大,实践了一个月,目前已经使用 webpack + gulp 实现了一套这样的架构。 有时间再总结一下,供大家参考。
@weilao 赶紧啊,今天刚开始使用webpack。 ( ╯□╰ )
@weilao 现在我这边公司也打算用webpack + gulp 实现,坐等分享。强烈希望能和vuejs结合使用!
你好, 请教一个问题.
react 目前我的感觉是通过获取 js 在客户端渲染, 适合做单页应用. views 成了引用js的容器. 那么传统的服务端端渲染 怎么搭配 react 使用呢?
@yinzSE react是客户端技术. 没有说不能喝传统渲染配合吧 传统服务器在后端渲染了后,在前端也可以继续渲染的.
赞,在龙老这里学到好多工程理念上的东西了
这么久还不更新,说好的其他篇呢
你好,切图仔。
不知道你的团队如何定义前端开发,据我所知,时至今日仍然有很多团队会把前端开发归类为产品或者设计岗位,虽然身份之争多少有些无谓,但我对这种偏见还是心存芥蒂,酝酿了许久,决定写一个系列的文章,试着从工程的角度系统的介绍一下我对前端,尤其是Web前端的理解。
只要我们还把自己的工作看作为一项软件开发活动,那么我相信读过下面的内容你也一定会有所共鸣。
前端,是一种GUI软件
现如今前端可谓包罗万象,产品形态五花八门,涉猎极广,什么高大上的基础库/框架,拽炫酷的宣传页面,还有屌炸天的小游戏……不过这些一两个文件的小项目并非是前端技术的主要应用场景,更具商业价值的则是复杂的Web应用,它们功能完善,界面繁多,为用户提供了完整的产品体验,可能是新闻聚合网站,可能是在线购物平台,可能是社交网络,可能是金融信贷应用,可能是音乐互动社区,也可能是视频上传与分享平台……
如此复杂的Web应用,动辄几十上百人共同开发维护,其前端界面通常也颇具规模,工程量不亚于一般的传统GUI软件:
尽管Web应用的复杂程度与日俱增,用户对其前端界面也提出了更高的要求,但时至今日仍然没有多少前端开发者会从软件工程的角度去思考前端开发,来助力团队的开发效率,更有甚者还对前端保留着”如玩具般简单“的刻板印象,日复一日,刀耕火种。
历史悠久的前端开发,始终像是放养的野孩子,原始如斯,不免让人慨叹!
前端工程的三个阶段
现在的前端开发倒也并非一无所有,回顾一下曾经经历过或听闻过的项目,为了提升其前端开发效率和运行性能,前端团队的工程建设大致会经历三个阶段:
第一阶段:库/框架选型
前端工程建设的第一项任务就是根据项目特征进行技术选型。
基本上现在没有人完全从0开始做网站,哪怕是政府项目用个jquery都很正常吧,React/Angularjs等框架横空出世,解放了不少生产力,合理的技术选型可以为项目节省许多工程量这点毋庸置疑。
第二阶段:简单构建优化
选型之后基本上就可以开始敲码了,不过光解决开发效率还不够,必须要兼顾运行性能。前端工程进行到第二阶段会选型一种构建工具,对代码进行压缩,校验,之后再以页面为单位进行简单的资源合并。
前端开发工程化程度之低,常常出乎我的意料,我之前在百度工作时是没有多少概念的,直到离开大公司的温室,去到业界与更多的团队交流才发现,能做到这个阶段在业界来说已然超出平均水平,属于“具备较高工程化程度”的团队了,查看网上形形色色的网页源代码,能做到最基本的JS/CSS压缩的Web应用都已跨入标准互联网公司行列,不难理解为什么很多前端团队对于前端工程构建的认知还仅停留在“压缩、校验、合并”这种程度。
第三阶段:JS/CSS模块化开发
分而治之是软件工程中的重要思想,是复杂系统开发和维护的基石,这点放在前端开发中同样适用。在解决了基本开发效率运行效率问题之后,前端团队开始思考维护效率,模块化是目前前端最流行的分治手段。
JS模块化方案很多,AMD/CommonJS/UMD/ES6 Module等,对应的框架和工具也一大堆,说起来很烦,大家自行百度吧;CSS模块化开发基本都是在less、sass、stylus等预处理器的import/mixin特性支持下实现的。
虽然这些技术由来已久,在如今这个“言必及React”的时代略显落伍,但想想业界的绝大多数团队的工程化落后程度,放眼望去,毫不夸张的说,能达到第三阶段的前端团队已属于高端行列,基本具备了开发维护一般规模Web应用的能力。
然而,做到这些就够了么?Naive!
第四阶段
当我们要开发一款完整的Web应用时,前端将面临更多的工程问题,比如:
这些无疑是一系列严肃的系统工程问题。
前面讲的三个阶段虽然相比曾经“茹毛饮血”的时代进步不少,但用于支撑第四阶段的多人合作开发以及精细的性能优化似乎还欠缺点什么。
到底,缺什么呢?
没有银弹
读过《人月神话》的人应该都听说过,软件工程 没有银弹。没错,前端开发同样没有银弹,可是现在是连™铅弹都没有的年月!(刚有了BB弹,摔)
前端历来以“简单”著称,在前端开发者群体中,小而美的价值观占据着主要的话语权,甚至成为了某种信仰,想与其他人交流一下工程方面的心得,得到的回应往往都是两个字:太重。
工程方案其实也可以小而美!只不过它的小而美不是指代码量,而是指“规则”。找到问题的根源,用最少最简单明了的规则制定出最容易遵守最容易理解的开发规范或工具,以提升开发效率和工程质量,这同样是小而美的典范!
2011年我有幸参与到 FIS 项目中,与百度众多大中型项目的前端研发团队共同合作,不断探索实践前端开发的工程化解决方案,13年离开百度去往UC,面对完全不同的产品形态,不同的业务场景,不同的适配终端,甚至不同的网络环境,过往的方法论仍然能够快速落地,为多个团队的不同业务场景量身定制出合理的前端解决方案。
这些经历让我明悟了一个道理:
分治的确是非常重要的工程优化手段。在我看来,前端作为一种GUI软件,光有JS/CSS的模块化还不够,对于UI组件的分治也有着同样迫切的需求:
如上图,这是我所信仰的前端组件化开发理念,简单解读一下:
其中第二项描述的就近维护原则,是我觉得最具工程价值的地方,它为前端开发提供了很好的分治策略,每个开发者都将清楚的知道,自己所开发维护的功能单元,其代码必然存在于对应的组件目录中,在那个目录下能找到有关这个功能单元的所有内部逻辑,样式也好,JS也好,页面结构也好,都在那里。
组件化开发具有较高的通用性,无论是前端渲染的单页面应用,还是后端模板渲染的多页面应用,组件化开发的概念都能适用。组件HTML部分根据业务选型的不同,可以是静态的HTML文件,可以是前端模板,也可以是后端模板:
基于这样的工程理念,我们很容易将系统以独立的组件为单元进行分工划分:
由于系统功能被分治到独立的模块或组件中,粒度比较精细,组织形式松散,开发者之间不会产生开发时序的依赖,大幅提升并行的开发效率,理论上允许随时加入新成员认领组件开发或维护工作,也更容易支持多个团队共同维护一个大型站点的开发。
结合前面提到的模块化开发,整个前端项目可以划分为这么几种开发概念:
以上5种开发概念以相对较少的规则组成了前端开发的基本工程结构,基于这些理念,我眼中的前端开发就成了这个样子:
组件的JS可依赖其他JS模块,
CSS可依赖其他CSS单元
综合上面的描述,对于一般中小规模的项目,大致可以规划出这样的源码目录结构:
如果项目规模较大,涉及多个团队协作,还可以将具有相关业务功能的页面组织在一起,形成一个子系统,进一步将整个站点拆分出多个子系统来分配给不同团队维护,针对这种情况后面我会单开文章详细介绍。
以上架构设计历经许多不同公司不同业务场景的前端团队验证,收获了不错的口碑,是行之有效的前端工程分治方案。
上面提到的模块化/组件化开发,仅仅描述了一种开发理念,也可以认为是一种开发规范,倘若你认可这规范,对它的分治策略产生了共鸣,那我们就可以继续聊聊它的具体实现了。
很明显,模块化/组件化开发之后,我们最终要解决的,就是模块/组件加载的技术问题。然而前端与客户端GUI软件有一个很大的不同:
前端应用没有安装过程,其所需程序资源都部署在远程服务器,用户使用浏览器访问不同的页面来加载不同的资源,随着页面访问的增加,渐进式的将整个程序下载到本地运行,“增量下载”是前端在工程上有别于客户端GUI软件的根本原因。
上图展示了一款界面繁多功能丰富的应用,如果采用Web实现,相信也是不小的体量,如果用户第一次访问页面就强制其加载全站静态资源再展示,相信会有很多用户因为失去耐心而流失。根据“增量”的原则,我们应该精心规划每个页面的资源加载策略,使得用户无论访问哪个页面都能按需加载页面所需资源,没访问过的无需加载,访问过的可以缓存复用,最终带来流畅的应用体验。
这正是Web应用“免安装”的魅力所在。
由“增量”原则引申出的前端优化技巧几乎成为了性能优化的核心,有加载相关的按需加载、延迟加载、预加载、请求合并等策略;有缓存相关的浏览器缓存利用,缓存更新、缓存共享、非覆盖式发布等方案;还有复杂的BigRender、BigPipe、Quickling、PageCache等技术。这些优化方案无不围绕着如何将增量原则做到极致而展开。
所以我觉得:
相信这种贯彻不会随着时间的推移而改变,在可预见的未来,无论在HTTP1.x还是HTTP2.0时代,无论在ES5亦或者ES6/7时代,无论是AMD/CommonJS/UMD亦或者ES6 module时代,无论端内技术如何变迁,我们都有足够充分的理由要做好前端程序资源的增量加载。
正如前面说到的,第三阶段前端工程缺少点什么呢?我觉得是在其基础架构中缺少这样一种“智能”的资源加载方案。没有这样的方案,很难将前端应用的规模发展到第四阶段,很难实现落地前面介绍的那种组件化开发方案,也很难让多方合作高效率的完成一项大型应用的开发,并保证其最终运行性能良好。在第四阶段,我们需要强大的工程化手段来管理”玩具般简单“的前端开发。
在我的印象中,Facebook是这方面探索的伟大先驱之一,早在2010年的Velocity China大会上,来自Facebook的David Wei博士就为业界展示了他们令人惊艳的静态网页资源管理和优化技术。
David Wei博士在当年的交流会上提到过一些关于Facebook的一些产品数据:
这是一个状态爆炸的问题,将所有状态乘起来,整个网站的资源组合方式会达到几百万种之多(去重之后统计大概有300万种组合方式)。支撑这么大规模前端项目运行的底层架构正是魏博士在那次演讲中分享的Static Resource Management System(静态资源管理系统),用以解决Facebook项目中有关前端工程的3D问题(Development,Deployment,Debugging)。
那段时间 FIS 项目正好遇到瓶颈,当时的FIS还是一个用php写的task-based构建工具,那时候对于前端工程的认知度很低,觉得前端构建不就是几个压缩优化校验打包任务的组合吗,写好流程调度,就针对不同需求写插件呗,看似非常简单。但当我们支撑越来越多的业务团队,接触到各种不同的业务场景时,我们深刻的感受到task-based工具的粗糙,团队每天疲于根据各种业务场景编写各种打包插件,构建逻辑异常复杂,隐隐看到不可控的迹象。
我们很快意识到把基础架构放到构建工具中实现是一件很愚蠢的事,试图依靠构建工具实现各种优化策略使得构建变成了一个巨大的黑盒,一旦发生问题,定位起来非常困难,而且每种业务场景都有不同的优化需求,构建工具只能通过静态分析来优化加载,具有很大的局限性,单页面/多页面/PC端/移动端/前端渲染/后端渲染/多语言/多皮肤/高级优化等等资源加载问题,总不能给每个都写一套工具吧,更何况这些问题彼此之间还可以有多种组合应用,工具根本写不过来。
Facebook的做法无疑为我们亮起了一盏明灯,不过可惜它并不开源(不是技术封锁,而是这个系统依赖FB体系中的其他方面,通用性不强,开源意义不大),我们只能尝试挖掘相关信息,网上对它的完整介绍还是非常非常少,分析facebook的前端代码也没有太多收获,后来无意中发现了facebook使用的项目管理工具phabricator中的一个静态管理方案Celerity,以及相关的说明,看它的描述很像是Facebook静态资源管理系统的一个mini版!
简单看过整个系统之后发现原理并不复杂(小而美的典范),它是通过一个小工具扫描所有静态资源,生成一张资源表,然后有一个PHP实现的资源管理框架(Celerity)提供了资源加载接口,替代了传统的script/link等静态的资源加载标签,最终通过查表来加载资源。
虽然没有真正看过FB的那套系统,但眼前的这个小小的框架给了当时的我们足够多的启示:
多么优雅的实现啊!
资源表是一份数据文件(比如JSON),是项目中所有静态资源(主要是JS和CSS)的构建信息记录,通过构建工具扫描项目源码生成,是一种k-v结构的数据,以每个资源的id为key,记录了资源的类别、部署路径、依赖关系、打包合并等内容,比如:
而资源加载框架则提供一些资源引用的API,让开发者根据id来引用资源,替代静态的script/link标签来收集、去重、按需加载资源。调用这些接口时,框架通过查表来查找资源的各项信息,并递归查找其依赖的资源的信息,然后我们可以在这个过程中实现各种性能优化算法来“智能”加载资源。
根据业务场景的不同,加载框架可以在浏览器中用JS实现,也可以是后端模板引擎中用服务端语言实现,甚至二者的组合,不一而足。
这种设计很快被验证具有足够的灵活性,能够完美支撑不同团队不同技术规范下的性能优化需求,前面提到的按需加载、延迟加载、预加载、请求合并、文件指纹、CDN部署、Bigpipe、Quickling、BigRender、首屏CSS内嵌、HTTP 2.0服务端推送等等性能优化手段都可以很容易的在这种架构上实现,甚至可以根据性能日志自动进行优化(Facebook已实现)。
因为有了资源表,我们可以很方便的控制资源加载,通过各种手段在运行时计算页面的资源使用情况,从而获得最佳加载性能。无论是前端渲染的单页面应用,还是后端渲染的多页面应用,这种方法都同样适用。
此外,它还很巧妙的约束了构建工具的职责——只生成资源表。资源表是非常通用的数据结构,无论什么业务场景,其业务代码最终都可以被扫描为相同结构的表数据,并标记资源间的依赖关系,有了表之后我们只需根据不同的业务场景定制不同的资源加载框架就行了,从此彻底告别一个团队维护一套工具的时代!!!
深耕静态资源加载框架可以带来许多收益,而且有足够的灵活性和健壮性面向未来的技术变革,这个我们留作后话。
总结
回顾一下前面提到过的前端工程三个阶段:
现在补充上第四阶段:
由于先天缺陷,前端相比其他软件开发,在基础架构上更加迫切的需要组件化开发和资源管理,而解决资源管理的方法其实一点也不复杂:
近几年来各种你听到过的各种资源加载优化策略大部分都可以在这样一套基础上实现,而这种优化对于业务来说是完全透明的,不需要重构的性能优化——这不正是我们一直所期盼的吗?正如魏小亮博士所说:我们可以把优秀的人集中起来去优化加载。
如何选型技术、如何定制规范、如何分治系统、如何优化性能、如何加载资源,当你从切图开始转变为思考这些问题的时候,我想说:
你好,工程师!
相关文章:(注: 以下文章还在占坑中, 作者还未完成)