7

自动化开发

Yeoman

Yeoman按照官方说法,它不只是一个工具,还是一个工作流。它其实包括了三个部分yo、grunt、bower,分别用于项目的启动、文件操作、包管理。但我并不太认同这是一个工作流的说法,至少目前来看还不够成熟,在真实的生产环境中会遇到许多问题。而未来的可能性大致应该有两条路可走,也许会产生某些工作流的标准来定义前端开发的软件质量,不过我更认为Yeoman应该走向高可定制的工作流工具的方向,而不是自身作为一个工作流来存在。

Yo

Yo是一个项目初始化工具,可以生成一套启动某类项目必须的项目文件。可以通过npm安装它到全局:

npm install -g yo

然后还需要安装一些generator,这是一个用于创建某个指定类型项目的生成器。比如安装一个最常用的webapp的生成器,然后就可以在项目路径下生成项目启动需要的所有文件,像这样:

npm install -g generator-webapp
cd /project_folder/
yo webapp

但是这种机制有一个很严重的问题,generator产生的文件结构是谁制定的?没有一个官方的相应的标准或者说Guide,generator的形式参差不齐,甚至我发现Firefox OS的generator生成的是一个API接口的Demo而不是一个种子,如果要进行开发需要进行很多删减。

不过产生这些generator的generator却是一个很好的工具,它应该是一个创造性的工具。首先需要安装generator-generator,然后使用它,接着会看到字符拼接的yeoman,像这样:

npm install -g yo generator-generator
$ mkdir ~/dev/generator-blog && cd $_
$ yo generator

    _-----_
   |       |
   |--(o)--|   .--------------------------.
  `---------´  |    Welcome to Yeoman,    |
   ( _´U`_ )   |   ladies and gentlemen!  |
   /___A___\   '__________________________'
    |  ~  |
  __'.___.'__
´   `  |° ´ Y `

当然使用它之前应该将写好的项目文件放入 app/templates 文件夹中,并在 templates 同级的路径中加入 index.js 进行配置就可以了。这里的index.js是运行在Nodejs中的,也就是说由它将templates中的项目文件放入该放的地方并且填入一些变量去构建整个项目。这里才是体现一个generator是否是一个好的generator的地方,如果仅仅是将一堆写好的项目文件下载下来那什么意义也没有,不存在万用种子。只有在使用generator生成项目时高度定制才是其意义所在,而相关标准才是最难的部分。

Bower

Bower是一个类似于npm的包管理器,但不同的是Bower主要针对前端,并且直接从Github查找需要的库下载到本地缓存。使用很简单,用npm安装bower后可以安装Github的项目并指定版本号,还可以重命名。默认会下载到项目中的 bower_components 文件夹中。

npm install -g bower
bower install jQuery
bower install jQuery#1.10.3
bower install jQueryOld=jQuery#1.6.4

还可以通过bower.json文件来配置需要安装的包,使用 bower init 命令就可以生成bower.json文件,然后在其中写入需要的包及其版本即可

{
  "name": "my_project",
  "version": "0.1.0",
  "main": [js/js.js, css/css.css],
  "ignore": [
    ".jshintrc",
    "**/*.txt"
  ],
  "dependencies": {
    "<name>": "<version>",
    "<name>": "<folder>",
    "<name>": "<package>"
  },
  "devDependencies": {
    "<test-framework-name>": "<version>"
  }
}

当然它也可以搜索包,像这样搜索一下jquery。

bower search jquery

如果觉得bower_components的文件夹名太长不好,可以在 .bowerrc 中以json的形式修改它的路径

{
  "directory": "lib"
}

还有许多其他的配置,可以在Bower存放在Google Doc的文档中查看。

但是Bower还有一个Bug,jQuery在Github上的项目文件是分模块的,必须使用项目中的Grunt才能打包成jquery.js文件,而官方的说法是使用小写q的 jquery 来获取components项目中的jquery文件,但是目前Bower是大小写不分的,所以无法获取独立的jQuery文件。如果bower可以指定获取某个项目中的某个或某些指定的文件将会更加犀利。

甚至Bower可以在Nodejs中运行一个 bower.commands 文件来让你编写安装各种包的node程序,并且可以监听 end 事件在安装结束后进行操作,这是异步的,这样就可以随心所欲的安装包和控制顺序了。

var bower = require('bower');
bower.commands
    .install(['jquery'], { save: true }, { /* custom config */ })
    .on('end', function (installed) {
        console.log(installed);
});

Grunt

Grunt目前来说是这三个Yeoman中最成熟最强大的,最关键的是Grunt有各种各样的插件,可以集成大部分能想得到的开发工具来进行自动化开发。另外Grunt的作者还开发了一整套的插件来适应常规的开发,这套插件以 grunt-contrib- 为前缀(下文中如无特殊说明,均指带有该前缀的插件名),除了文件的基本操作,还包括有测试、编译、压缩、代码检查等各种功能的插件,而且不止一个选择。

