前端开发环境搭建 grunt bower、requirejs 、 anjular

现在web开发的趋势是前后端分离。前端采用某些js框架,后端采用某些语言提供restful API,两者以json格式进行数据交互。

如果后端采用node.js,则前后端可以使用同一种语言,共享某些可重用的Js代码,并共享构建工具。但很多时候我们可能采用别的语言,如ruby/java/scala等,此时前后端代码基本上是完全独立的。虽然大家都在同一个项目中,但可以分成互相独立的两块,并且前后端通常使用不同的构建工具。

比如当后端使用Scala时,我们会使用sbt进行项目构建,对scala代码进行编译、测试、打包等。它的专长是处理与scala相关的任务,但对于前端的支持比较弱。前端一些常见的任务,如js库的下载和管理、css文件的转换、js文件合并压缩、js测试的执行等,很难在sbt中找到好用的插件,而利用js世界里的工具来做反而更加方便一些。

我们项目组这几天正在讨论是否在项目中引入一些前端框架,还是直接用原生Javascript写。经过反复讨论和调研,最终决定引入AngularJs。但AngularJs的引入并不是单一的任务,因为我们还需要考虑前端代码的测试、依赖管理等,都需要有相应的工具支持,所以最后引入了这么一整套工具:

  1. Grunt – Js任务管理工具,通过各种插件对项目进行各种操作,比如文件转换、运行测试、打包部署等。相当于java里的ant/maven/gradle,ruby中的rack,scala中的sbt。
  2. Bower – Js库依赖管理工具。当你需要jquery时,不需要手动下载,只需要执行bower install jquery
  3. RequireJs – Js库加载管理,及模块化支持。可以按需及并行加载js库,可以把我们的代码以模块化的方式组织。
  4. AngularJs – Js前端框架,支持依赖注入,双向绑定等我认为很重要的功能

这套东西都是比较基础且使用比较广泛的。一般一旦在项目中引入前端框架,或者需要写比较多的Js代码时,我们都会采用它们,所以很有必要学习并掌握它们。

各工具都相当的独立

在开始前,需要先提示一下,在javascript的世界里,很多东西都是由社区提供的,所以每一种工具都相当独立。比如,很多工具都有着自己独立的配置文件,自己的命令行参数,有时候还需要有一些额外的插件把两个工具结合起来。

所以下面将会有很多比较琐碎的命令,我们需要一一了解。不过好在我们一旦了解了,下次就可以使用已经配置好的文件,通过几条命令将把有的东西都准备好,很方便。

安装nodejs

在Mac中,我们可以使用brew来安装。在其它系统下,请使用相应的工具或直接到官网下载。

brew install node

Nodejs可以让我们在服务器端使用javascript编程,它是很多js工具的基础。如果你已经安装,请确保使用最新版本:

brew upgrade node

查看版本:

node -v

我这里显示:

v0.10.28

npm

Npm是node官方提供的包依赖管理工具。我们下面使用的grunt等,都是以插件形式下载安装的。

当我们安装好nodejs后,npm就自动可用了。

查看版本:

npm -v

我这里显示:

2.0.0-alpha-5

创建项目目录

下面我们从零开始,首先在任意位置新建一个目录作为我们的项目根目录,比如:

mkdir ~/myproject

然后进入该目录:

cd ~/myproject

后面的命令都将在项目根目录下操作。

为npm创建package.json

首先我们需要为npm提供一个package.json,告诉它我们的项目信息,特别是项目中将会使用的插件。我们不需要手动创建,因为可以直接调用以下命令:

npm init

它会问我们一些问题,我们可以按需回答,也可以全部使用默认值,反正以后可以改起来也很容易。

最后将会产生如下的package.json文件:

{
  "name": "grunt-bower-angular-demo",
  "version": "0.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/freewind/grunt-bower-angular-demo.git"
  },
  "author": "",
  "license": "BSD",
  "bugs": {
    "url": "https://github.com/freewind/grunt-bower-angular-demo/issues"
  }
}

对于像我们这样的非nodejs项目来说,里面的大部分内容都没用,可以删掉大部分,只剩下:

{
  "name": "grunt-bower-angular-demo",
  "version": "0.0.0"
}

安装 grunt

npm install grunt --save-dev

将使用npm下载grunt插件,它们将保存到项目根目录下的node_components目录下。

后面的--save-dev参数是说,把这个插件信息,同时添加到package.jsondevDependencies中:

"devDependencies": {
  "grunt": "~0.4.5"
}

由于grunt仅在开发阶段使用,所以使用--save-dev。如果是运行时使用的,则用--save

安装 grunt-cli

上面安装的grunt并不包含命令行工具,我们还需安装相应的grunt-cli,才能在命令行中调用grunt命令:

npm install grunt-cli -g

后面的-g是说,把grunt-cli安装成全局工具,以便在任意目录下使用。

安装后,输入:

grunt --version

我这里显示为:

grunt-cli v0.1.13
grunt v0.4.5

在比较少的情况下,可能提示找不到grunt,则需要根据安装grunt-cli时的提示信息,把相应的路径添加到PATH中:

echo PATH=$PATH:/your/path/to/grunt >> ~/.bashrc
source ~/.bashrc

为grunt创建配置文件Gruntfile.js

安装grunt-init

npm install grunt-init -g

下载grunt模板

git clone https://github.com/gruntjs/grunt-init-gruntfile.git ~/.grunt-init/gruntfile

生成Gruntfile

grunt-init gruntfile

根据需要回答问题,或者使用默认值,将得到以下Gruntfile.js文件:

/*global module:false*/
module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>
' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */
',
    // Task configuration.
    concat: {
      options: {
        banner: '<%= banner %>',
        stripBanners: true
      },
      dist: {
        src: ['lib/<%= pkg.name %>.js'],
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    uglify: {
      options: {
        banner: '<%= banner %>'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: 'dist/<%= pkg.name %>.min.js'
      }
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        unused: true,
        boss: true,
        eqnull: true,
        browser: true,
        globals: {
          jQuery: true
        }
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      lib_test: {
        src: ['lib/**/*.js', 'test/**/*.js']
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    watch: {
      gruntfile: