对原理不关心,直接跳到使用指南
Web 前端项目开发需要依赖一揽子辅助工具和约定,来保持代码的快速流转和可维护性。
一套完整的前端技术基础设施包括六个方面:
这类项目在航旅事业部、整个阿里占绝大部分,有两个直接的影响:
对于第二类项目,让我们再多了解一些被人熟知的真相:
因此,项目和关键功能碎片化之后更要开源(企业内部开源),让你的代码可以被“种”在大量的页面、角落中,并可以自己生长(被fork二次开发)。让代码单元以多种形态(服务、组件、模块)运行在线上。
以下结论摘自《编写可维护的JavaScript》:
这种样子的产品要求代码的可抽离性要高,这时每个小项目(单元)的Demo就显得意义不大了(本地Demo仅用作开发过程的管理、归档、历史版本迭代用),因为线上环境是最好的 Demo。
前端工程师拥有大部分的HTML的发布权限和所有的JS/CSS/IMG的发布权限。HTML片段参与组成线上页面。基于HTML和CSS/JS分离的原则,前端工程师会将更多的时间用户修补JS/CSS而非HTML。因此JS和CSS的重要性大于HTML。
传统上前端工作里“项目”的概念远没有后台软件开发领域里那么清晰,这主要是由于以往的客户端页面较简单,不需要太多“项目”层面的支持。随着现在客户端功能的越趋复杂,有必要系统的来引入一套针对前端业务特点构建的架构模式。
Clam 里对于项目的定义是一个完整的前端应用,或其中一个相对独立的某一个业务场景。如:一个单页富应用,可以作为一个 Clam 项目;或者业务耦合度较高,用户使用路径很近的一组页面,也可以作为一个 Clam 项目。
对于一个 Clam 项目的具体页面,除了页面自身的html模板,样式和脚本外,它还可以引用一组模块,其中每个模块都有其独立的html模板,样式和脚本文件。同时页面上还可以存在一些通用的组件,
实际上,定义清楚模块是万里长征的第一步,任何项目初始状态的模块划分始终是最清晰。在互联网开发实际场景中,页面信息碎片化过程中渐渐失去了结 构,如果模块代码没有真正独立(成单独仓库)、而是长在项目的某个目录中,松散的页面信息结构最终导致项目代码失控。而每个模块独占一个项目,强行将代码 独立,不仅要求开发者具备更强的代码抽象能力,更从根本上避免“弱约定”的弊病("弱约定"很容易被紧急多变的需求击碎)。
这是我们无法在项目中完全高保真还原Demo的根本原因:互联网信息碎片化总会导致代码的熵变大。因此,往往开发者拿到一段“局部”代码,去 debug 它在另外一个项目中嵌入后的页面。因此,HTML和CSS/JS的分离尤为重要。我们不要纠结 Demo 无法高保真的还原。
Grunt-flexcombo 是一款基于NodeJS的轻服务,便携且易于配置。用于淘系环境中的Demo服务和自定义虚机等场景。
启动服务后,绑定设备,以下两种方法取其中一个,推荐第二种:
然后可以直接通过
1,将示例代码检出
将仓库检出到任意目录:
进入到刚检出的目录中:
在
4,打开浏览器,绑定HTTP代理到本机
5,浏览器中访问这个URL
from https://github.com/jayli/generator-clam
什么是 Generator-Clam
阿里航旅前端基础设施:Web 前端项目开发需要依赖一揽子辅助工具和约定,来保持代码的快速流转和可维护性。
一套完整的前端技术基础设施包括六个方面:
- 前端框架(Kissy)
- 代码骨架(Clam)
- 构建工具(Grunt)
- 服务和环境(Grunt-flexcombo)
- 模块规范和公共代码仓库(PI)
- 项目的开源开发模式(Git)
为什么开源这么重要!!!
Web 项目大致分三类:- 结构精密,业务架构性强的 Web 应用,又称 OPOA(One Page One App),比如旅行计划
- 结构松散,需求变更频繁的页面级开发,比如旅行首页和淘宝首页。
- 组件代码,代码发布出来就是要被别的页面引用的。
这类项目在航旅事业部、整个阿里占绝大部分,有两个直接的影响:
- 接手别人的项目成本高,前端开发资源无法形成有效互补,表现在“前端资源永远不够用”
- 跨业务的合作项目成本高,2013年酒店类目接入天猫和淘主站,不一致的编程约定、环境、代码结构,导致一半的项目时间浪费在这上。最后的表现还是“前端资源不够用”。
对于第二类项目,让我们再多了解一些被人熟知的真相:
1. Web 前端技术的特点
表现层代码隶属Web技术栈最顶层,受需求变化影响最直接,大量改版项目也导致页面代码频繁重写。因此一个由零散的信息碎片拼装而成的页面,天然不成架构。这会导致代码无法顺利流转、项目交接困难。因此,项目和关键功能碎片化之后更要开源(企业内部开源),让你的代码可以被“种”在大量的页面、角落中,并可以自己生长(被fork二次开发)。让代码单元以多种形态(服务、组件、模块)运行在线上。
2. 互联网开发周期特点
以下结论摘自《编写可维护的JavaScript》:
- 软件声明周期中80%的成本消耗在了维护上
- 几乎所有的软件维护者都不是它的最初作者
- 编码规范提高可读性,让工程师快速理解新的代码
- 源码应当作为产品来一部分来发布,是完整可打包的。
很少有人会打开代码编辑器从零开始写代码,多数时间你面对的是写好的代码... 正如我在Yahoo常说的:“当你开始工作时,不是在给你自己写代码,而是为后来人写代码”不幸的是,我们在日常项目中痛苦的适应着这种局面,痛苦的对已有的线上脏代码打补丁,而这些线上代码几乎全无文档、无规范、无交接人。因此开源会:
——N.C.Zakas
- 将重要的、通用的功能用版本管理工具收纳起来
- 源码的结构、构建方式、约定,作为技术文档的一部分沉淀下来
- 更方便、透明的代码合并,一定程度用代码描述功能需求
- debug 源码代替直接 debug 线上代码
3. Web项目特点
互联网产品不是一次性完工,是经过很多次迭代、不同项目的代码堆积而成的。因此产品的技术架构本身要支持模块拼装,模块以项目为单位,可装可卸。最大的难题在于:- 集成测试
- 依赖真实环境的开发(本地测试)
- 真实环境中的debug某个项目(单元)
这种样子的产品要求代码的可抽离性要高,这时每个小项目(单元)的Demo就显得意义不大了(本地Demo仅用作开发过程的管理、归档、历史版本迭代用),因为线上环境是最好的 Demo。
项目(Assets)前端代码发布流程
基本流程
前端工程师拥有大部分的HTML的发布权限和所有的JS/CSS/IMG的发布权限。HTML片段参与组成线上页面。基于HTML和CSS/JS分离的原则,前端工程师会将更多的时间用户修补JS/CSS而非HTML。因此JS和CSS的重要性大于HTML。
CDN 的 Combo 服务
Combo是淘系CDN提供的基础服务,动态输出CDN里的颗粒文件。比如:http://g.tbcdn.cn/path/??a.js,b.js
将输出a.js
和b.js
两个文件Generator-Clam 遵循的原则
Generator-Clam
我们简称Clam
,Clam
志在为前端工程师提供更简单和一致的项目开发体验。Clam 遵循这些易于理解的原则:- Server 服务随源码携带
- Assets 的源码和目标代码同时发布,比如
a-min.js
一定存在其源码a.js
。 - CDN 代码非覆盖式发布,发布基于项目版本
x.y.z
,而非文件版本,比如线上代码为0.1.2/a.js
而非a-0.1.2.js
- 项目均包含描述文件
Gruntfile.js
,用于组织 Grunt 构建和定义本地 Server 服务 - 共享代码通过
bower install {moduleName}
的形式安装和更新 - 项目结构代码骨架统一:Pages+Mods+widgets
Generator-Clam 模块化开发
模块定义
Generator-Clam
非常激进的提供了从底层架构层面对于前端模块化开发的支持。先来了解一下Generator-Clam
对于前端项目的理解。传统上前端工作里“项目”的概念远没有后台软件开发领域里那么清晰,这主要是由于以往的客户端页面较简单,不需要太多“项目”层面的支持。随着现在客户端功能的越趋复杂,有必要系统的来引入一套针对前端业务特点构建的架构模式。
Clam 里对于项目的定义是一个完整的前端应用,或其中一个相对独立的某一个业务场景。如:一个单页富应用,可以作为一个 Clam 项目;或者业务耦合度较高,用户使用路径很近的一组页面,也可以作为一个 Clam 项目。
对于一个 Clam 项目的具体页面,除了页面自身的html模板,样式和脚本外,它还可以引用一组模块,其中每个模块都有其独立的html模板,样式和脚本文件。同时页面上还可以存在一些通用的组件,
- 一个Clam项目由若干页面Page、模块Module、和组件Widget构成;
- 其中Page和Module可以由html,css,js等组成;Widget对外提供的代码通常只有JS/CSS;
- Page可以在其html里包含Module的html文件来使用模块;
- Module通常是与业务关系较密切的独立功能块,比如一个订票网站的常用联系人模块;
- Module应做到尽可能独立,最理想的情况是完全独立于页面;
- Widget是与具体业务耦合较松,复用性更强的功能块,如日历组件;
- Page的html部分通常是用来包含模块html或为组件提供容器的。
.hello_pro
├── build/
├── Gruntfile.js
└── src
├── config.js
├── mods
│ └── def
│ ├── demo.html
│ ├── index.css
│ ├── index.html
│ └── index.js
└── pages
└── abc
├── index.css
├── index.html
└── index.js
其中src/pages/abc/index.html
内容:<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title> H5Test - Abc demo </title>
<script src="http://g.tbcdn.cn/kissy/k/1.4.0/seed-min.js"></script>
<link rel="stylesheet" href="index.css"/>
<script src="../??config.js"></script>
</head>
<body>
<!--#include virtual="../../mods/def/index.html" -->
<script>
KISSY.use('h5-test/abc/index', function(S, Abc) {
new Abc();
});
</script>
</body>
</html>
因为页面中需给JS传参,通过new Abc({JSONData})
将数据传递给Abc模块,不要通过全局变量给JS传参。src/mods/def/index.html
内容为:<link href="../mods/def/index.css" rel="stylesheet" />
模块正文
<script>
KISSY.use('h5-test/def/index', function(S, Def) {
new Def();
});
</script>
启动本地demo服务grunt demo
,浏览器绑定HTTP服务127.0.0.1:8080
,访问demo/pages/abc/index.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title> H5Test - Abc demo </title>
<script src="http://g.tbcdn.cn/kissy/k/1.4.0/seed-min.js"></script>
<link rel="stylesheet" href="index.css"/>
<script src="../??config.js"></script>
</head>
<body>
<link href="../mods/def/index.css" rel="stylesheet" />
模块正文
<script>
KISSY.use('h5-test/def/index', function(S, Def) {
new Def();
});
</script>
<script>
KISSY.use('h5-test/abc/index', function(S, Abc) {
new Abc();
});
</script>
</body>
</html>
Clam 推荐将模块所有的外部依赖都在模块html文件内引入,这样可以做到模块的完全独立。同样的,你毋需担心引入诸如jQuery,CSS Reset等文件带来的麻烦。Clam会在项目build阶段做排重处理,确保你最终打包后的页面一个资源只引入一次。模块在线上环境中的拼装和组合
除了业务架构本身就很强的Web富应用开发外,前端开发最大的挑战在于“门户”型页面的架构、和组队开发。实际上,定义清楚模块是万里长征的第一步,任何项目初始状态的模块划分始终是最清晰。在互联网开发实际场景中,页面信息碎片化过程中渐渐失去了结 构,如果模块代码没有真正独立(成单独仓库)、而是长在项目的某个目录中,松散的页面信息结构最终导致项目代码失控。而每个模块独占一个项目,强行将代码 独立,不仅要求开发者具备更强的代码抽象能力,更从根本上避免“弱约定”的弊病("弱约定"很容易被紧急多变的需求击碎)。
这是我们无法在项目中完全高保真还原Demo的根本原因:互联网信息碎片化总会导致代码的熵变大。因此,往往开发者拿到一段“局部”代码,去 debug 它在另外一个项目中嵌入后的页面。因此,HTML和CSS/JS的分离尤为重要。我们不要纠结 Demo 无法高保真的还原。
无论如何,前端项目中的Demo都无法做到高保真还原
项目的构建
Clam 项目构建基于Grunt,构建任务作为插件配置到Gruntfile.js
中。这些任务包括:-
Grunt-combohtml
- 合并SSI的html,抽取页面中分散的JS和CSS,合并好后输出;
- 解析原html中的juicer模板,生成vm、tms或者php。
-
Grunt-kmc,JS库代码依赖KISSY,使用KISSY打包工具来解析源码中JS的依赖关系,生成
map.js
或者静态合并。 - Grunt-uglify、Grunt-cssmin、Grunt-replace、Grunt-less,
Grunt-css_combo
- Grunt-mytps,上传本地图片到tps服务器,依赖python(默认不开启,开发者自行配置)
- Grunt-toascii,把文件中的非英文字符转码成对应的ascii码(默认不开启,开发者自行配置)
- Grunt-cssimage,对css文件中的图片进行压缩替换,支持远程图片抓取(默认不开启,开发者自行配置)
- Grunt-tms,将Juicer模板文件转换为TMS格式。
基于 NodeJS 的本地 Server
本地 Server 跟随代码安装和启动,以 Grunt 插件形式提供。本地服务原理
Grunt-flexcombo 是一款基于NodeJS的轻服务,便携且易于配置。用于淘系环境中的Demo服务和自定义虚机等场景。
Grunt-flexcombo 是 flex-combo 的grunt插件版本,Grunt-Flexcombo 原理介绍。
本地服务的启动
基于Clam生成的项目目录运行grunt debug
,将会启用flexcombo
服务,会在本地启动两个 Server 服务,两个服务分属两个端口proxyport
(反向代理服务)和port
(Flexcombo 模拟 CDN 环境)- 反向代理服务:用于启用本地虚机
-
flexcombo服务:映射 CDN Combo 请求中的某个文件到本地:
http://cdn/??a.js,b.js
启动服务后,绑定设备,以下两种方法取其中一个,推荐第二种:
将cdn配向本机127.0.0.1 g.tbcdn.cn a.tbcdn.cn
- 将浏览器或者设备HTTP代理配置到本机的反向代理服务的端口
然后可以直接通过
g.tbcdn.cn
域名来预览本地文件http://g.tbcdn.cn/{group}/{project}
推荐使用proxyHosts中的虚机名来访问本地文件列表http://demo.com/
flexcombo服务可以配合watch和你Gruntfile.js中的构建命令,完成代码调试,比如在Gallery中的代码调试,更多内容参照:grunt-flexcombo 配置方法。
Flexcombo 服务启动后如何映射 Combo URL 里的文件
我们通过一个案例来说明原理:1,将示例代码检出
将仓库检出到任意目录:
git clone https://github.com/jayli/grunt-flexcombo.git
2,补全node_modules
进入到刚检出的目录中:
cd grunt-flexcombo/test
npm install
3,启动本地服务在
test
目录中执行sudo grunt demo
这时服务启动。4,打开浏览器,绑定HTTP代理到本机
5,浏览器中访问这个URL
http://g.tbcdn.cn/??test/index.js,kissy/k/1.4.0/seed.js
可以看到本地文件test/index.js
和线上文件kissy/k/1.4.0/seed.js
一并输出。这时,在命令行窗口可以看到请求log.from https://github.com/jayli/generator-clam