lxjwlt's blog

Yeoman简易使用

yeoman是一种工具集,它包含了三个工具,这些工具能简化整个项目流程:

yeoman安装

首先,要确保你已经安装了 Node.jsGitRubyCompass

然后,用以下命令全局安装 yo ,该命令会同时安装 grunt 和 bower:

npm install -g yo

如果安装过程出错,首先检查是否以前有单独安装过grunt或bower的,要先卸载了grunt和bower,再运行以上命令。

bower

基本使用

首先,你需要创建一个 bower.json 文件到项目的根目录,该文件用于说明项目依赖哪些代码库,有了该文件,当运行安装命令时,bower就会安装该文件中所指定的代码库文件。运行以下命令生成 bower.json 文件:

bower init

输入相关信息后,可以在根目录下生成一个 bower.json 文件,此时 bower.json 文件中并未指定任何代码库,该文件内容的结构如下:

{
    "name": "a name",
    "version": "0.0.0",
    "dependencies": {

    }
}

安装需要的代码库,比如jQuery,命令如下:

bower install jquery --save

其中--save参数可省略。设定该参数的作用在于,当安装完jQuery代码库后,bower会自动将该依赖写入bower.json 文件中,此时该文件的内容如下:

{
    "name": "a name",
    "version": "0.0.0",
    "dependencies": {
        "jquery": "~2.0.3"
    }
}

上述命令安装的是最新版本的jQuery库,如果需要下载某个版本的代码库,比如兼容IE6-8的1.10.2版本的jQuery:

bower install jquery#1.10.2 --save

那如何查看代码库有哪些版本呢?我们可以运行以下命令查看:

bower info jquery

在不清楚是否存在某个代码库的时候,我们可以运行以下命令搜索,比如搜索是否有normalize.css

bower search normailize

搜索结果如下:

这样,我们知道了,在bower中normalize.css的名称为normalize-css,知道其名称后我们就可以安装了:

bower install normailize-css --save

此时bower.json 文件中已写入了一定数量的代码库依赖了,那么在下次项目中就可以直接使用这个文件来安装其中指定的代码库了,而不必再次一个一个的重复输入命令行来安装了。运行以下命令安装bower.json文件中的依赖:

bower install

bower设置

.bowerrc文件中可以配置bower的相关属性,最常见的设置是重设代码库存放的路径。默认情况下,代码库存放的路径是根目录下的bower_components文件夹中,如果需要更换存放路径,比如存放到app/bower_components目录下,.bowerrc文件可写成:

{
    "directory": "app/bower_components"
}

更多的设置请参考:bower configuration

注册自己的代码库

如果你有自己的代码库,比如自己写的jQuery插件之类的,那么你可以在bower上注册你的代码库。

首先需要创建一个github仓库,并上传好代码文件,然后用github的标签为你的代码库添加版本号,创建版本号:

git tag v0.0.1

设定最近commit上去的代码库的版本号:

git push origin v0.0.1

版本号的写法可以参考: semver

新建了仓库,设置了版本号后,就可以在bower中注册该代码库,该命令格式为:

bower register <my-package-name> <git-endpoint>

其中<git-endpoint>是该代码库仓库的地址,比如:

bower register lxjwltwebapp git://github.com/lxjwlt/webapp.git

注册好后,就可以通过安装命令来安装了:

bower install lxjwltwebapp

grunt

插件安装

首先,我们需要在项目文件的根目录中创建两个文件,一个是 package.json 文件,一个是 Gruntfile.js 文件。

类似于上述的bower,grunt也需要下载相关的插件,但这些插件的依赖是写入 package.json 文件中的。可以运行以下命令,创建一个基本的 package.json 文件:

npm init

package.json 文件基本结构如下:

{
  "name": "a name",
  "version": "0.0.0",
  "devDependencies": {

  }
}

然后我们需要下载相关的插件,查询相关插件及其用法:grunt plugins,建议用ctrl+F来查询。

从上面的网页中选择你所需要的插件,比如名为contrib-clean的插件,可以运行以下命令安装该插件:

npm install grunt-contrib-clean --save-dev

其中 --save-dev 参数作用是将该依赖写入 package.json 文件中。注意和bower安装命令的不同。

此时插件还不能运行,我们需要对其进行配置。

插件配置

插件的配置需要上述提到的 Gruntfile.js 文件。请在根目录下手动创建该文件,其中的基本结构为:

module.exports = function(grunt){

    // 项目配置
    grunt.initConfig({
        clean: {
            build: {
                files: ["path/to/dir/one", "path/to/dir/two"]
            }
        }
    });

    // 加载'grunt-contrib-clean'任务的插件
    grunt.loadNpmTasks('grunt-contrib-clean');

    // 默认任务
    grunt.registerTask('default', ['clean']);
}

从上往下:

配置对象中的属性名要对应插件的名字,比如grunt-contrib-uglify插件,它在配置对象中对应的属性名为uglify。每个插件又可以有多个子任务,比如上述代码中clean任务下有名为build的子任务。有了子任务,插件就能在不同的情况下完成不同的任务。子任务的名称是任意的,如:

    grunt.initConfig({
        // clean插件用于清除文件
        clean: {
            allFile: {
                // 这里是clean任务'allFile'子任务的选项和文件
            }
        },
        // uglify插件用于压缩javascript代码
        uglify: {
            build: {
                // 这里是uglify任务'build'子任务的选项和文件
            },
            server: {
                // 这里是uglify任务'server'子任务的选项和文件
            }
        }
    });

对于以上代码,当在命令行中运行 grunt uglify,则uglify中的两个子任务buildserver都会被运行,如果只想执行指定的子任务,比如server,可以运行grunt uglify:server

针对不同情况,我们会给插件定义不同的子任务,而每个子任务中的插件配置是不同的,但每一个插件配置无非都是两部分组成:指定文件插件选项

如何指定文件?

指定无映射文件,比如在clean插件中:

clean: {
    build: {
        files: ["path/to/dir/one", "path/to/dir/two"]
    }
}

映射文件,比如:

// 一对一映射
uglify: {
    build: {
        src: 'path/to/dir/index.js',
        dest: 'path/to/dir/index.min.js'
    },
    // 也可以写成
    server: {
        files: {
            'path/to/dir/index.min.js': 'path/to/dir/index.js'
        }
    }
}
// 多对一映射
// concat插件用于合并代码文件
concat: {
    build: {
        src: ['path/to/dir/index.js', 'path/to/dir/home.js'],
        dest: 'path/to/dir/global.js'
    },
    // 也可以写成
    server: {
        files: {
            'path/to/dir/global.js': ['path/to/dir/index.js', 'path/to/dir/home.js']
        }
    }
}

以上均的写法均是指定固定文件的,一旦文件新添或删除,就要修改 Gruntfile.js 文件,所以我们需要动态地指定文件,以uglify插件为例:

uglify: {
    build: {
        // 将app/scripts目录下的所有js文件压缩
        // 并将压缩后的文件放入dist/scripts文件中
        files: {
            expand: true, // 启动动态指定
            cwd: 'app/scripts/', // 所有操作都基于该目录下
            src: '*.js'// 所有js文件
            dest: 'dist/scripts',
            ext: '.min.js' // 定义后缀
        }
    }
    // 以下写法与上述效果相同
    // 注意 cwd src dest 的写法!!
    server: {
        files: {
            expand: true,
            cwd: 'app/',
            src: 'scritps/*.js',
            dest: 'dist/',
            ext: '.min.js'
        }
    }
}

插件选项

插件会提供一定的选项,我们能通过options属性来定义这些选项,如uglify插件:

uglify: {
    build: {
        options: {
            // 在每个压缩的js文件开头添加注释
            banner: '/* this is a compressed file */ \n'
        },
        files: {
            // …… ……
        }
    }
}

下面介绍一些常用插件及其用法。

常用插件用法

首先提一句,插件的具体用法可以在插件详情页中查询:grunt plugins

connect && watch

前端开发的过程中,在修改了文件后,比如css,我们每每都需要在浏览器按F5来查看页面效果,这样的操作非常繁琐,而connect插件和watch插件可以帮我们监控文件改动并自动刷新页面,设置如下:

watch: {
    livereload: {
        options: {
            // 配置对象中的属性值可通过以下语法获取 '<%= %>'
            // 此处的livereload值需要同connect中的一致,此时为35729
            livereload: '<%= connect.options.livereload %>'
        },
        // 监控如下文件
        files: [
            'app/*.html',
            'app/styles/{,*/}*.css',
            'app/scripts/{,*/}*.js',
            'app/images/{,*/}*.{gif,jpeg,jpg,png,svg,webp}'
        ]
    }
},
connect: {
    // 总选项
    options: {
        keepalive: true,
        port: 9000,
        livereload: 35729,
        hostname: 'localhost'
    },
    livereload: {
        // 子任务选项
        options: {
            // 运行任务后自动在浏览器中打开该页面
            open: true,
            // 定义启动服务的目录
            base: ['app/']
        }
    }
}