安装Grunt和Bower不太一样,需要先在全局安装一个Grunt的客户端,然后在每个项目中安装Grunt。

npm install -g grunt-cli
cd /project/
npm install grunt

不过和Bower相似的是,可以通过编写配置json文件来使用 npm install 来安装Grunt和所有需要的插件,另外Grunt的插件也都是npm管理的,所以可以直接在 package.json 中直接编写。

{
    "name": "myProject",
    "version": "0.1.0",
    "devDependencies": {
        "grunt": "*",
        // other plugin...
        "grunt-contrib-watch": "*"
    }
}

安装完成后在项目根目录中建立 Gruntfile.js 文件来配置Grunt的工作流程。下面以 copy 插件为例使用Grunt进行开发。在 exports 中Grunt会以参数形式被传入函数,它有3个方法, initConfigloadNpmTasksregisterTask,分别用来定义插件操作,载入插件,注册任务。

module.exports = function (grunt) {
    grunt.initConfig({
        copy: {
            main: {
                files: {
                    src: ['path/**'], 
                    dest: 'dest/'
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.registerTask('default', ['copy']);
};

在配置中以插件名为键定义一个Object作为该插件的配置,其中还可以再定义一层以任务名为键,比如 main ,然后是插件的部分,copy插件使用 files 来定义对文件的具体操作, src 是要复制的文件, dest 则是要复制到的路径。

然后使用 loadNpmTasks 加载插件,需要写全名,包括grunt-contrib前缀。

最后是注册一个任务,这里的任务即是执行操作时需要调用的东西。比如代码中注册了 default 任务,包括一个数组中的所有任务,这样在执行default任务时就会执行相应的所有任务。另外default是一个特殊的任务名,如果在执行任务时没有指定名称,则执行该任务。当然直接运行copy任务也是可以的,甚至可以指定一个子任务,比如main。所以下面4行代码是相同的效果。

grunt
grunt default
grunt copy
grunt copy:main

不过需要特别注意的是,注册的任务名不能和原有的任务相同,这样会报错,比如这样:

grunt.registerTask('copy', ['copy']);

和copy类似的文件基本操作还有 clean 清除, concat 连接, rename 重命名, compress 打包, crypt 编码等等,相关的配置可以在npmjs.org上的对应项目介绍中找到。

还有四个用于压缩的插件 htmlmin , cssmin , uglify , imagemin 分别对应HTML文件、CSS文件、JS文件和图片文件;以及两个用于检查代码的插件 csslint , jshint 分别检查CSS代码和JS代码。

当然,最重要的是,Grunt可以编译一些CSS和JS的其他形式代码。coffee 用于编译CoffeeScript,而CSS就更多了,比如SASS可以使用 compass 或者 sass, 还有 lessstylus,我最喜欢的是Stylus,因为它使用的是Javascript来编译,而不像SASS是Ruby编译的,还需要准备Ruby的环境,非常麻烦。而且在Stylus中还可以写类似JS的条件语句和循环语句。这个国旗icon的项目很好的使用了Stylus以很短的代码完成了上百个国家的图标的CSS Sprite - National Flag on Github。还有许多种Javascript模板的预编译插件,haml , jst , jade , hogan 等等。

除了用于编码的插件,还有许多用于测试的插件,在grunt-contrib中提供了三个测试框架的插件, nodeunit 用于Nodejs,qunit 用于Qunit,是来自jQuery团队的测试框架,还有Junit的后继者 jasmine。另外Mocha也有自己的Grunt插件 grunt-mocha 。用于捕获多个浏览器测试框架karma也有相应的插件 grunt-karma

此外,contrib中还有一些其他插件,比如 connect 用于http等协议的请求,支持https, commands 用于执行shell命令, manifest 用于生成离线应用所需的 manifest.appcache 文件,还有用于插件YUI文档的 yuidoc

最最重要的一个插件就是 watch ,它可以随时监听某些指定的文件,当它们发生改变时执行相应的任务。再次使用copy做例子,添加watch任务后可以在原有文件发生改变时,将复制过去的副本也同步改变。

module.exports = function (grunt) {
    grunt.initConfig({
        watch: {
            copy: {
                files: 'path/**',
                tasks: 'copy'
            }
        },
        copy: {
            main: {
                files: {
                    src: ['path/**'], 
                    dest: 'dest/'
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.registerTask('default', ['copy', 'watch']);
};

由此,项目开发中的大部分工作都交由程序代替了人工,Yo和Bower可以快速的启动一个项目,Grunt在开发中可以自动化的持续完成编码中重复性的工作以及自动化检查和测试代码以提高质量。


我的前端开发工作流 系列文章:

原文博客http://www.tychio.net/tech/2013/09/25/improve-workflow.html


Tychio
3.4k 声望139 粉丝

软件工程师,Web前端工程师,高级软件咨询师,远程自由职业开发