node package manager
Orgs are free. Discover, share, and reuse code in your team. Create a free org »

clam

Clam 是什么

Clam是一套基于 Node.js 的前端工程项目开发环境(支持HTTP和HTTPS)。

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文件里引入所需样式和脚本即可,只需要一个简单的配置项cdnPath

# 在项目配置文件 .clam/project.json 里修改 cdnPath
"cdnPath": "http://yourcdn.com/yourproject"

# >> 这样项目打包时就会将相对路径替换为指定的`cdnPath`
# 注意:结尾不要附加斜杠

至此使用Clam就完成了一个最基本的前端项目的开发。

关于Clam提供的更多功能:

  • 本地资源文件代理(支持combo功能)
  • 接口模拟
  • 模块化开发管理
  • 打包和发布管理
  • 项目 Hosts 管理
  • 基于模板创建页面和模块

强烈推荐继续阅读。

Clam 本地文件代理

Clam 本地文件代理提供了一个方便调试测试或线上问题的机制。其工作原理很简单,当你需要调试一个测试或线上页面如hello_clam.html时,首先找到该页面所属的项目hello_clam。确保已经配置了项目的cdnPath,并将文件服务器域名指向本机:

# 在 hosts 文件中将文件服务器指向本机
127.0.0.1 yourcdn.com

然后开启或切换到此项目的 Clam 环境:

cd hello_clam
clam on

注: Mac、Linux下需加sudo

此时再访问 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.jsClam提供了方便且强大的接口模拟能力。

  • 通过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+))*(\/[^?]*)(?:\?(.*))*$/)

目前仅支持httphttpssocket等待完善中。

"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目录下的 project.json 中增加了一个 proxy 的配置项。关于此配置项的详细说明请参考:Doji配置

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 file="/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>

clam 动态内容生成

你可以在页面和模块中使用Juicer语法,避免大量重复代码出现。 可以使用#def语法定义一个变量。在def后面的JSON格式数据(格式请严格按照JSON格式编写)。

<!--#def 	{"a":[1,2,3],"b":{data:[1,2,3]} -->

默认情况下创建的数据的作用域只限定与本模板执行环境中。只要你愿意你可以把数据通过以下语法传递给子模板,引入子模板有两种语法格式(完整/简易)。

写法:

<!--#include data='{"b":${b}}' file="/mods/mod.html"-->

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

  1. 如上例展示的,直接写在#def中定义的JSON数据的key,该写法需要注意的是,传递给子模块的数据的类型必须为Object
  2. 在数据源位置以key / value的形式重新定义,使用$符表示引用#def中定义的JSON数据,无$则以普通字符串的形式传入子模块,该写法对数据类型无要求

例子

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 引用远程HTML代码

写法:

<!--#remote url="http://foo.com/path/to/bar.html"-->

clam 特殊标签属性

fe-move

该属性用于外联js的script标签中,以提示clam解析时,将该外联js文件引入放于页面的何处。共有2种属性值:top | bottom

写法:

<script type="text/javascript" src="xxx.js" fe-move="top|bottom"></script>

属性值解释:

top:放于<head></head>的头部
bottom:放于</body>之前

fe-group

对assets进行combo

写法:

<script type="text/javascript" src="xxx.js" fe-group="cc"></script>
<script type="text/javascript" src="yyy.js" fe-group="cc"></script>

=>

<script type="text/javascript" src="~/??xxx.js,yyy.js"></script>