pngmin

对于图片压缩插件,官方推荐的是grunt-contrib-imagemin插件,该插件支持所有格式的图片的压缩,但在win7 64位中,该插件的部分功能无法安装,导致压缩不能最大化,平均只能压缩6-7%。

在grunt插件库中,找到能用的就只有pngmin插件了,该插件只支持压缩png格式的图片,但前端开发中一般只用png格式的图片,而且该插件可调整图片的压缩比率,非常强大,基本用法如下:

pngmin: {
    options: {
        // 设置压缩65-80%,即图片压缩后的大小为20-35%
        // 此为推荐值
        quality: '65-80',
        // 设置后缀,因为默认后缀很奇葩
        ext: '.png'
    },
    build: {
        expand: true,
        cwd: 'app',
        src: 'images/*.png',
        dest: 'dist'
    }
}

其余一些推荐插件

yo

yo工具融合了grunt和bower二者,它负责初始化整个项目,我们可以根据不同的情况自定义项目文件的内容,定义项目文件的结构等等。

yo初始化项目需要用到yeoman特有的模板,该模板称之为generator,generator包含了项目所需要的一切文件。可以说,项目文件的初始化都是基于该模板所进行的。

yeoman提供了很多已经定义好的generator,比如generator-webapp,我们可以运行以下命令安装:

npm install -g generator-webapp

该generator生成项目文件过程中,我们可以选择是否下载HTML5 Boilerplate、 jQuery、 Modernizr、或 Bootstrap这些文件以供项目使用,运行以下命令使用generator-webapp生成项目文件:

yo webapp

虽然yeoman提供了许多这样的generator方便我们使用,但这些generator不一定符合我们项目的需求,必要时,我们需要知道如何创建自己的generator。

如何创建自己的generator?

首先我们需要下载模板的模板:generator-generator,运行以下命令安装该模板:

npm install -g generator-generator

因为这是模板的模板,所以安装该模板会生成一个基本的模板,请创建一个新的目录,进入该目录运行:

yo generator

在生成模板的过程中,yo会提示你输入该模板的名称(默认名称为当前目录的名),比如名为project

在安装generator-project模板前,请先运行:

npm link

这样你就可以通过运行yo project来基于该模板生成项目文件了。但由于我们还没给该模板进行配置,生成的项目文件是没有任何实用意义的。

配置generator

我们粗略地看看project模板中的文件结构:

├── app
│   ├── index.js
│   └── templates
│       ├── _bower.json
│       ├── _package.json
│       ├── editorconfig
│       ├── jshintrc
│       └── travis.yml
├── test
│   ├── test-creation.js
│   └── test-load.js
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── LICENSE
├── package.json
└── README.md

我们需要关注的是其中的:

我们将项目相关的配置文件都放置到app/templates/目录下,然后打开app/index.js文件,其中关键的是:

util.inherits(ProjectGenerator, yeoman.generators.Base);
ProjectGenerator.prototype.askFor = function askFor() {
    // …… ……
};
ProjectGenerator.prototype.app = function app() {
    // …… ……
};
ProjectGenerator.prototype.projectfiles = function projectfiles() {
    // …… ……
};

第一句代码让ProjectGenerator继承了yeoman.generators.Base,这就是为什么紧接下来的代码中能调用许多有用的函数来帮助我们创建项目。

接下来是给ProjectGenerator原型添加成员函数,项目创建的流程就是在这些成员函数中定义的,当然,如果有必要你可以任意修改这些函数的名称,或增添新的函数,这些函数会按照从上往下的顺序依次执行

下面,假设我们项目创建流程:

  1. 询问是否添加Boorstrap、modernizr、requireJS……
  2. 根据询问结果重构配置文件
  3. 创建项目目录
  4. 将不需要重构的文件拷贝到项目中

我们先来看askFor函数,其中有两部分最为关键:

var prompts = [{
    // 定义询问的类型及相关选项
    // …… ……
}, {
    // 可以定义任意数量的询问
    // …… ……
}];

this.prompt(prompts, function (answers) {
    // 定义一个回调函数
    // 因为bind绑定的缘故,此间的this为ProjectGenerator
    // …… ……
}.bind(this));

