Clam
是什么
Clam
是一套基于 Node.js 的前端工程项目开发环境。
Clam
内部集成了本地开发服务器、前端模块化开发管理、打包和发布管理等功能。Clam
志在为前端工程师提供更简单和一致的项目开发体验。
Clam
的安装
安装Clam
最简单的方式是通过 Node.js 提供的包管理工具npm
来安装:
npm -g install clam
# Mac 和 Linux 环境下可能需要 sudo 权限
# 注意:Windows 平台下请使用原生命令行环境,不要在 Cygwin 下安装。
开始使用 Clam
Clam
的使用非常简单,首先需要新建一个空的项目目录,例如hello_clam
:
mkdir hello_clam
cd hello_clam
然后在此目录下执行Clam
的项目初始化命令:
clam init
这条命令会在项目目录下生成一套标准的Clam
项目目录结构:
hello_clam
- build
- src
- pages
- mods
- widgets
- tests
- .clam
- project.json
# src: 项目源文件目录,包括 html 模板、样式、脚本、图片资源等;
# tests: 项目测试脚本;
# build: 项目打包发布上线的目标文件;
# .clam: 项目元信息,project.json 为项目配置文件。
其中在项目开发阶段核心工作目录是src
。其目录结构对应了Clam
提供的一套标准的前端模块化开发架构(基于Page,Module,Widget的分层模块开发,具体参考Clam
模块化开发章节)。
对于一个简单的项目,只需将所有文件都丢到pages
目录,并以相对路径在html
文件里引入所需样式和脚本即可。不用担心,在build
阶段Clam
会自动将项目html
文档里使用的相对路径替换为需要发布的线上路径。只需要一个简单的配置项cdnPath
:
# 在项目配置文件 .clam/project.json 里修改 cdnPath
"cdnPath": "http://yourcdn.com/yourproject"
# >> 这样项目打包时就会将相对路径替换为指定的`cdnPath`
# 注意:结尾不要附加斜杠
完成开发后,就可以将项目文件打包为线上发布版本:
clam build server v1
Clam
打包操作只需要传入一个版本号参数,上面的命令将在工程的build
目录里生成打包后的v1
版本。
至此使用Clam
就完成了一个最基本的前端项目的开发。
关于Clam
提供的更多功能:
- 本地资源文件代理(支持
combo
功能) - 接口模拟
- 模块化开发管理
- 打包和发布管理
- 项目 Hosts 管理
- 基于模板创建页面和模块
- 嗯,还有一个专为不喜欢命令行操作的工程师打造的 GUI 配置管理界面
强烈推荐继续阅读。
Clam
本地文件代理
Clam
本地文件代理提供了一个方便调试测试或线上问题的机制。其工作原理很简单,当你需要调试一个测试或线上页面如hello_clam.html
时,首先找到该页面所属的项目hello_clam
。确保已经配置了项目的cdnPath
,并将文件服务器域名指向本机:
# 在 hosts 文件中将文件服务器指向本机
127.0.0.1 yourcdn.com
然后开启或切换到此项目的 Clam 环境:
cd hello_clam
clam on
此时再访问 hello_clam.html
页面时,Clam
内置的本地服务器会将此文件所引用的样式和脚本资源替换为该项目目录的源文件。
Tips~ 对于手机端检测FPS,手势和console.log的处理可以用 clam on wap
进行注入。并在电脑端打开http://clam.com进行查看数据
值得一提的是Clam
的文件代理功能提供了combo
支持,其中默认支持的combo
格式以?
作为servlet
,以,
作为文件分隔符。如果需要定制combo
,可以到用户根目录的~/.flex-combo/config.json
配置文件里更改flex-combo
的配置。
flex-combo
是一个提供本地combo
服务的Node.js
模块。
Clam
本身的combo
功能也是基于此模块实现的。
关于flex-combo
的详细信息参见 flex-combo官方文档。
Clam
接口模拟
依托于Node.js
,Clam
提供了方便且强大的接口模拟能力。
要在Clam
里模拟一个接口,只需要在配置文件project.json
的json
字段增加如下信息:
"json": [
{
"url": "/api/fake_api", // 需要模拟的接口路径
"enabled": "local", // 转发到远程服务器或者本地处理
"remote": "remotehost.com", // 远程服务器
"local": "local_a.js" // 本地处理脚本
},
{
"url": "/api/", // 需要模拟的接口路径
"enabled": "remote", // 转发到远程服务器或者本地处理
"remote": "remotehost.com", // 远程服务器
"local": "local_b.js" // 本地处理脚本
}
]
新增功能:Add by Ryota 良田
- 通过
dataApi
方式进行本地自动代理。模拟接口。enabled参数分别为 auto
local
remote
- 若设置为
auto
则为自动查找模式。优先本地查找。本地文件对应目录为 ${root}/.clam/dataApi
- dataApi如果
exports.handle
存在,并且是一个函数。则认定用户需要自助解决数据返回情况。其它认为反回数据为json
- 参数
remote
由以往的域名模式改为完整地址模式。 匹配正则为
// 'https://www.taobao.com:80/a/d/b?a=1&b=2'.match(/^(?:(http(?:s*)|socket):\/\/)*([^\:\/]+)*(?:\:(\d+))*(\/[^?]*)(?:\?(.*))*$/)
// ["https://www.taobao.com:80/a/d/b?a=1&b=2", "https", "www.taobao.com", "80", "/a/d/b", "a=1&b=2"]
// 'socket://www.taobao.com:80/a/d/b?a=1&b=2'.match(/^(?:(http(?:s*)|socket):\/\/)*([^\:\/]+)*(?:\:(\d+))*(\/[^?]*)(?:\?(.*))*$/)
// ["socket://www.taobao.com:80/a/d/b?a=1&b=2", "socket", "www.taobao.com", "80", "/a/d/b", "a=1&b=2"]
// 'socket://www.taobao.com:80/a/d/b?a=1&b=2'.match(/^(?:(http(?:s*)|socket):\/\/)*([^\:\/]+)*(?:\:(\d+))*(\/[^?]*)(?:\?(.*))*$/)
目前仅支持http
. https
socket
等待完善中。
"dataApi" : [
{
"url": "/api/fake_api", // 需要模拟的接口路径
"enabled": "local", // 转发到远程服务器或者本地处理
"remote": "http://remotehost.com:9023/s/a?a=1&b=2", // 远程服务器
"local": "local_a.js" // 本地处理脚本
},
{
"url": "/api/", // 需要模拟的接口路径
"enabled": "remote", // 转发到远程服务器或者本地处理
"remote": "remotehost.com", // 远程服务器
"local": "local_b.js" // 本地处理脚本
}
]
其中url
是需要匹配的接口路径(最长路径匹配);enabled
用来控制接口本地处理还是做远程转发;remote
和local
字段分别指定了远程转发服务器地址和本地处理脚本的文件名。本地处理脚本需要放置在项目的.clam/json/
目录下。
假如此时项目请求/api/fake_api
接口,Clam
将调用.clam/json/local_a.js
处理;若请求/api/another_fake_api
则Clam
会将此请求转发到远程服务器remotehost.com
上。
本地处理脚本使用Node.js
内置的http
模块来实现,它提供了一个接收http request
和http response
对象参数的处理函数:
exports = module.exports = function(req, res){
res.end('this is a fake api response~')
return true
}
关于此函数的详细用法请参考Node.js
官方文档的http
模块部分。
Clam
代理配置
在.clam
目录下的 project.json
中增加了一个 proxy
的配置项。关于此配置项的详细说明请参考:Doji配置
Clam
模块化开发
除了一系列的贴心功能外,Clam
还非常激进的提供了从底层架构层面对于前端模块化开发的支持。先来了解一下Clam
对于前端项目的理解。
传统上前端工作里“项目”的概念远没有后台软件开发领域里那么清晰,这主要是由于以往的客户端页面较简单,不需要太多“项目”层面的支持。随着现在客户端功能的越趋复杂,有必要系统的来引入一套针对前端业务特点构建的架构模式。
Clam
里对于项目的定义是一个完整的前端应用,或其中一个相对独立的某一个业务场景。如:一个单页富应用,可以作为一个Clam
项目;或者业务耦合度较高,用户使用路径很近的一组页面,也可以作为一个Clam
项目。
对于一个Clam
项目的具体页面,除了页面自身的html
模板,样式和脚本外,它还可以引用一组模块,其中每个模块都有其独立的html
模板,样式和脚本文件。同时页面上还可以存在一些通用的组件,如下图所示:

- 一个
Clam
项目由若干页面Page
、模块Module
、和组件Widget
构成; - 其中
Page
和Module
可以由html
,css
,js
等组成;Widget
则不应包含html
; Page
可以在其html
里包含Module
的html
文件来使用模块;通过静态包含或动态加载使用Widget
;Module
通常是与业务关系较密切的独立功能块,比如一个订票网站的常用联系人模块;Module
应做到尽可能独立,最理想的情况是完全独立于页面;Widget
是与具体业务耦合较松,复用性更强的功能块,如日历组件;Page
的html
部分通常是用来包含模块html
或为组件提供容器的。
要使用一个模块很简单,只需在页面html
里引用模块html
即可,拿我们的hello_clam
项目做例子,我们目前的项目目录结构为:
hello_clam
- build
- src
- pages
- home.html
- home.css
- home.js
- mods
- say_hello
- say_hello.html
- say_hello.css
_ say_hello.js
- contact
- contact.html
- contact.css
- contact.js
- widgets
- tests
- .clam
- project.json
其中home.html
内容为:
<html>
<head>
<title>hello clam</title>
<link rel="stylesheet" href="home.css" />
<script type="text/javascript" src="home.js"></script>
</head>
<body>
<h1>hello clam!</h1>
<!--#include "/mods/say_hello/say_hello.html"-->
</body>
</html>
say_hello.html
内容为:
<link rel="stylesheet" href="say_hello.css" />
<script type="text/javascript" src="say_hello.js"></script>
<p>Hello! Dear Clam User!</p>
现在启动Clam
后就可以在浏览器里输入127.0.0.1/home.html
,可以看到home.html
页面为嵌入了say_hello.html
的内容:
<html>
<head>
<title>hello clam</title>
<link rel="stylesheet" href="home.css" />
<script type="text/javascript" src="home.js"></script>
</head>
<body>
<h1>hello clam!</h1>
<link rel="stylesheet" href="say_hello.css" />
<script type="text/javascript" src="say_hello.js"></script>
<p>Hello! Dear Clam User!</p>
</body>
</html>
不用担心这样做会导致过多的资源文件请求,在build
阶段Clam
会根据项目配置自动做资源文件合并处理。
Clam
推荐将模块所有的外部依赖都在模块html
文件内引入,这样可以做到模块的完全独立。同样的,你毋需担心引入诸如jQuery
,CSS Reset
等文件带来的麻烦。Clam
会在项目build
阶段做排重处理,确保你最终打包后的页面一个资源只引入一次。
Clam
打包和发布管理
终于可以打,包,啦!
clam build server v1
如你所愿,Clam
打包操作只需要传入一个版本号即可。执行上面的命令Clam
就会在工程的build
目录里生成v1
版本的项目打包文件。
如果需要在脚本中动态加载资源,可以通过$CLAM_VER$
来引用打包阶段传入的版本号变量。
具体说来Clam
在打包阶段执行了如下动作:
- 在
build
目录以传入版本号新建目录 - 根据传入版本替换源文件中的
$CLAM_VER$
变量 - 拼接页面和模块
html
代码 - 页面和模块引入资源文件去重、合并
Clam
GUI 配置管理界面
除了强大快捷的命令行操作外,Clam
还为喜欢图形界面的用户提供了一套基于网页的配置管理界面。
# 启动 Clam GUI 配置管理界面
clam ui
# 注意:可能需要 sudo 权限
目前Clam
的 GUI 管理界面还在持续开发完善中,更多功能敬请期待。
Clam
项目 Hosts 管理
在实际前端工作中,前端开发者一般会同时几个项目。这些项目对Host配置有不同的需求。
Clam
允许在启动clam on
或者clam ui
时自动修改系统hosts文件,完成hosts切换。
有两种方式为Clam
设置Hosts切换规则。
- 执行
sudo clam ui
,通过GUI设置项目的host。 - 修改
.clam/project.json
中hosts
字段的值。
修改完毕后无需重新启动clam,即刻生效。
如果你使用clam管理所有前端项目,当你打开Clam时,系统各种环境就已经准备妥当,可以立即开始开发。
有时候,你只是想让Clam管理部分项目,此时你需要用命令sudo clam gc
恢复hosts到正常状态。
Clam
基于模板创建页面和模块
Clam
提供两个命令行用来自动创建框架页面和模块。以页面为例
clam page <页面名> [模板]
其中<页面名>为必填字段。在输入时,Clam建议你省去.html
的扩展名。当然如果你输入也没关系,clam会处理好。
执行该命令将在项目目录下src/page
创建一个html文件和一个css文件,文件名正是你输入的名称。文件内容已经搭建好一些基本框架:
同理,你可以执行clam mod <模块名> [模板]
或者 clam wideget <组件名> [模板]
来帮助你创建模板和组件框架。
如果你对生成的html文件内容不满意,可以自定义模板。以mac为例,自定义的模板放置在~/.clam/templates/<模板名>
中。
这个目录包含了3个子目录
<模板名>
-page
-mod
-widget
其中page中存放页面模板,也就是执行clam page ...
命令用到的模板。mod中存放页面模板,也就是执行clam mod ...
命令用到的模板。widget中存放页面模板,也就是执行clam widget ...
命令用到的模板。
当执行clam page <页面名> [模板名]
命令后,Clam将:
- 复制
~/.clam/templates/<模板名>/page/
下所有文件到项目目录下src/pages
. - 修改所有命名为
template.*
的文件名为<页面名>.*
。 - 替换所有文件中的变量。模板支持的变量格式如下
- clam page
{
project:{项目信息},
page:{name:'', description:'',jsns:'', cssns:''}
}
- clam mod
{
project:{项目信息},
mod:{name:'', description:'',jsns:'', cssns:''}
}
- clam widget
{
project:{项目信息},
widget:{name:'', description:'',jsns:'', cssns:''}
}
其中{项目信息}的格式请参考
项目目录/.clam/project.json
中的格式。
- 在模板中引用变量的格式是完全的Juicer语法。例如:
- 引用项目名称
${project.name}
- 引用页面名称
${page.name}
- 引用项目的最终编码
${project.charset[0]}
- 支持循环和判断语句,详情请参考地球上最快的前端模板引擎Juicer的语法。
clam
动态内容生成
你可以在页面和模块中使用Juicer语法,避免大量重复代码出现。
可以使用#def
语法定义一个变量。在def后面的JSON格式数据(格式请严格按照JSON格式编写)。
<!--#def {"a":[1,2,3],"b":{data:[1,2,3]} -->
默认情况下创建的数据的作用域只限定与本模板执行环境中。只要你愿意你可以把数据通过以下语法传递给子模板,引入子模板有两种语法格式(完整/简易)。
完整写法:
<!--#include data="b" file="/mods/mod.html"-->
完整写法的优点在于:文件地址和数据源的位置没有顺序要求,可任意前后放置。
简易写法:
<!--#include "/mods/mod.html" "b"-->
简易写法需要遵循文件地址、数据源的顺序进行放置。
数据源的写法也分为两种:
- 如上例展示的,直接写在#def中定义的JSON数据的key,该写法需要注意的是,传递给子模块的数据的类型必须为Object
- 在数据源位置以key / value的形式重新定义,使用$符表示引用#def中定义的JSON数据,无$则以普通字符串的形式传入子模块,该写法对数据类型无要求
以上文中#def的数据为例,若希望将key为b指向的数据传入子模块,数据源有两种写法:
例子
1. <!--#include "/mods/mod.html" "b"-->
2. <!--#include "/mods/mod.html" "data:$b.data"-->
这样数据源b就可以传递给 b.html 模块的执行环境中了。
使用第2种写法可以实现更灵活的数据传递,例如希望向子模块传入普通字符串,或#def定义的JSON中的某一部分数据:
例子
<!--#include "b.html" "str:hello world,b:$b"-->
例子
template.html
<!--#def {"data":[1,2,3],"data2":{"data":[1,2,3]}} -->
{@each data as item,index}
<div>data:${item},index:${index}</div>
{@/each}
<!--#include "sub-template.html" "data2"-->
sub-template.html
<h2>this is sub template</h2>
{@each data as item,index}
<div>data:${item},index:${index}</div>
{@/each}
output
<div>data:1,index:0</div>
<div>data:2,index:1</div>
<div>data:3,index:2</div>
<h2>this is sub template</h2>
<div>data:1,index:0</div>
<div>data:2,index:1</div>
<div>data:3,index:2</div>
clam
引用TMS区块
写法:
简易写法 <!--#include "tms:ID:URL"-->
完整写法 <!--#include file="local_url" tms="[#]ID:URL"-->
参数:
ID:TMS上的站点ID
URL:TMS区块URL
完整写法中#为一个开关,若出现则表示关闭远程TMS区块而调用file指定的本地模块
例子:
<!--#include "tms:608:http://act.ju.taobao.com/go/rgn/ju/normal_banner.php"-->
<!--#include file="/mods/mod.html" tms="608:http://act.ju.taobao.com/go/rgn/ju/normal_banner.php"-->
在./clam/project.json中添加"tms"字段,包含flag和method两个子属性
flag是将URL切分出vm需要部分的起始标识点
method对应vm的方法,项目不同会有所不同,通过%s和%d传递URL和ID
例子:
"tms" : {
"flag" : "/rgn/",
"method": "$!securityUtil.ignoretext($cmsTool.importRgnFromCache(\"%s\", %d, 300000))"
}
clam
特殊标签属性
clam-moveto
该属性用于外联js的script标签中,以提示clam解析时,将该外联js文件引入放于页面的何处。共有三种属性值:head | tail | none
写法:
<script type="text/javascript" src="xxx.js" clam-moveto="head|tail|none"></script>
属性值解释:
head:放于<head></head>的头部
tail:放于</body>之前
none:原始位置,不加以变动