clam

A full Web front end develop envirment.

npm install clam
4 downloads in the last day
6 downloads in the last week
262 downloads in the last month

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 内置的本地服务器会将此文件所引用的样式和脚本资源替换为该项目目录的源文件。

值得一提的是Clam的文件代理功能提供了combo支持,其中默认支持的combo格式以?作为servlet,以,作为文件分隔符。如果需要定制combo,可以到用户根目录的~/.flex-combo/config.json配置文件里更改flex-combo的配置。

flex-combo是一个提供本地combo服务的Node.js模块。
Clam本身的combo功能也是基于此模块实现的。
关于flex-combo的详细信息参见 flex-combo官方文档

Clam 接口模拟

依托于Node.jsClam提供了方便且强大的接口模拟能力。

要在Clam里模拟一个接口,只需要在配置文件project.jsonjson字段增加如下信息:

    "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用来控制接口本地处理还是做远程转发;remotelocal字段分别指定了远程转发服务器地址和本地处理脚本的文件名。本地处理脚本需要放置在项目的.clam/json/目录下。

假如此时项目请求/api/fake_api接口,Clam将调用.clam/json/local_a.js处理;若请求/api/another_fake_apiClam会将此请求转发到远程服务器remotehost.com上。

本地处理脚本使用Node.js内置的http模块来实现,它提供了一个接收http requesthttp response对象参数的处理函数:

exports = module.exports = function(req, res){
    res.end('this is a fake api response~')
    return true
}

关于此函数的详细用法请参考Node.js官方文档的http模块部分。

Clam 模块化开发

除了一系列的贴心功能外,Clam还非常激进的提供了从底层架构层面对于前端模块化开发的支持。先来了解一下Clam对于前端项目的理解。

传统上前端工作里“项目”的概念远没有后台软件开发领域里那么清晰,这主要是由于以往的客户端页面较简单,不需要太多“项目”层面的支持。随着现在客户端功能的越趋复杂,有必要系统的来引入一套针对前端业务特点构建的架构模式。

Clam里对于项目的定义是一个完整的前端应用,或其中一个相对独立的某一个业务场景。如:一个单页富应用,可以作为一个Clam项目;或者业务耦合度较高,用户使用路径很近的一组页面,也可以作为一个Clam项目。

对于一个Clam项目的具体页面,除了页面自身的html模板,样式和脚本外,它还可以引用一组模块,其中每个模块都有其独立的html模板,样式和脚本文件。同时页面上还可以存在一些通用的组件,如下图所示:

image

  • 一个Clam项目由若干页面Page、模块Module、和组件Widget构成;
  • 其中PageModule可以由htmlcssjs等组成;Widget则不应包含html
  • Page可以在其html里包含Modulehtml文件来使用模块;通过静态包含或动态加载使用Widget
  • Module通常是与业务关系较密切的独立功能块,比如一个订票网站的常用联系人模块;
  • Module应做到尽可能独立,最理想的情况是完全独立于页面;
  • Widget是与具体业务耦合较松,复用性更强的功能块,如日历组件;
  • Pagehtml部分通常是用来包含模块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文件内引入,这样可以做到模块的完全独立。同样的,你毋需担心引入诸如jQueryCSS 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切换规则。

  1. 执行sudo clam ui,通过GUI设置项目的host。
  2. 修改.clam/project.jsonhosts字段的值。

修改完毕后无需重新启动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将:

  1. 复制~/.clam/templates/<模板名>/page/下所有文件到项目目录下src/pages.
  2. 修改所有命名为template.*的文件名为<页面名>.*
  3. 替换所有文件中的变量。模板支持的变量格式如下
    1. clam page {
      project:{项目信息},
      page:{name:'', description:'',jsns:'', cssns:''}
      
      }
    2. clam mod {
      project:{项目信息},
      mod:{name:'', description:'',jsns:'', cssns:''}
      
      }
    3. clam widget {
      project:{项目信息},
      widget:{name:'', description:'',jsns:'', cssns:''}
      
      } 其中{项目信息}的格式请参考项目目录/.clam/project.json中的格式。
  4. 在模板中引用变量的格式是完全的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"-->

简易写法需要遵循文件地址、数据源的顺序进行放置。

数据源的写法也分为两种:

  1. 如上例展示的,直接写在#def中定义的JSON数据的key,该写法需要注意的是,传递给子模块的数据的类型必须为Object
  2. 在数据源位置以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:原始位置,不加以变动
npm loves you