prompts数组中定义询问的类型,然后将该数组传入到ProjectGeneratorprompt方法中,其后,又传入一个回调函数,该函数中对询问结果(询问的值)进行处理。

prompt询问的详细用法请参考:Inquirer

下面是prompt范例,询问用户是否添加Boorstrap、modernizr、requireJS:

var prompts = [{
    // 询问的类型
    type: 'checkbox',
    // 询问的名称
    name: 'features',
    message: 'What more would you like?',
    // 这里定义三个选项
    // 对于checkbox类型的询问下的选项来说,选中为true,否则为false
    // 所有为true的选项的value值会组成一个字符串,该询问的值就是这个字符串
    choices: [{
      name: 'Bootstrap for Sass',
      value: 'includeBootstrap',
      checked: false
    }, {
      name: 'Modernizr',
      value: 'includeModernizr',
      checked: false
    }, {
      name: 'RequireJS',
      value: 'includeRequirejs',
      checked: false
    }]
  }];

  this.prompt(prompts, function (answers) {
    // 获取名为features的询问的值
    var features = answers.features;

    // 该函数用于解析checkbox类型的询问的值
    function hasFeature(feat) { return features.indexOf(feat) !== -1; }

    // 将解析出来的值存入ProjectGenerator中
    this.includeRequirejs = hasFeature('includeRequirejs');
    this.includeModernizr = hasFeature('includeModernizr');
    this.includeBootstrap = hasFeature('includeBootstrap');

    cb();
  }.bind(this));

在回调函数中,我们获取到了询问的返回值,并已经存入了ProjectGenerator,那接下来我们要做的就是利用这些值来重构配置文件。

在yeoman中,我们使用template方法来重构这些文件的:

this.template('_bower.json', 'bower.json');

template方法会遍历第一个参数文件(上述例子中是_bower.json),并运行Lo-Dash解析该文件,解析后得到第二个参数文件,将其放入项目文件根目录。

template方法解析的文件中可以使用<% %>语句,该语句中能输入任意的javscript语句,而且ProjectGenerator的属性可以直接以变量的形式使用在该语句中,这意味着我们现在能用前面获取到的询问的值来重构bower.json文件了,将_bower.json文件修改为:

{
  "name": "myProject",
  "version": "0.0.0",
  "dependencies": {
    "normalize-css": "~2.1.3",
    "jquery": "=1.10.2"<% if (includeRequirejs) { %>,
    "requirejs": "~2.1.9"<% } %><% if (includeBootstrap) { %>,
    "sass-bootstrap": "~3.0.2"<% } %><% if (includeModernizr) { %>,
    "modernizr": "~2.7.1"<% } %>
  }
}

如此,bower就能根据用户的选择来决定下载哪些代码库文件了。

所有文件都能用这种方法来重构,HTML模板文件和Gruntfile.js文件也不例外,但在win7 64位系统下,对Gruntfile.js文件的重构貌似会出错。

我们已经知道如何重构配置文件了,接下来只剩目录创建和文件拷贝了。

目录创建用到mkdir方法,文件拷贝copy方法,copy方法与template方法的不同在于,copy方法是单纯的拷贝复制,不会重构该文件。下面是两种方法的范例:

ProjectGenerator.prototype.app = function app() {
    // 创建项目目录
    this.mkdir('app');
    this.mkdir('app/scripts');
    this.mkdir('app/styles');
    this.mkdir('app/images');
    this.mkdir('app/fonts');
    this.mkdir('app/scss');
};

// 这函数中的操作可以写入前面的app函数中
// 但通常,我们会根据功能的不同,将这些操作分开来写,这样使条理清晰,且便于管理
ProjectGenerator.prototype.moveFiles = function moveFiles() {
    // 文件拷贝,
    this.copy('_package.json', 'package.json');
    this.copy('Gruntfile.js');
    this.copy('bowerrc', '.bowerrc');
};

现在我们可以运行yo project来生成项目文件,当生成完成后,我们进入项目目录,我们可以发现,bower已经自动下载好了所有的代码库文件,而且grunt的所有依赖也下载完成,我们现在可以直接运行grunt server等等grunt命令来进行开发了。所有这一切都归功于yo配置文件开头的一句代码:

// 当初始化结束时运行
this.on('end', function () {
    // 安装依赖
    this.installDependencies({ skipInstall: options['skip-install'] });
});

呼,yeoman的基本用法终于介绍完了,如有不清楚的地方可以参考我的 generator-lxjwlt

参考