颐和园镇水铜牛
落日无声听云解,
小桥有意任水流。
几见风和星斗稀,
历经波动鬼神愁。
2014-6-28
巍巍岐山凤,
悠悠渭水钩。
命在直中取,
不向曲中求。
在远程仓库一节中,我们讲了远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改。
GitHub就是一个免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。
搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或CentOS,这样,通过几条简单的apt或yum命令就可以完成安装。
假设你已经有sudo权限的用户账号,下面,正式开始安装。
yum install curl curl-devel zlib-devel openssl-devel perl cpio expat-devel gettext-devel
下载最新的git包
wget http://www.codemonkey.org.uk/projects/git-snapshots/git/git-latest.tar.gz
tar xzvf git-latest.tar.gz
cd git-2011-11-30 #你的目录可能不是这个
autoconf
./configure --prefix=/usr/local/git
make
make install
检查下安装的版本,大功告成
git --version
$ sudo adduser git
$ git config --global user.name "whdsmile"
$ git config --global user.email "whdsmile@gmail.com"
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。
先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:
$ sudo git init --bare sample.git
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:
$ sudo chown -R git:git sample.git
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/local/git/bin/git-shell
这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。
$ git add test
$ git commit -m "add file"
现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:
$ git clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.
剩下的推送就简单了。
管理公钥
如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。
这里我们不介绍怎么玩Gitosis了,几百号人的团队基本都在500强了,相信找个高水平的Linux管理员问题不大。
OpenX Ad Server (简称OpenX )是一个采用PHP开发的广告管理与跟踪系统。
从http://www.openxconsultant.com/blog/2013/08/openx-source-v2-8-11-released-for-download/下载源代码,或者先从http://www.openxconsultant.com/blog/category/featured/检查最新发布。
提示:如果研究学习源代码,可以使用SVN检出,地址:https://svn.openx.org/openx/tags/2.8/openx-2.8.11/
提示:选择中文简体,会出现一些乱码问题,建议采用命名和语言版本均以英文为主。时区可以选择Asia/Shanghai。
登录系统后,进入Inventory > Advertisers。
选择添加新广告主(Add new advertiser),进入添加新广告主界面。
填写广告主的基本信息(Basic information):
在广告主报告(Advertiser report)区域,选择什么条件以及什么时间下需要给广告主发邮件:
其他(Miscellaneous)区域可以让您设置广告投放的限制选项:
完成广告主(京东商城)创建后,从广告主界面点击添加新广告活动(Add new campaign),系统将会带您到添加新广告活动的界面。
填写基本信息(Basic information):
下一步是选择广告活动的类型。有三种活动类型可选:
选择好广告计划类型后,可以设置一些其他的内容:
设置完成后,点击保存。
进入Inventory > Websites,在网站管理界面,在右上方选择添加新网站(Add new website),您将可以看到添加新网站界面。
创建新网站:
完成以上操作后点击保存,添加网站成功。
进入Inventory > Zones,点击添加新广告位(Add new zone)选项,到达添加新广告位页面。
添加新广告位:
一旦完成操作点击保存,完成添加广告位。
在广告位列表中,点击链接广告(Linked Banners),选择一个广告主和广告活动。
或者在广告的详细页面的链接广告位(Linked Zones)标签中,选择一个广告位。
在广告位列表中,点击获取代码(Invocation Code)
用户有一个用户名和密码,与至少一个OpenX帐户,并可以与任意数量的账户挂钩。一个用户可以有不同的连结多种帐户的多个角色。每个角色都有其自己的设置权限,定义用户可以执行相关联的帐户上的行动。
系统安装后的初始用户即是管理员,又是经理。
进入广告主,或网站的详情编辑页面,点击用户访问(User Access)标签,在操作(Actions)下拉菜单中,可以创建或关联使用该账户访问的用户。
下载和安装windows下Git环境:
下载完两个软件之后,首先安装 TortoiseGit,安装完 TortoiseGit 之后先别急着用,接着安装 msysGit,安装过程中的一些选项都可以默认!
本节介绍在 Git 命令行(Git Bash)中 SSH 密钥的生成,以及在 Gitlab 上的使用。
使用命令行进行 Git 操作,显得高端、大气、上档次。如果你不慕虚名,只想生活简单而美好,就像使用 SVN 小乌龟(TortoiseSVN)那样工作,可以直接跳过本节。
mkdir .ssh
创建。ssh-keygen -t rsa -C "your email"
。邮件地址可以不写,默认是 本机用户名@本机机器名
。最好是填写一些标识自己的记号。用记事本打开、复制 id_rsa.pub 文件的内容,在 Gitlab 网站 My Porfile 页面右下角,点击 Add Public Key ,粘贴刚才复制的内容到 Key 的大文本框,提交即可。
注意:在复制内容的时候,文本的两端都不要有空格和换行。
从程序目录打开 “Git Bash”(或使用右键菜单),在命令行中git clone
一个项目。
提示是否把项目Git库的域名设置为可信任的已知域名,直接回答yes
就可以了。
其他命令,一样的用法。不清楚的话,可以查阅《史上最浅显易懂的Git教程》。
本节介绍在Git小乌龟(TortoiseGit)中生成密钥,以及在Gitlab上的使用。
.ppk
)。git clone
项目的目录中,右键选择Git克隆
。.ppk
的私钥文件。git clone
以外的工作,就不需赘述了。本教程的内容较少原创,多从其他文档上摘录。
Yeoman 1.0 包含以下三套工具,分別說明如下:
要安裝這三套工具之前,還有許多相依的工具必須事先安裝,否則工具指令會無法正確執行,以下包括 node.js , Git for Windows 與 RubyInstaller 這三套工具的安裝注意事項:
请选择正確的 Windows 安裝包,注意 CPU 架構有区分 32-bit 与 64-bit 两种:
安裝时要确保 Add to PATH 項目有被安装进去:
安裝到 Adjusting your PATH environment 步骤時,选择 Run Git from the Windows Command Prompt 的相容性比较高,问题也会少很多:
其他保留预设值即可。
由於前端开发作业经常会用到 Compass 工具撰写 CSS,而且在使用 Yeoman 的時候,有些产生器所产生的 grunt 定义包也会用到 Compass 来执行,所以可以预先安装好。
安裝 Compass 工具会需要先安裝 Ruby 才能安装与使用。建议通过 RubyInstaller (Windows) 提供的 MSI 安装包进行安裝,但安裝的过程中有一個步驟非常重要,你必須在 Installation Destination and Optional Tasks 步驟時,勾选 Add Ruby executables to your PATH 选项才行,如下图示:
安裝好这些工具之后,就可以开始准备安裝 Yeoman 相关工具了:
打开命令行。
使用npm 安裝 yo , bower 與 grunt 工具。
npm install -g yo grunt-cli bower
其中 -g 代表要把 yo , grunt-cli , bower 這三个套件安裝到全域 (global)
安裝 yo 相关的 程序码产生器 (generator) 套件
因為 yo 這套工具主要就是用來自動產生網站骨架或程式碼,在執行 yo 之前,你必須預先安裝好這些程式碼產生器範本,這些被稱為 YEOMAN GENERATORS,你可以在 YEOMAN GENERATORS 找到許多現成的產生器範本,並且一樣透過 npm 進行安裝。
例如你在 YEOMAN GENERATORS 頁面找到一個 webapp 產生器,那麼你可以用以下指令進行安裝:
npm install -g generator-webapp
如果想安裝 angular 產生器,那麼你可以用以下指令進行安裝:
npm install -g generator-angular
以此類推!
如果你要順道安裝 Compass 的話,也可以輸入以下指令進行安裝
gem update --system
gem install compass
Yeoman 與 Compass 安裝完成!
先创建一个网站目录,例如webapp。
在这个目录下,命令行执行命令,产生网站骨架:
yo angular
这是一個交互的过程,yo安装哪些模块:
安装完成后可能会遇到一些问题,如:
表示依赖的模块需要手动bower install & npm install安装。
译者注1:Semantic Versioning 2.0.0的原文地址是http://semver.org
版本号MAJOR.MINOR.PATCH(主版本号.副版本号.补丁版本号)的递增规则如下:
预发布信息和构建元数据可以作为附加标签,扩展MAJOR.MINOR.PATCH(主版本号.副版本号.补丁版本号)的格式。
在软件开发管理的世界里,有一个可怕的地方叫做“依赖关系地狱”。当你的系统越来越大,集成了更多的包,你就会深刻的认识到自己的卑微和渺小。直到有一天,你在这个坑里彻底绝望。
系统依赖的模块发布新版本,就成了噩梦。如果依赖关系的规定过于严格,会遇到一个版本依赖关系的死结(升级一个依赖包之前,必须升级完所有的依赖包)。如果依赖关系规定的太松散,版本依赖关系的混乱就会成为你的切肤之痛(假定你的版本兼容性很重要)。当版本依赖关系的死结、混乱,让你不能轻松、安全的向前迁移项目的时,你就坠入了“依赖关系地狱”。
为了解决这个问题,我提出了一套简单的规则和要求,决定版本号如何分配和递增。这些规则都是基于(但不限于)业界早已存在的流行做法。使用此制度的软件必须声明一个公共API,可以在代码内部声明,也可以严格地写入文档中,但必须是清晰而准确的。一旦确定了公共API,都必须通过明确的版本号递增,来表明系统所做的变更。认真考虑一下X.Y.Z(主版本号.副版本号.补丁版本号)的版本格式。不影响API的BUG修正,递增补丁版本号。向后兼容API的功能新增或变更,递增副版本号。不向后兼容的API变更,递增主版本号。
我把这个制度称为“语义化版本控制”。在这个方案中,规范了从一个版本到下一个版本的版本号,规范了更改版本号传达源代码意图的方法,以及源代码做了什么样的修改。
在本文档中,对关键词(”MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL”)的使用,遵循RFC2119标准的规范。
译者注2:RFC2119标准规范了表示“要求”(Requirement)的动词涵义。关于RFC2119标准可以参考《RFC2119 中文版-RFC文档用于指出要求级别的关键字》。
1.9.0 -> 1.10.0 -> 1.110
1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92
。1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85
。1.0.0 < 2.0.0 < 2.1.0 < 2.1.1
。在主版本号、副版本号、补丁版本号都相同时,预发布版本号的优先级低于正常版本号。例如:1.0.0-alpha < 1.0.0
。两个预发布版本号的主版本号、副版本号、补丁版本号都相同时,优先级必须(MUST)从左至右的比较由点分隔的标识符,直到发现差异。标识符仅由数字组成的,比较数字的大小。标识符包含字母或连字符的,按照ASCII码的顺序进行比较。数字标识符总是小于非数字标识符。如果前面所有的标识符都是相同的,版本号中预发布字段较长的,优先级较高。例如:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
。语义化版本控制并不新鲜,也不是一个革命性的想法。事实上,你可能做了很多,已经完成了这件事。问题是完成并不等于极致。如果不符合某种正式的规范,版本号在依赖关系管理上基本没有什么用处。给上述想法起个名字,做出清晰的定义,就很容易向软件用户传达你的意图。一旦这些意图是明确的,一个灵活的(但也不能太灵活)依赖关系管理规范,就流畅的运转起来了。
举一个简单的例子,说明语义化版本控制如何把你从“依赖关系地狱”中拯救出来。设想一个叫“消防车”的库,依赖一个使用语义化版本控制的包,名叫“梯子”。“消防车”刚刚生产出来的时候,梯子的版本号是3.1.0。由于“消防车”使用了在3.1.0版本才开始提供的一些功能,你就可以放心地指定依赖的“梯子”的版本大于等于3.1.0,但低于4.0.0。现在,当“梯子”有3.1.1和3.2.0的版本可用时,就可以更新到你的包管理系统,并知道他们将与当前依赖的软件兼容。
作为一个负责任开发者,你当然会验证任何包所标榜的新功能。现实世界可是一个鱼龙混杂的地方,做什么事情都可以,但要警惕。你可以让语义化版本控制,给你提供一个稳健的方式来发布和升级包,不必再被迫为使用依赖包的新版本而反复折腾,为你节省时间,减少麻烦。
如果这一切听起来还不错,你需要做的所有事情,就是开始声明你正在使用语义化版本控制,并遵循其规则。欢迎你在README文档中链接这个网站(译者注3),让更多的人知道这个规则,并从中受益。
译者注3:原文中提到的网站是指http://semver.org。
最简单的做法就是从0.1.0版本开始你的初始开发,在后续的发布中递增副版本号。
如果你的软件在产品环境中使用,它可能已经应该是1.0.0。如果的软件用户,已经开始依赖一个稳定的API,应该是1.0.0。如果你担心很多向后兼容的问题,应该已经是1.0.0。
所有在快速开发和快速迭代的时候,主版本号都是0。如果你的API每天都在改变,仍然应该会在版本0.y.z阶段。或者,在一个单独的开发分支中,开展下一个主版本的开发工作。
这是一个负责任的开发问题,也是一个有远见的问题。在有大量依赖关系管理的软件中,不应该轻率的推出不兼容的改变。升级所必须付出的成本是有意义的。发布冲击主版本号的不兼容改变,意味着你深思熟虑过将要发生的震荡,有磕碰主要版本发布不兼容的改变意味着你想通过你的变化的影响,并评估过所涉及的投入产出比。
为预计会给其他人使用的软件,提供适当的文档,是一个专业开发者的责任。控制软件的复杂性,是保持项目高效的一个非常重要的组成部分。如果没有谁知道如何使用你的软件,或者用什么方法可以安全地调用,保持项目高效是很难做到的。从长远来看,采用语义化版本控制,坚定的看守一个定义良好的公共API,可以让每一个人都能流畅的做每一件事。
一旦意识到已经破坏了语义化版本规范,要发布修正了问题,并且恢复向后兼容的新的主版本号。即便是这样,这也是一次不能接受的版本发布。如果方便,一定要记录有问题的版本,并通知你的用户,让他们意识到问题的存在。
这是向后兼容的,因为不影响公共API。如果软件与你的包有相同的依赖,有可能会有自己的依赖关系规范,而且软件的作者也会关切任何有可能的冲突。确定这个依赖变更是补丁级别的,还是副版本级别的,取决于你更新你的依赖是要修正一个BUG,还要要引入一组新的功能。我们通常期待是后者,哪怕增加大量的代码。因为在这种情况下,就显然是一个副版本级别的变更了。
那你就发挥出色的判断能力吧。如果拥有大量的忠实拥趸,愿意委曲求全的回到公共API的预期轨道上来,那你最好发布一个主版本。哪怕没什么修改,严格的说也只是补丁级别的。请记住,语义化版本控制是通过版本号的变更来传达意图的。如果变更对你的用户很重要,就使用版本号通知他们。
废弃某些功能,在软件开发中很常见的,通常也会推动软件向前发展。
在废弃公共API的一部分时,你要做两件事:
你在主版本中彻底删除这些功能之前,至少要在发布的一个副版本中包含这些功能,好让用户平滑的迁移到新的API上来。
没有什么限制,但是你最好保持理智。比如,一个版本号有255个字符,就有点出格了。另外,在开发团队中具体的制度可能会有一些自定义的长度限制。
语义化版本控制规范是由Tom Preston-Werner编写的。他是Gravatars 的创始人,也是GitHub的共同创办人。
如果你有一些反馈,可以在GitHub上提交一个问题。
选用JSDoc3作为注释规范,文档生成工具使用grunt-jsdoc,本文不再介绍选型过程和原因。对JSDoc3的深入学习使用,可以参考入门教程http://usejsdoc.org/index.html。
JSDoc注释的样式如下例,与单行注释 //
和多行注释 /**/
不同,而是类似于JAVA的JDoc和PHP的PHPDoc。
/**
* JSDoc注释。
*/
/**
* 函数注释的示例。
* @param {Integer} augend 被加数。
* @param {Integer} addend 加数。
* @return {Integer} 两数之和。
* @example
* add(1, 2) => 3
*/
function add(augend, addend){
return augend + addend;
}
@param
用于对函数、类的方法的参数进行注释,是JSDoc中最常用的注释标签。
@param
注释必须指定一个参数名,也可以有一个用大括号括起来的参数类型,以及参数的描述信息。参数类型可以是javascript内置的数据类型,如Array、Boolean、Date、Function、Number Object 、String 等,还可以是其他JSDoc所支持的(英文没看太懂,以后用到再认真学习)。
The parameter type can be a built-in JavaScript type, such as string or Object, or a JSDoc namepath to another symbol in your code. If you have written documentation for the symbol at that namepath, JSDoc will automatically link to the documentation for that symbol. You can also use a type expression to indicate, for example, that a parameter is not nullable or can accept any type; see the @type documentation for details.
下面直接使用http://usejsdoc.org/tags-param.html文档中的例子,说明 @param
标签中如何使用参数名、参数类型和参数描述信息。
只有参数名的注释:
/**
* @param somebody
*/
function sayHello(somebody) {
alert('Hello ' + somebody);
}
包括参数名和参数类型的注释:
/**
* @param {string} somebody
*/
function sayHello(somebody) {
alert('Hello ' + somebody);
}
包括参数名、参数类型和参数描述的注释:
/**
* @param {string} somebody Somebody's name.
*/
function sayHello(somebody) {
alert('Hello ' + somebody);
}
@return
说明函数的返回值。例如:
@return {Number}
@return {Number} Sum of a and b
@return {Number|Array} Sum of a and b or an array that contains a, b and the sum of a and b.
@module
标签用于标记当前代码文件属于哪个模块。
在 @link
或 @see
标签中,使用 module:moduleName
可以链接到一个模块。例如,使用 {@link module:foo/bar}
,可以链接到由 "@module foo/bar
定义的模块。
如果没有提供模块名,将使用模块文件所在路径及文件名作模块名。
@see
标签可以指向一个相关的引用。示例如下:
/**
* Both of these will link to the bar function.
* @see {@link bar}
* @see bar
*/
function foo() {}
// Use the inline {@link} tag to include a link within a free-form description.
/**
* @see {@link foo} for further information.
* @see {@link http://github.com|GitHub}
*/
function bar() {}
@name
:类名称@class
:类描述@constructor
:表明这是一个构造函数,非常重要。@extends
:类继承的父类。@type
:数据的类型,主要用来注释属性。@default
:默认值,主要用来注释属性。@abstract
标明一个成员是抽象的,需要子类去实现。@public
、@protected
、@private
:类、方法或属性的访问权限要把JSDoc3的注释生成API类库文档,@lends
是个很要紧的标签。
例如,@lends Sample.prototype
表示下面的对象归属于Sample。
较详尽的使用说明参见http://usejsdoc.org/tags-lends.html。
有关类的定义和注释示例,如下:
/**
* @name Sample
* @class 示例类
* @public
* @constructor
*/
function Sample(){
this.something = [];
}
Sample.prototype =
/** @lends Sample.prototype*/
{
/**
* 属性示例。
* @private
* @type {Array}
* @default []
*/
something : [],
/**
* 方法示例。
* @public
* @param {String} arg 跟踪方法插件。
*/
doSomething: function(arg){
}
};
@example
: 示例代码。
@enum [<type>]
: 一组同样类型的静态属性集合。switch
语句中的分支应该只使用枚举。
@overview
:对当前代码文件的描述。
@copyright
:代码的版权信息。
@author <name> [<emailAddress>]
:代码的作者信息。
@version
:当前代码的版本。
如果你有为项目写说明文档的好习惯,碰巧又使用的是MarkDown格式,碰巧文件名又是README.md,那就很好了。
把README.md文件放在代码清单里边,JSDoc工具是自动为你生成API文档的首页,什么系统概况、设计需求、设计方案及版本更新记录等等的内容都可以放进来。
Grunt的安装使用,请参考教程《Grunt的安装和使用》。
提示:JSDoc某些配置有git依赖, 需要在命令行中可以执行git命令。最好先安装一个msysgit(http://msysgit.github.io/),然后在环境变量中增加git的bin目录。
使用JSDoc3插件,在package.json中的NPM依赖配置参考,如下:
"devDependencies": {
... ...
"grunt-jsdoc": "~0.5.4",
"ink-docstrap": "~0.3.0",
... ...
}
使用JSDoc3插件,在Gruntfile中的配置参考,如下:
jsdoc: {
dist : {
src: ['README.md', 'src/sample.js'],
options: {
destination: 'api',
template: "libs/jsdoc3/docstrap/template",
configure: "libs/jsdoc3/docstrap/template/jsdoc.conf.json"
}
}
}
详细使用方法和参数,参考:https://github.com/krampstudio/grunt-jsdoc-plugin。
本教程的内容较少原创,多从其他文档上摘录,详见参考资料部分。
Grunt是一个自动化的项目构建工具。Grunt和Grunt的插件都是通过Node.js的包管理器npm来安装和管理的。
如果还没有安装Node.js,需要先从Node.js网站(http://nodejs.org/)下载安装。
Node.js安装中,以及后继的其他安装中,可能需要系统管理员权限。
安装Grunt之前,可以在命令行中运行
node -v
查看你的Node.js版本。0.8.0及以上版本的Node.js才能很好的运行Grunt。
为了方便使用Grunt,你应该在全局范围内安装Grunt的命令行接口(CLI)。
在命令行中,执行如下命令:
npm install -g grunt-cli
这条命令将会把grunt命令植入到你的系统路径中,这样就允许你从任意目录来运行它(定位到任意目录运行grunt命令)。
使用Grunt构建的项目中,大多包含有package.json和Gruntfile。
package.json:用于存储已经作为npm模块发布的项目元数据(也就是依赖模块,包括Grunt本身)。
Gruntfile.js:用于配置或者定义Grunt任务和加载Grunt插件。
如果已经安装好了Grunt的命令行接口(CLI),并且项目中已经有了package.json和Gruntfile,使用Grunt进行工作是非常容易的。
npm install
,安装项目相关依赖(插件,Grunt内置任务等依赖)。有些Grunt插件,比如JSDoc3插件,需要在命令行中可以执行git命令。最好先安装一个msysgit(http://msysgit.github.io/),然后在环境变量中增加git的bin目录。
在上述的命令行中执行 grunt
,就可以运行Grunt构建了。
至此,通过安装Node.js、在命令行中运行
npm install -g grunt-cli
、npm install
和grunt
三个命令,就完成了使用现有的Grunt项目进行工作的所有准备。
有很多项目的结构和内容都很类似,人们就设计开发了可以快速工作的模板,能够通过模板迅速的自动创建项目,称为脚手架工具(grunt-init
)。
在Grunt的GitHub主页(https://github.com/gruntjs)上有很多之用的 grunt-init-*
脚本架模板工具,比如grunt-init-jquery、grunt-init-gruntplugin等等。
grunt-init
是一个用于自动创建项目的脚手架工具。
先在命令行中进行全局安装:
npm install -g grunt-init
这样,会把grunt-init命令植入到你的系统路径,从而允许你在任何目录中都可以运行它。
把需要的模板放在你的 ~/.grunt-init/
目录中(在Windows平台是%USERPROFILE%/.grunt-init目录)。
%USERPROFILE%
一般是指“C:\Users\你的用户名”这个目录。
建议使用如下的git clone命令把这个模板克隆到该目录:
git clone https://github.com/clientlab/grunt-init-gruntfile.git ~/.grunt-init/gruntfile
在Windows平台上稍微有些不方便,但是可以在文件浏览器中输入%USERPROFILE%,定义到该目录下,然后执行如下命令:
git clone https://github.com/clientlab/grunt-init-gruntfile.git ./.grunt-init/gruntfile
要想使用上述命令,需要先安装一个msysgit(http://msysgit.github.io/),然后在环境变量中增加git的bin目录。
最终的文件目录结构,如下:
.grunt-init/
.grunt-init/gruntfile/
.grunt-init/gruntfile/README.md
.grunt-init/gruntfile/template.js
.grunt-init/gruntfile/root/
然后进入一个准备开发项目的空目录,按Shift+鼠标右键,打开右键菜单,选择“在此处打开命令窗口(W)”,打开命令行,执行如下程序:
grunt-init gruntfile
请注意,此模板将在当前目录中生成文件,如果你不想覆盖现有文件,一定要使用一个新的空目录。
在组织项目的开发工作时,如果构建工具选型是Grunt,那么常常有以下几个需求:
不同的团队、 不同的项目因为细化需求的不同,都会各自选择不同的解决方案。
使用任务的目标名称,在逻辑上分组构建任务。这是大多数项目最初会用到的方法,如grunt中文文档项目(https://github.com/basestyle/grunt-cn)。
在传递给grunt.initConfig方法的对象中,使用如下类似的不同任务目标名称区分webapp和pc两类任务:
jshint:{
webapp: ['app/www/js/main.js'],
pc: ['js/chart.js','js/common.js','js/demand.js','js/dialog.js','js/formbeautify.js','js/jquery.autopagination.js',
'js/jquery.cascadeselect.js','js/jquery.datepicker.js','js/jquery.formvalid.js','js/jquery.memberinput.js',
'js/jquery.multiupload.js','js/jquery.tabs.js','js/pmstation.js','js/project.js','js/setting.js','js/tasktable.js','js/work.js']
},
在自定义的别名任务中,把两类任务分组成webapp和pc两个单任务:
grunt.registerTask('webapp', ['htmlhint:webapp','jshint:webapp','uglify:webapp','cssmin:webapp','copy:webapp','replace:webapp']);
grunt.registerTask('pc', ['jshint:pc','uglify:pc','cssmin:pc','copy:pc','replace:pc']);
如果两类任务调用的任务都相同,也可以使用动态别名任务的方法来处理。
代码示例如下:
grunt.registerTask('build', 'Run all my build tasks.', function(n) {
if (n == null) {
grunt.warn('Build num must be specified, like build:001.');
}
grunt.task.run('foo:' + n, 'bar:' + n, 'baz:' + n);
});
参见Grunt文档的《常见问题-“动态”别名任务》(中文/英文)。
将配置对象分拆出来,然后在Gruntfile.js中require进来,再将这些不同任务的对象组合成一个对象传递给grunt.initConfig。
上述方案是在我提出问题(有没有分拆Gruntfile.js的方案?(https://github.com/basestyle/grunt-cn/issues/34))后,TooBug[GitHub]和Vincent Hou给出的解决方案。
因为没有进一步展开,所以没有示例代码。
我在知乎上提出问题《有什么方案可以把较为庞大的gruntfile分拆?(http://www.zhihu.com/question/21766711)》后,墨磊[GitHub]给出的解决方案,基本上就是使用自定义任务把构建任务分解在不同的文件中的。
比如,在gruntjs.com(https://github.com/gruntjs/gruntjs.com)项目中,也是通过把blog、docs等任务从Gruntfile中分拆出来的。
代码示例如下:
// Load local tasks
grunt.loadTasks('tasks'); // getWiki, docs tasks
grunt.registerTask('build', ['clean', 'copy', 'jade', 'docs', 'blog', 'plugins', 'concat']);
上例中的docs、blog任务就是放在tasks目录下,使用grunt.loadTasks方法统一加载的。
前文的所述的几个方案,用于分拆Gruntfile虽然很不错,但是并不能解决最初提出的后两个需求:
我发现grunt-run-grunt(https://github.com/Bartvds/grunt-run-grunt)插件可以同时满足这三个需求。
很遗憾的是,这个插件关注的人不多,Fork的人也不多。或许其他的开发团队有更高明的方案也说不一定,还需要继续跟进。
在选型定案使用grunt-run-grunt插件以后,项目的组织就确定了。
入口Gruntfile的代码示例:
run_grunt:{
options: {
'base': require('path').resolve('.')
},
main:{
src: ['src/pro/a.Gruntfile.js', 'src/frameworks/b.lib.Gruntfile.js', 'src/frameworks/c.lib.Gruntfile.js']
}
}
需要特别注意的是,options中的base配置是指整个项目所有的Gruntfile都是以这个入口Gruntfile所在的目录为base目录的。
这个扩展机制可以在options中配置最终传递给grunt的命令行参数,属性名是命令行参数名,属性值是命令行参数的值。
上例配置的意义是要求执行类似如下样式的Grunt命令:
grunt --base e:/dev/
各部分的Gruntfile与正常的Gruntfile没有什么区别。唯一的区别就是,这个方案把所有的Gruntfile的base目录设定为入口Gruntfile所在的目录。
比如,src/pro/a.Gruntfile.js中的代码示例如下:
concat: {
"pro: {
src: [
"src/frameworks/b.lib.js",
"src/frameworks/c.lib.js",
"src/pro/a.js"
],
dest: "dist/pro/a.js"
}
}
示例中,不管是src目录,还是dest目录,都是以入口Gruntfile目录为基准的。
当然,base目录不这样安排是最简单的,在入口Gruntfile的run_grunt任务中不配置base就可以了。如此以来,所有的Gruntfile中的目录都是以本Gruntfile所在目录为base目录的。