• Posts tagged "grunt"

Blog Archives

用Highcharts轻松构建交互性图表

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-highcharts/

nodejs-highcharts

前言

在大数据时代,数据可视化技术显得至关重要。一个设计优雅的信息图,能在一个600px*400px的画布上,显示出各种行业的全球市场的几百万条数据。

“如果图像能通过数据驱动,如果图像能动起来,如果图像支持交互,如果还能画股票K线图”,那才我想要的。Highchart是一个基于Javascript的跨浏览器的图形可视化工具,能满足我的需求。

目录

  1. highchart介绍
  2. highchart项目构建
  3. highchart使用
  4. highstock使用
  5. 项目源代码

1. highchart介绍

Highcharts是一个非常流行界面美观的纯Javascript图表库。它主要包括两个部分:Highcharts和Highstock。

Highcharts能够很简单便捷的在web网站或是web应用程序添加有交互性的图表,并且免费提供给个人学习、个人网站和非商业用途使用。目前HighCharts支持的图表类型有曲线图、区域图、柱状图、饼状图、散状点图和综合图表。

Highstock可以为您方便地建立股票或一般的时间轴图表。它包括先进的导航选项,预设的日期范围,日期选择器,滚动和平移等等。

官方网站:http://www.highcharts.com

中文资源:http://www.highcharts.me

2. highchart项目构建

系统环境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19

初始化项目


~ D:\workspace\javascript>mkdir highchart && cd highchart 

~ D:\workspace\javascript\highchart>bower search highchart
Search results:

    highcharts git://github.com/ewen/bower-highcharts.git
    highcharts.com git://github.com/highslide-software/highcharts.com.git
    angular-highcharts-directive git://github.com/rootux/angular-highcharts-directive.git
    highcharts-components git://github.com/nesbert/highcharts.git
    highcharts-ng git://github.com/pablojim/highcharts-ng.git
    angular-highcharts git://github.com/frapontillo/angular-highcharts.git

~ D:\workspace\javascript\highchart>bower install highcharts.com
bower highcharts.com#*      not-cached git://github.com/highslide-software/highcharts.com.git#*
bower highcharts.com#*         resolve git://github.com/highslide-software/highcharts.com.git#*
bower highcharts.com#*        download https://github.com/highslide-software/highcharts.com/archive/v3.0.7.tar.gz
bower highcharts.com#*         extract archive.tar.gz
bower highcharts.com#*        resolved git://github.com/highslide-software/highcharts.com.git#3.0.7
bower highcharts.com#~3.0.7    install highcharts.com#3.0.7

highcharts.com#3.0.7 bower_components\highcharts.com

我们通过bower来下载highchart库,有关bower的安装和使用,请参考文章:bower解决js的依赖管理

通过Ant工具构建highcharts. Ant是一个用Java开发的自动化构建工具。


~ cd bower_components\highcharts.com

~ ant build
Buildfile: D:\workspace\javascript\highchart\bower_components\highcharts.com\build.xml

set.properties:

clean:
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\js\adapters
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\js\modules
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highcharts\gfx
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highcharts\js\adapters
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highcharts\js\modules
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highstock\gfx
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highstock\js\adapters
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\dist\highstock\js\modules
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\tests
    [mkdir] Created dir: D:\workspace\javascript\highchart\bower_components\highcharts.com\build\coverage-report

assemble:

check-crlf:

do-lint:

lint:

minify:

compile:
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highcharts.src.js
     [echo] Google Closure compiler: 463267 bytes -> 141575 bytes (52163 bytes zipped).
     [echo] Yahoo YUI Compressor: 463267 bytes -> 155872 bytes (57484 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highstock.src.js
     [echo] Google Closure compiler: 579013 bytes -> 176349 bytes (64166 bytes zipped).
     [echo] Yahoo YUI Compressor: 579013 bytes -> 194135 bytes (70231 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highcharts-more.src.js
     [echo] Google Closure compiler: 62849 bytes -> 21926 bytes (8265 bytes zipped).
     [echo] Yahoo YUI Compressor: 62849 bytes -> 22532 bytes (8682 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/highcharts-more.src.js
     [echo] Google Closure compiler: 62849 bytes -> 21926 bytes (8265 bytes zipped).
     [echo] Yahoo YUI Compressor: 62849 bytes -> 22532 bytes (8682 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/adapters/mootools-adapter.src.js
     [echo] Google Closure compiler: 8270 bytes -> 2406 bytes (1230 bytes zipped).
     [echo] Yahoo YUI Compressor: 8270 bytes -> 2338 bytes (1189 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/adapters/prototype-adapter.src.js
     [echo] Google Closure compiler: 9270 bytes -> 3330 bytes (1487 bytes zipped).
     [echo] Yahoo YUI Compressor: 9270 bytes -> 3251 bytes (1407 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/adapters/standalone-framework.src.js
     [echo] Google Closure compiler: 12261 bytes -> 5087 bytes (2328 bytes zipped).
     [echo] Yahoo YUI Compressor: 12261 bytes -> 5223 bytes (2318 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/exporting.src.js
     [echo] Google Closure compiler: 18002 bytes -> 7264 bytes (3266 bytes zipped).
     [echo] Yahoo YUI Compressor: 18002 bytes -> 7204 bytes (3332 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/data.src.js
     [echo] Google Closure compiler: 16086 bytes -> 4436 bytes (2048 bytes zipped).
     [echo] Yahoo YUI Compressor: 16086 bytes -> 4486 bytes (2066 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/drilldown.src.js
     [echo] Google Closure compiler: 11421 bytes -> 5553 bytes (2109 bytes zipped).
     [echo] Yahoo YUI Compressor: 11421 bytes -> 5678 bytes (2161 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/funnel.src.js
     [echo] Google Closure compiler: 6824 bytes -> 1974 bytes (1226 bytes zipped).
     [echo] Yahoo YUI Compressor: 6824 bytes -> 1885 bytes (1180 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/no-data-to-display.src.js
     [echo] Google Closure compiler: 3009 bytes -> 1379 bytes (846 bytes zipped).
     [echo] Yahoo YUI Compressor: 3009 bytes -> 1185 bytes (729 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/map.src.js
     [echo] Google Closure compiler: 34651 bytes -> 12719 bytes (5188 bytes zipped).
     [echo] Yahoo YUI Compressor: 34651 bytes -> 12889 bytes (5350 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/heatmap.src.js
     [echo] Google Closure compiler: 1195 bytes -> 579 bytes (511 bytes zipped).
     [echo] Yahoo YUI Compressor: 1195 bytes -> 594 bytes (519 bytes zipped).
     [echo]
     [echo] Minifying D:\workspace\javascript\highchart\bower_components\highcharts.com/js/modules/annotations.src.js
     [echo] Google Closure compiler: 8776 bytes -> 3398 bytes (1668 bytes zipped).
     [echo] Yahoo YUI Compressor: 8776 bytes -> 3535 bytes (1777 bytes zipped).
     [echo]
     [echo] Minifying build/canvas-tools.src.js
     [echo] Google Closure compiler: 100698 bytes -> 57894 bytes (15848 bytes zipped).
     [echo] Yahoo YUI Compressor: 100698 bytes -> 59764 bytes (16054 bytes zipped).
     [echo]

BUILD SUCCESSFUL
Total time: 1 minute 19 seconds

在新生成的build/dist文件夹中,就能找到highchart.js, highstock.js,这两个用于发布的文件了。

highchart-build

用grunt创建一个静态服务器,grunt的使用说明,请参考文章:grunt让Nodejs规范起来

grunt的运行时环境,新建文件:package.json,Gruntfile.js


~ npm install grunt
~ npm install grunt-contrib-connect

~ vi packages.json
{
    "name": "nodejs-highcharts",
    "version": "0.0.1",
    "private": true,
    "scripts": {
        "start": "node app.js"
    },
    "dependencies": {
    },
    "devDependencies": {
        "grunt": "~0.4.1",
        "grunt-contrib-connect": "~0.4.1"
    }
}

~ vi Gruntfile.js
'use strict';
module.exports = function(grunt) {
    grunt.initConfig({
      connect: {
        server: {
          options: {
            port: 9001,
            base: './',
            keepalive: true
          }
        }
      }
    });

    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.registerTask('server', ['connect']);
};

新建一个测试文件:demo1.html


<!DOCTYPE html>
<html>
<head>
<title>HighCharts</title>
<script src="bower_components/highcharts.com/lib/jquery-1.10.1.js"></script>
<script src="bower_components/highcharts.com/build/dist/highcharts/js/highcharts.js"></script>
</head>
<body>

<div id="container" style="height: 400px"></div>
<script>
$(function () {
Highcharts.setOptions({
colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4']
});

$('#container').highcharts({
chart: {
type: 'pie'
},

series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}]
});
});
</script>
</body>
</html>

由于highcharts是依赖于jQuery的,所以我们也需要引入jquery.js

启动grunt server


~ D:\workspace\javascript\highchart>grunt server
Running "connect:server" (connect) task
Waiting forever...
Started connect web server on 127.0.0.1:9001.

浏览器打开:http://localhost:9001/demo1.html

highchart-pie

3. highchart使用

找到highchart的官网,查看demo:http://www.highcharts.com/demo/,支持好几十种可视化图形。

我最常用的是曲线图:

新建文件

  • line.html:网页文件
  • line.js:脚本文件,进行简单封装
  • line.json:数据文件

安装moment类库,用于时常日期转换

~ bower install moment

json/line.json


[
    {
        "name": "TF1312",
        "data": [
            [20131104091540,4.52],
            [20131104091600,4.51],
            [20131104091740,4.18],
            [20131104091843,4.26],
            [20131104091900,4.23],
            [20131104092053,4.2],
            [20131104092138,4.28],
            [20131104092256,4.83],
            [20131104092307,4.83],
            [20131104092439,4.88],
            [20131104092556,7.1],
            [20131104092623,7.12]
        ]
    },
    {
        "name": "TF1403",
        "data": [
            [20131104091540,4.23],
            [20131104091600,4.22],
            [20131104091740,4.11],
            [20131104091843,4.14],
            [20131104091900,4.13],
            [20131104092052,4.12],
            [20131104092126,4.15],
            [20131104092222,4.33],
            [20131104092306,4.35],
            [20131104092440,4.37],
            [20131104092556,5.1],
            [20131104092600,5.1]
        ]
    },
    {
        "name": "TF1406",
        "data": [
            [20131104091540,3.92],
            [20131104091600,3.92],
            [20131104091700,3.92],
            [20131104091800,3.92],
            [20131104091900,3.92],
            [20131104092052,3.88],
            [20131104092110,3.9 ],
            [20131104092254,3.8],
            [20131104092302,3.89],
            [20131104092400,3.9],
            [20131104092556,4.43],
            [20131104092634,4.44]
        ]
    }
]

line.html


<!DOCTYPE html>
<html>
<head>
<title>HighCharts - SplineChart</title>
<script src="bower_components/highcharts.com/lib/jquery-1.10.1.js"></script>
<script src="bower_components/moment/moment.js"></script>
<script src="bower_components/highcharts.com/build/dist/highcharts/js/highcharts.js"></script>
</head>
<body>
<div id="container" style="height: 400px"></div>
<script src="js/line.js"></script>
<script>
createSpline('#container','json/line.json',true);
</script>
</body>
</html>

js/line.js


/**
 * hightcharts global
 */
Highcharts.setOptions({
    global: {useUTC: false}
});

/**
 * my credits
 */
var defaultCredits = {
    href: 'http://blog.fens.me',
    position: {x: -30, y: -30},
    style: {color: '#191a37', fontWeight: 'bold'},
    text: 'http://blog.fens.me'
}

/**
 * transfer Datetime: 20130101101010 to time
 */
function transferDate(json) {
    for (var i = 0; i < json.length; i++) {
        var obj = json[i].data;
        for (var j = 0; j < obj.length; j++) {
            obj[j][0] = moment(obj[j][0], 'YYYYMMDDHHmmss').toDate().getTime();
        }
    }
    return json;
}

/**
 * Spline Chart Template
 */
function getSplineChart(series) {
    return {
        chart: {
            type: 'spline',
            animation: Highcharts.svg,
            marginRight: 10
        },
        title: {
            text: 'Title'
        },
        credits: defaultCredits,
        xAxis: {
            maxPadding: 0.05, minPadding: 0.05, type: 'datetime', tickWidth: 5,
            labels: {
                formatter: function() {
                    return Highcharts.dateFormat('%H:%M:%S', this.value);
                }
            }
        },
        yAxis: {
            title: {text: 'yAxis'},
            plotLines: [
                {value: 0, width: 1, color: '#808080'}
            ]
        },
        tooltip: {
            formatter: function () {
                return '<b>' + this.series.name + '</b><br/>' +
                    Highcharts.dateFormat('%H:%M:%S', this.x) + '<br/>' +
                    Highcharts.numberFormat(this.y, 2);
            }
        },
        legend: {enabled: true},
        exporting: {enabled: false},
        plotOptions: {
            spline: {
                lineWidth: 4, states: {hover: {lineWidth: 5}},
                marker: {enabled: false},
                pointInterval: 3600000,
                pointStart: Date.UTC(2009, 9, 6, 0, 0, 0)
            }
        },
        series: series
    };
}

/**
 * Create a spline
 */
function createSpline(id,url,transDate){
    $.get(url, function (json) {
        if(transDate||false) json=transferDate(json);
        $(id).highcharts(getSplineChart(json));
    });
}

查看效果图

highchart-line

4. highstock使用

到highchart的官网,查看stock demo:http://www.highcharts.com/stock/demo/,同样支持好多种可视化图形。

选择K线图(candlestick)实验:

新建文件:

  • candlestick.html:网页文件
  • candlestick.js:脚本文件,进行简单封装
  • candlestick.json:数据文件

json/candlestick.json, 部分数据


[
[1162771200000,78.95,80.06,78.43,79.71,15525782],
[1162857600000,80.45,81.00,80.13,80.51,18788494],
[1162944000000,80.02,82.69,79.89,82.45,24699702],
[1163030400000,82.90,84.69,82.12,83.34,32973747],
[1163116800000,83.55,83.60,82.50,83.12,13358721],
[1163376000000,83.22,84.45,82.64,84.35,16105532],
[1163462400000,84.80,85.00,83.90,85.00,21054939],
[1163548800000,85.05,85.90,84.00,84.05,23522893],
[1163635200000,84.87,86.30,84.62,85.61,24796743],
[1163721600000,85.14,85.94,85.00,85.85,16667310],
[1163980800000,85.40,87.00,85.20,86.47,20394365],
[1164067200000,87.42,88.60,87.11,88.60,22274133],
[1164153600000,88.99,90.75,87.85,90.31,24013531],
[1164326400000,89.53,93.08,89.50,91.63,18538136],
[1164585600000,92.51,93.16,89.50,89.54,38411382],
[1164672000000,90.36,91.97,89.91,91.81,37039840]
]

candlestick.html


<!DOCTYPE html>
<html>
<head>
<title>HighStock - Candlestick</title>
<script src="bower_components/highcharts.com/lib/jquery-1.10.1.js"></script>
<script src="bower_components/moment/moment.js"></script>
<script src="bower_components/highcharts.com/build/dist/highstock/js/highstock.js"></script>
</head>
<body>
<div id="container" style="height: 500px"></div>
<script src="js/candlestick.js"></script>
<script>
createCandlestick('#container','json/candlestick.json');
</script>
</body>
</html>

js/candlestick.js


/**
 * my credits
 */
var defaultCredits = {
    href: 'http://blog.fens.me',
    position: {x: -30, y: -30},
    style: {color: '#191a37', fontWeight: 'bold'},
    text: 'http://blog.fens.me'
}
/**
 * Paser Origin Data to Chart
 */
function paserStockData(data,transferDate){
    var ohlc = [],
        volume = [];

    for (var i = 0; i < data.length; i++) {
        if(transferDate||false){
            data[i][0] = moment(data[i][0], 'YYYYMMDDHHmmss').toDate().getTime();
        }

        ohlc.push([
            data[i][0], // the date
            data[i][1], // open
            data[i][2], // high
            data[i][3], // low
            data[i][4] // close
        ]);

        volume.push([
            data[i][0], // the date
            data[i][5] // the volume
        ])
    }

    var groupingUnits = [
        ['week',[1]],
        ['month',[1, 2, 3, 4, 5, 6]]
    ];

    var series=[{
        type: 'candlestick',
        name: 'Stock',
        data: ohlc,
        dataGrouping: {
            units: groupingUnits
        }
    }, {
        type: 'column',
        name: 'Volume',
        data: volume,
        yAxis: 1,
        dataGrouping: {
            units: groupingUnits
        }
    }];
    return series;
}

/**
 * Spline Chart Template
 */
function getCandlestick(series) {
    return {
        rangeSelector: {
            selected: 1
        },
        credits:defaultCredits,
        title: {
            text: 'Title'
        },
        yAxis: [{
            title: {
                text: 'Price'
            },
            height: 200,
            lineWidth: 2
        }, {
            title: {
                text: 'Volume'
            },
            top: 300,
            height: 100,
            offset: 0,
            lineWidth: 2
        }],
        series:series
    }
}

/**
* Create a Candlestick
*/
function createCandlestick(id,url,transDate){
    $.get(url, function (json) {
        json=paserStockData(json,transDate);
        $(id).highcharts('StockChart',getCandlestick(json));
    });
}

查看效果图
highchart-candlestick

5. 项目源代码

项目已经上传到github,感兴趣的同学自行下载。

https://github.com/bsspirit/highcharts-demo

转载请注明出处:
http://blog.fens.me/nodejs-highcharts/

打赏作者

Stylus让CSS也能编程

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-stylus-css/

stylus

前言

到目前为止,前端开发三个最重要的部分HTML, Javascript, CSS中,不管是文档还是工具,对于CSS的支持是最少的。在HTML5+CSS3的时代,我们要更加懂CSS,要让CSS也成为开发的利器。

对于开发来说,CSS的弱点在于静态化。我们不得不背CSS的各种规范,然后一字一句的编写文件。我们需要一个真正能提高开发效率的工具,LESS, SASS都在这方面做了一些贡献。

Stylus比LESS更强大,而且基于nodejs比SASS更符合我们的思路。

目录

  1. Stylus介绍
  2. Stylus安装
  3. Stylus语法
  4. 在Express3项目中使用Stylus
  5. 基于grunt自动处理

1. Stylus介绍

Stylus是一个CSS的预处理框架,Stylus是一种新型语言,可以创建健壮的、动态的、富有表现力的CSS。

什么是CSS预处理技术?CSS预处理技术,是指用一种新语言用来为CSS 增加可编程的的特性,无需考虑浏览器的兼容性问题。你可以在 CSS 中使用变量、简单的程序逻辑、函数等等在编程语言中的一些基本技巧,可以让你的 CSS 更见简洁,适应性更强。

Stylus默认使用 .styl 的作为文件扩展名,支持多样性的CSS语法。

2. Stylus安装

系统环境:

win7 64bit, node v0.10.5, npm 1.2.19

我们直接基于一个Express3的项目进行安装。express的入门,请参考文章:Nodejs开发框架Express3.0开发手记–从零开始

~ D:\workspace\javascript>express -e nodejs-stylus
~ D:\workspace\javascript>cd nodejs-stylus && npm install

~ D:\workspace\javascript\nodejs-stylus>npm install stylus
stylus@0.37.0 node_modules\stylus
├── debug@0.7.2
├── cssom@0.2.5
├── mkdirp@0.3.5
└── sax@0.5.5

配置Stylus的测试环境

增加两个文件:

  • stylus.js: 启动命令,渲染stylus的文件,在命令打印渲染后的结果
  • stylus.styl: stylus可编程的样式文件

增加stylus.js文件


~ vi stylus.js

var stylus = require("stylus"),
    str = require("fs").readFileSync("public/stylus/stylus.styl", "utf8");

stylus.render(str,function (err, css) {
    if (err) throw err;
    console.log(css);
});

增加public/stylus/stylus.styl文件


~ vi public/stylus/stylus.styl

border-radius()
  -webkit-border-radius: arguments
  -moz-border-radius: arguments
  border-radius: arguments

body a
  display inline-block
  padding: 2px 5px
  font: 12px/1.4 Arial, sans-serif
  background: black
  color: #eee
  border-radius: 2px

form input
  padding: 5px
  border: 1px solid
  border-radius: 5px

运行stylus.js的命令


~ D:\workspace\javascript\nodejs-stylus>node stylus.js

body a {
  display: inline-block;
  padding: 2px 5px;
  font: 12px/1.4 Arial, sans-serif;
  background: #000;
  color: #eee;
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  border-radius: 2px;
}
form input {
  padding: 5px;
  border: 1px solid;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}

我们看到了stylus的渲染效果,通过可编程的stylus.styl文件,生成标准的CSS的样式。接下来,我们继续看Stylus的基本使用。

3. Stylus语法

1). 选择器(Selectors)


body
  color white
==> 
body {
  color: #fff;
}

pad(n)
  padding (- n)
body
  pad(5px)
==> 
body {
  padding: -5px;
}

foo bar baz,
form input,
> a
  border 1px solid
==> 
foo bar baz,
form input,
> a {
  border: 1px solid;
}

textarea
input
  color #A7A7A7
  &:hover
    color #000
==> 
textarea,
input {
  color: #a7a7a7;
}
textarea:hover,
input:hover {
  color: #000;
}

2). 变量(Variables)


font-size = 14px
body
  font font-size Arial, sans-seri
==>
body {
  font: 14px Arial, sans-seri;
}

#logo
  position: absolute
  top: 50%
  left: 50%
  width: 150px
  height: 80px
  margin-left: -(@width / 2)
  margin-top: -(@height / 2)
==>
#logo {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 150px;
  height: 80px;
  margin-left: -75px;
  margin-top: -40px;
}

body
  color: red
  ul
    li
      color: blue
      a
        background-color: @color
==>
body {
  color: #f00;
}
body ul li {
  color: #00f;
}
body ul li a {
  background-color: #00f;
}

3). 插值(Interpolation)


vendor(prop, args)
  -webkit-{prop} args
  -moz-{prop} args
  {prop} args
button
  border-radius 1px 2px / 3px 4px
==>
button {
  border-radius: 1px 2px/3px 4px;
}

table
  for row in 1 2 3 4 5
    tr:nth-child({row})
      height: 10px * row
==>
table tr:nth-child(1) {
  height: 10px;
}
table tr:nth-child(2) {
  height: 20px;
}
table tr:nth-child(3) {
  height: 30px;
}
table tr:nth-child(4) {
  height: 40px;
}
table tr:nth-child(5) {
  height: 50px;
}

4). 运算符(Operators)


body
  n = 5
  foo: (n)em
  foo: (n)%
  foo: (n * 5)px
  foo: unit(n + 5, '%')
==>
body {
  foo: 5em;
  foo: 5%;
  foo: 25px;
  foo: 10%;
}

a = 15px
div
  if a is defined
    padding 5px
  if b
    margin 5px
==>
div {
  padding: 5px;
  margin: 5px;
}

pad(types = padding, n = 5px)
  if padding in types
    padding n
  if margin in types
    margin n
body
  pad()
body
  pad(margin)
body
  pad(padding margin, 10px)
==>
body {
  padding: 5px;
}
body {
  margin: 5px;
}
body {
  padding: 10px;
  margin: 10px;
}

5). 混合书写(Mixins)


border-radius(n)
  -webkit-border-radius n
  -moz-border-radius n
  border-radius n
form input[type=button]
  border-radius 5px
==>
form input[type=button] {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}

support-for-ie ?= true
opacity(n)
  opacity n
  if support-for-ie
    filter unquote('progid:DXImageTransform.Microsoft.Alpha(Opacity=' + round(n * 100) + ')')
#logo
  &:hover
    opacity 0.5
==>
#logo:hover {
  opacity: 0.5;
  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
}

inline-list()
  li
    display inline
comma-list()
  inline-list()
  li
    &:after
      content ', '
    &:last-child:after
      content ''
ul
  comma-list()
==>
ul li {
  display: inline;
}
ul li:after {
  content: ', ';
}
ul li:last-child:after {
  content: '';
}

6). 方法(Functions)


invoke(a, b, fn)
  fn(a, b)
add(a, b)
  a + b
body
  padding invoke(5, 10, add)
  padding invoke(5, 10, sub)
==>
body {
  padding: 15;
  padding: fn(5, 10);
}

get(hash, key)
  return pair[1] if pair[0] == key for pair in hash
div
  hash = (one 1) (two 2) (three 3)
  padding:get(hash, two)
==>
div {
  padding: 2;
}

7). 关键字参数(Keyword Arguments)


body {
   color: rgba(255, 200, 100, 0.5);
   color: rgba(red: 255, green: 200, blue: 100, alpha: 0.5);
   color: rgba(alpha: 0.5, blue: 100, red: 255, 200);
   color: rgba(alpha: 0.5, blue: 100, 255, 200);
}
==>
body {
  color: rgba(255,200,100,0.5);
  color: rgba(255,200,100,0.5);
  color: rgba(255,200,100,0.5);
  color: rgba(255,200,100,0.5);
}

//查看函数或混合书写中接受的参数
p(rgba)
==>
inspect: rgba(red, green, blue, alpha)

8). 内置方法(Built-in Functions)


red(#c00)
green(#0c0)
blue(#00c)
alpha(rgba(0,0,0,0.3))
dark(black)
light(white)
hue(hsla(50deg, 100%, 80%))
saturation(hsla(50deg, 100%, 80%))
lightness(hsla(50deg, 100%, 80%))

nums = 1 2
push(nums, 3, 4, 5)

nums= 4 5
unshift(nums, 3, 2, 1)

pairs = (one 1) (two 2) (three 3)
keys(pairs)
values(pairs)

typeof(12)
typeof(#fff)

unit(15in)
unit(10%,px)

match('^foo(bar)?', foo)
match('^foo(bar)?', foobar)

abs(-5px)
ceil(5.5in)
floor(5.6px)
round(5.5px)
min(1, 5)
max(1, 5)
even(6px)
odd(5mm)
sum(1 2 3)
avg(1 2 3)
join(',', 1 2 3)

hsla(10, 50, 30)
rgba(255,0,0,1)
rgb(255,204,0)
#fc08
lighten(#2c2c2c, 30)
darken(#D62828, 30)
desaturate(#f00, 40%)
saturate(#c33, 40%)
invert(#d62828)

unquote("sans-serif")
s('bar()')
s('bar(%s)', 'baz')
warn("oh noes!")
error('add() expects units')

op = '+'
operate(op, 15, 5)
last(1 2 3)
length((1 2 3 4))
p('test')
opposite-position(right)

image-size('tux.png')
add-property('bar', 1 2 3)
stop(50%, orange)

9). 多参数(Rest Params)


box-shadow(args...)
  -webkit-box-shadow args
  -moz-box-shadow args
  box-shadow args
#login
  box-shadow 1px 2px 5px #eee
==>
#login {
  -webkit-box-shadow: 1px 2px 5px #eee;
  -moz-box-shadow: 1px 2px 5px #eee;
  box-shadow: 1px 2px 5px #eee;
}

box-shadow()
  -webkit-box-shadow arguments
  -moz-box-shadow arguments
  box-shadow arguments
#login
  box-shadow #ddd 1px 1px, #eee 2px 2px
==>
#login {
  -webkit-box-shadow: #ddd 1px 1px, #eee 2px 2px;
  -moz-box-shadow: #ddd 1px 1px, #eee 2px 2px;
  box-shadow: #ddd 1px 1px, #eee 2px 2px;
}

10). 注释(Comments)


// one line
==>

/*
 * multi-line
 */
==>
/*
 * multi-line
 */

/*!
 * multi-line buffered
 */
==>
/*
 * multi-line buffered
 */

11). 条件(Conditionals)


box(x, y, margin-only = false)
  if margin-only
    margin y x
  else
    padding y x
div
  box(5px, 10px, true)
==>
div {
  margin: 10px 5px;
}

negative(n)
  error('invalid num') unless n is a 'unit'
  return yes if n < 0   no negative(15) ==> no
negative('str')
==> 
invalid num
at negative() (stylus:173)

12). 迭代(Iteration)


apply(props)
  props = arguments if length(arguments) > 1
  for prop in props
    {prop[0]} prop[1]
body
  apply(one 1, two 2, three 3)
==>
body {
  one: 1;
  two: 2;
  three: 3;
}  
body
  list = (one 1) (two 2) (three 3)
  apply(list)
==>
body {
  one: 1;
  two: 2;
  three: 3;
}

first-even(nums)
  return n if n % 2 == 0 for n in nums
div
    padding first-even(1 3 5 5 6 3 2)px
==>
div {
  padding: 6px;
}

13). @import,@media,@font-face, @keyframes, @extend,@css


//样式文件导入
@import "reset.css" //原始css导入
@import 'mixins/vendor'//stylus动态导入

//与标准CSS一样
@media print 
  #header
  #footer
    display none

//与标准CSS一样
@font-face
  font-family Geo
  font-style normal
  src url(fonts/geo_sans_light/GensansLight.ttf)

//@keyframes没用过不知道干什么的!汗!
vendors = official
@keyframes foo {
  from {
    color: black
  }
  to {
    color: white
  }
}
==>
@keyframes foo {
  0% {
    color: #000;
  }
  100% {
    color: #fff;
  }
}

//@extend样式
.message {
  padding: 10px;
  border: 1px solid #eee;
}
.warning {
  @extend .message;
  color: #E2E21E;
}
==>
.message,
.warning {
  padding: 10px;
  border: 1px solid #eee;
}
.warning {
  color: #e2e21e;
}

//@css不转义,直接输出字符串
@css {
  body {
    font: 14px;
  }
}
==>
body {
  font: 14px;
}

14). 自检API(Introspection API)


reset()
  if mixin == 'root'
    got
      root true
  else if mixin
    got 'a mixin'
  else
    'not a mixin'
reset()
body
  reset()
  foo reset()
==>
got {
  root: true;
}
body {
  got: 'a mixin';
  foo: 'not a mixin';
}

4. 在Express3项目中使用Stylus

刚开我们一直在讲Stylus的语法,下面我们配置到项目里试一下真正地效果。

实现自动编译stylus到css,我们还要借助于grunt。

~ D:\workspace\javascript\nodejs-stylus>npm install grunt-contrib-stylus --save-dev

需要修改的文件:

  • Gruntfile.js:grunt的配置文件
  • demo.styl:stylus的脚本
  • index.ejs:ejs模板文件

配置grunt的stylus任务

~ vi Gruntfile.js
module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        stylus: {
            compile: {
                files: {
                    'public/stylesheets/demo.css': ['public/stylus/demo.styl']
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-stylus');
    grunt.registerTask('default', ['stylus']);
}

增加一个demo.styl的文件

~ vi public/stylus/demo.styl
body
  color red
pad(n)
  padding (- n)
body
  pad(5px)

修改index.ejs文件


~ vi views/index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/demo.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
</body>
</html>

运行grunt命令:

grunt stylus

自动生成一个文件:
public/stylesheets/demo.css

打开浏览器:localhost:3000
stylus1

5. 基于grunt自动处理

接下来通过grunt wath配置自动更新。

下载grunt-contrib-watch插件

~ D:\workspace\javascript\nodejs-stylus>npm install grunt-contrib-watch --save-dev

修改Gruntfile.js文件,增加watch的配置


module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        stylus: {
            compile: {
                files: {
                    'public/stylesheets/demo.css': ['public/stylus/*.styl']
                }
            }
        },
        watch: {
            scripts: {
                files: ['public/stylus/demo.styl'],
                tasks: ['stylus']
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-stylus');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('default', ['stylus']);
}

接下来,我们启动两个命令窗口,一个用于grunt watch,一个用于supervisor.


~ D:\workspace\javascript\nodejs-stylus>grunt watch
~ D:\workspace\javascript\nodejs-stylus>supervisor app.js

stylus2

修改demo.styl


~ vi public/stylus/demo.styl

body
  color red
  background gray
pad(n)
  padding (- n)
body
  pad(5px)

点击保存后。
grunt watch日志


OK
>> File "public\stylus\demo.styl" changed.

Running "stylus:compile" (stylus) task
File public\stylesheets\demo.css created.

Done, without errors.
Completed in 0.825s at Mon Sep 02 2013 15:06:21 GMT+0800 (中国标准时间) - Waiting...

supervisro日志


GET / 200 0ms - 205b
GET /stylesheets/demo.css 200 13ms - 57b

刷新页面: http://localhost:3000/
stylus3

我们实现了,stylus与express3开发的自动集成!

到目前为止,就像学一门新语言一样,还是晕头转向!Stylus很强大,但是能不能真正被用起来,还需要我们更多的探索。

5. 在线体验和资源

作者提供了在线体验的应用:http://learnboost.github.io/stylus/try.html

中文资源:http://www.zhangxinxu.com/jq/stylus/

转载请注明出处:
http://blog.fens.me/nodejs-stylus-css/

打赏作者

AngularJS从构建项目开始

AngularJS体验式编程系列文章,将介绍如何用angularjs构建一个强大的web前端系统。angularjs是由Google团队开发的一款非常优秀web前端框架。在当前如此多的web框架下,angularjs能脱颖而出,从架构设计上就高人一等,双向数据绑定,依赖注入,指令,MVC,模板。Angular.js创新地把后台技术融入前端开发,扫去jQuery一度的光芒。用angularjs就像写后台代码,更规范,更结构化,更可控。

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:

http://blog.fens.me/angularjs-yeoman-project/

angularjs-yeoman

前言

随着AngularJS被越来越多的开发人员所了解,AngularJS的应用受到市场的好评。AngularJS是一种新型的以Javascript为基础的框架,以后台的编程思路影响着web前端的开发。在扎根细节之前,让我们先了解AngularJS项目构架。自顶向下的开始,就是不一样的开发。Angular体验式编程从此开始。

目录

  1. AngularJS介绍
  2. 构建AngularJS项目(Yeoman,angular-seed)
  3. AngularJS项目结构(Yeoman)
  4. AngularJS必备基础
  5. 启动项目

1. AngularJS介绍

AngularJS是一个为动态WEB应用设计的结构框架。它能让你使用HTML作为模板语言,通过扩展HTML的语法,让你能更清楚、简洁地构建你的应用组件。它的创新点在于,利用 数据绑定 和 依赖注入,它使你不用再写大量的代码了。这些全都是通过浏览器端的Javascript实现,这也使得它能够完美地和任何服务器端技术结合。

AngularJS介绍,摘自:http://angularjs.cn/A00n

2. 构建AngularJS项目(Yeoman,angular-seed)

说到构建项目,目前我了解的有三种:

  • 手工项目: 自己建目录,下载类库,写html,js,css,自己实现引用关系
  • angular-seed项目: 下载github的angular-seed,在别人的基础之上开发
  • Yeoman项目:通过Yeoman下载一个标准Yeoman项目,已经内置了grunt及各种工具包

1). 手工项目

构建手工项目是我们平时用的最多的一种方式了,适用于小型或demo项目。我不在解释,参照angularjs一步一步操作就行了。

2). angular-seed项目

一般把这个项目称为angular的“种子项目”,构建方法是去github下载项目源代码,基于已有项目结构再做开发。这种项目都融入了前人的经验,会以一种比较合理的结构,帮我们构建出项目的原型。适合于有一定规模项目开发,同时更适合geek的扩展。

下载演示一下构建过程


~ D:\workspace\javascript>git clone https://github.com/bsspirit/angular-seed.git
Cloning into 'angular-seed'...
remote: Counting objects: 1007, done.
remote: Compressing objects: 100% (497/497), done.
emote: Total 1007 (delta 521), reused 847 (delta 412)
Receiving objects: 100% (1007/1007), 6.30 MiB | 164 KiB/s, done.
Resolving deltas: 100% (521/521), done.

~ D:\workspace\javascript>cd angular-seed
~ D:\workspace\javascript\angular-seed>node scripts\web-server.js
Http Server running at http://localhost:8000/

打开浏览器:http://localhost:8000/app/index.html

angular-seed1

通过阅读READMD.md,我们可以了解这个”种子项目”如何使用。

项目目录及介绍


## Directory Layout
    app/                --> all of the files to be used in production
      css/              --> css files
        app.css         --> default stylesheet
      img/              --> image files
      index.html        --> app layout file (the main html template file of the app)
      index-async.html  --> just like index.html, but loads js files asynchronously
      js/               --> javascript files
        app.js          --> application
        controllers.js  --> application controllers
        directives.js   --> application directives
        filters.js      --> custom angular filters
        services.js     --> custom angular services
      lib/              --> angular and 3rd party javascript libraries
        angular/
          angular.js        --> the latest angular js
          angular.min.js    --> the latest minified angular js
          angular-*.js      --> angular add-on modules
          version.txt       --> version number
      partials/             --> angular view partials (partial html templates)
        partial1.html
        partial2.html

    config/karma.conf.js        --> config file for running unit tests with Karma
    config/karma-e2e.conf.js    --> config file for running e2e tests with Karma

    scripts/            --> handy shell/js/ruby scripts
      e2e-test.sh       --> runs end-to-end tests with Karma (*nix)
      e2e-test.bat      --> runs end-to-end tests with Karma (windows)
      test.bat          --> autotests unit tests with Karma (windows)
      test.sh           --> autotests unit tests with Karma (*nix)
      web-server.js     --> simple development webserver based on node.js

    test/               --> test source files and libraries
      e2e/              -->
        runner.html     --> end-to-end test runner (open in your browser to run)
        scenarios.js    --> end-to-end specs
      lib/
        angular/                --> angular testing libraries
          angular-mocks.js      --> mocks that replace certain angular services in tests
          angular-scenario.js   --> angular's scenario (end-to-end) test runner library
          version.txt           --> version file
      unit/                     --> unit level specs/tests
        controllersSpec.js      --> specs for controllers
        directivessSpec.js      --> specs for directives
        filtersSpec.js          --> specs for filters
        servicesSpec.js         --> specs for services

启动server

node scripts/web-server.js

单元测试(Unit test):karma + jasmine
端到端测试(End to End test):karma + jasmine + webserver

我们大概了解了“种子工程”的全貌,这样子心理有数了,就可以开始我们的项目开发了。

对于更高要求的开发者来说,“种子工程”的基础是不够。

  • 1. karam,jasmine都需要手动安装
  • 2. 没有代码自动化(自动增加controller…)
  • 3. 没有实现构建自动化(自动打包,自动压缩js…)
  • ……

一个大型项目构成是方方面面的,接下来我们通过标准化的Yeoman来构建一个企业级应用的项目基础。

3). Yeoman项目
yeoman是一个标准化的项目开发工作流工具,详细使用介绍请参考:Yeoman自动构建js项目

通过yeoman也构建项目

~ D:\workspace\javascript>mkdir nodejs-angular
~ D:\workspace\javascript>cd nodejs-angular
~ D:\workspace\javascript>npm install -g generator-angular

# 创建项目
~ D:\workspace\javascript\nodejs-angular>yo angular
[?] Would you like to include Twitter Bootstrap? Yes
[?] Would you like to use the SCSS version of Twitter Bootstrap with the Compass CSS Authoring Framework? No
[?] Which modules would you like to include? angular-resource.js, angular-cookies.js, angular-sanitize.js
   create app/styles/bootstrap.css
   create app/styles/main.css
   create app\index.html
   create bower.json
   create package.json
   create Gruntfile.js
   invoke   angular:common:D:\toolkit\nodejs\node_modules\generator-angular\app\index.js
   create     .bowerrc
   create     .editorconfig
   create     .gitattributes
   create     .jshintrc
   create     app\.buildignore
   create     app\.htaccess
   create     app\404.html
   create     app\favicon.ico
   create     app\robots.txt
   create     app\views\main.html
   create     test\.jshintrc
   create     test\runner.html
   create     .gitignore
   invoke   angular:main:D:\toolkit\nodejs\node_modules\generator-angular\app\index.js
   create     app\scripts\app.js
   invoke   angular:controller:D:\toolkit\nodejs\node_modules\generator-angular\app\index.js
   create     app\scripts\controllers\main.js
   create     test\spec\controllers\main.js
   invoke   karma:app
   create     karma.conf.js
   create     karma-e2e.conf.js
   create     .travis.yml

I'm all done. Running bower install & npm install for you to install the required dependencies. If this fails, try runni
ng the command yourself.

输入yo angular后,会提示我们要不要使用bootstrap;要不要用SCSS生成CSS;要不要include试angular的资源文件。我们选择完以后,会列出生成的项目文件,这个命令执行要2分钟左右,会自动下载很多的依赖包。

3. AngularJS项目结构(Yeoman)

yo-angular1

更直观地看到目录结构

  • .tmp:临时目录
  • app:开发的源代码的目录
  • dist:生成用于发布的项目
  • node_modules:nodejs依赖包
  • test:测试文件的目录
  • .bowerrc:bower属性
  • .editooconfig:对开发工具的属性配置
  • .gitattributes:git属性的配置
  • .gitignore:git管理文件的配置
  • .jshintr:JSHint配置
  • .travis.yml:travis-ci持续集成的配置
  • bower.json:bower依赖管理
  • Gruntfile.js:grunt开发过程管理
  • karma.conf.js:karma自动化测试
  • karma-e2e.conf.js:karma端到端自动化测试
  • package.json:项目依赖文件

4. AngularJS必备基础

从上面的目录结构,我们可以看出AngularJS对哪些东西是需要的。

git, yeoman, bower, grunt, karma …

所以,不要着急上手,先把基础工具都掌握,请参考系列文章:从零开始nodejs系列文章:成为高手 – 进阶篇

基础知识掌握多少,注定我们能开发多大规模的项目。

5. 启动项目

我们下载这个工程后,发现没有任何的文档。启动项目应该如何操作呢?

从刚才分析目录及文件结构,我们知道了这个项目是基于grunt构建的,那么一切的操作都会源于Gruntfile.js。grunt的详细介绍,请参考:grunt让Nodejs规范起来

打开Gruntfile.js,直接定位到grunt.registerTask()


  grunt.registerTask('server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'open',
      'watch'
    ]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'copy:dist',
    'cdnify',
    'ngmin',
    'cssmin',
    'uglify',
    'rev',
    'usemin'
  ]);

  grunt.registerTask('default', [
    'jshint',
    'test',
    'build'
  ]);

这里定义了4个任务:server,test,build, default。

从名字看就能猜出对应该的功能。

启动server


~ D:\workspace\javascript\nodejs-angular>grunt server
Running "server" task

Running "clean:server" (clean) task
Cleaning .tmp...OK

Running "concurrent:server" (concurrent) task

Running "coffee:dist" (coffee) task

Running "copy:styles" (copy) task

Running "autoprefixer:dist" (autoprefixer) task
File ".tmp/styles/bootstrap.css" created.
File ".tmp/styles/main.css" created.

Running "connect:livereload" (connect) task
Started connect web server on localhost:9000.

Running "open:server" (open) task

Running "watch" task
Waiting...

浏览器被自动打开:http://localhost:9000/#/

yo-angular2

执行default任务,生成用于部署的目录dist


~ D:\workspace\javascript\nodejs-angular>grunt --force
Running "jshint:all" (jshint) task
>> 3 files lint free.
Warning: Task "karma" not found. Used --force, continuing.

Running "clean:dist" (clean) task
Cleaning .tmp...OK
Cleaning dist/.htaccess...OK
Cleaning dist/404.html...OK
Cleaning dist/bower_components...OK
Cleaning dist/favicon.ico...OK
Cleaning dist/index.html...OK
Cleaning dist/robots.txt...OK
Cleaning dist/scripts...OK
Cleaning dist/styles...OK
Cleaning dist/views...OK

Running "useminPrepare:html" (useminPrepare) task
Going through app/index.html to update the config
Looking for build script HTML comment blocks

Found a block:
<!-- build:css(.tmp) styles/main.css -->
<link rel="stylesheet" href="styles/bootstrap.css">
<link rel="stylesheet" href="styles/main.css">
<!-- endbuild -->
Updating config with the following assets:
- .tmp\styles\bootstrap.css
- .tmp\styles\main.css

Found a block:
<!-- build:js scripts/plugins.js -->
<script src="bower_components/bootstrap-sass/js/bootstrap-affix.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-alert.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-dropdown.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-tooltip.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-modal.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-transition.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-button.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-popover.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-typeahead.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-carousel.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-scrollspy.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-collapse.js"></script>
<script src="bower_components/bootstrap-sass/js/bootstrap-tab.js"></script>
<!-- endbuild -->
Updating config with the following assets:
- app\bower_components\bootstrap-sass\js\bootstrap-affix.js
- app\bower_components\bootstrap-sass\js\bootstrap-alert.js
- app\bower_components\bootstrap-sass\js\bootstrap-dropdown.js
- app\bower_components\bootstrap-sass\js\bootstrap-tooltip.js
- app\bower_components\bootstrap-sass\js\bootstrap-modal.js
- app\bower_components\bootstrap-sass\js\bootstrap-transition.js
- app\bower_components\bootstrap-sass\js\bootstrap-button.js
- app\bower_components\bootstrap-sass\js\bootstrap-popover.js
- app\bower_components\bootstrap-sass\js\bootstrap-typeahead.js
- app\bower_components\bootstrap-sass\js\bootstrap-carousel.js
- app\bower_components\bootstrap-sass\js\bootstrap-scrollspy.js
- app\bower_components\bootstrap-sass\js\bootstrap-collapse.js
- app\bower_components\bootstrap-sass\js\bootstrap-tab.js

Found a block:
<!-- build:js scripts/modules.js -->
<script src="bower_components/angular-resource/angular-resource.js"></script>
<script src="bower_components/angular-cookies/angular-cookies.js"></script>
<script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
<!-- endbuild -->
Updating config with the following assets:
- app\bower_components\angular-resource\angular-resource.js
- app\bower_components\angular-cookies\angular-cookies.js
- app\bower_components\angular-sanitize\angular-sanitize.js

Found a block:
<!-- build:js({.tmp,app}) scripts/scripts.js -->
<script src="scripts/app.js"></script>
<script src="scripts/controllers/main.js"></script>
<!-- endbuild -->
Updating config with the following assets:
- {.tmp,app}\scripts\app.js
- {.tmp,app}\scripts\controllers\main.js

Configuration is now:

cssmin:
{ 'dist\\styles\\main.css': 'dist\\styles\\main.css' }

concat:
{ 'dist\\styles\\main.css':
[ '.tmp\\styles\\bootstrap.css',
'.tmp\\styles\\main.css' ],
'dist\\scripts\\plugins.js':
[ 'app\\bower_components\\bootstrap-sass\\js\\bootstrap-affix.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-alert.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-dropdown.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-tooltip.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-modal.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-transition.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-button.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-popover.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-typeahead.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-carousel.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-scrollspy.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-collapse.js',
'app\\bower_components\\bootstrap-sass\\js\\bootstrap-tab.js' ],
'dist\\scripts\\modules.js':
[ 'app\\bower_components\\angular-resource\\angular-resource.js',
'app\\bower_components\\angular-cookies\\angular-cookies.js',
'app\\bower_components\\angular-sanitize\\angular-sanitize.js' ],
'dist\\scripts\\scripts.js':
[ '{.tmp,app}\\scripts\\app.js',
'{.tmp,app}\\scripts\\controllers\\main.js' ] }

uglify:
{ dist: { files: { '<%= yeoman.dist %>/scripts/scripts.js': [ 'dist/scripts/scripts.js' ] } },
'dist\\scripts\\plugins.js': 'dist\\scripts\\plugins.js',
'dist\\scripts\\modules.js': 'dist\\scripts\\modules.js',
'dist\\scripts\\scripts.js': 'dist\\scripts\\scripts.js' }

requirejs:
{}

Running "concurrent:dist" (concurrent) task

Running "copy:styles" (copy) task

Running "imagemin:dist" (imagemin) task

Running "coffee:dist" (coffee) task

Running "htmlmin:dist" (htmlmin) task

Running "svgmin:dist" (svgmin) task

Running "autoprefixer:dist" (autoprefixer) task
File ".tmp/styles/bootstrap.css" created.
File ".tmp/styles/main.css" created.

Running "concat:dist\styles\main.css" (concat) task
File "dist\styles\main.css" created.

Running "concat:dist\scripts\plugins.js" (concat) task
File "dist\scripts\plugins.js" created.

Running "concat:dist\scripts\modules.js" (concat) task
File "dist\scripts\modules.js" created.

Running "concat:dist\scripts\scripts.js" (concat) task
File "dist\scripts\scripts.js" created.

Running "copy:dist" (copy) task
Created 63 directories, copied 367 files

Running "cdnify:dist" (cdnify) task
Going through dist/404.html, dist/index.html to update script refs

Running "ngmin:dist" (ngmin) task
ngminifying dist/scripts/modules.js, dist/scripts/plugins.js, dist/scripts/scripts.js

Running "cssmin:dist\styles\main.css" (cssmin) task
File dist\styles\main.css created.

Running "uglify:dist" (uglify) task
File "dist/scripts/scripts.js" created.

Running "uglify:dist\scripts\plugins.js" (uglify) task
File "dist\scripts\plugins.js" created.

Running "uglify:dist\scripts\modules.js" (uglify) task
File "dist\scripts\modules.js" created.

Running "uglify:dist\scripts\scripts.js" (uglify) task
File "dist\scripts\scripts.js" created.

Running "rev:dist" (rev) task
dist/scripts/modules.js >> 6b865daa.modules.js
dist/scripts/plugins.js >> 76c21dca.plugins.js
dist/scripts/scripts.js >> ff635307.scripts.js
dist/styles/main.css >> a5c01db0.main.css

Running "usemin:html" (usemin) task

Processing as HTML - dist/404.html
Update the HTML to reference our concat/min/revved script files
Update the HTML with the new css filenames
Update the HTML with the new img filenames
Update the HTML with data-main tags
Update the HTML with the data tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input

Processing as HTML - dist/index.html
Update the HTML to reference our concat/min/revved script files
<script src="scripts/plugins.js" changed to <script src="scripts/76c21dca.plugins.js"
<script src="scripts/modules.js" changed to <script src="scripts/6b865daa.modules.js"
<script src="scripts/scripts.js" changed to <script src="scripts/ff635307.scripts.js"
Update the HTML with the new css filenames
<link rel="stylesheet" href="styles/main.css" changed to <link rel="stylesheet" href="styles/a5c01db0.main.css"
Update the HTML with the new img filenames
Update the HTML with data-main tags
Update the HTML with the data tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input

Processing as HTML - dist/views/main.html
Update the HTML to reference our concat/min/revved script files
Update the HTML with the new css filenames
Update the HTML with the new img filenames
Update the HTML with data-main tags
Update the HTML with the data tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input

Running "usemin:css" (usemin) task

Processing as CSS - dist/styles/a5c01db0.main.css
Update the CSS with new img filenames

Done, but with warnings.

Elapsed time
jshint:all 69ms
clean:dist 593ms
useminPrepare:html 49ms
concurrent:dist 2s
autoprefixer:dist 65ms
concat:dist\scripts\scripts.js 26ms
copy:dist 475ms
ngmin 21ms
ngmin:dist 210ms
uglify:dist 37ms
uglify:dist\scripts\plugins.js 252ms
uglify:dist\scripts\modules.js 76ms
usemin:html 313ms
usemin:css 82ms
Total 5s

有了工具的支持,开发效率就是事半功倍了,前提是你要知道如何善用这些工具!

转载请注明出处:
http://blog.fens.me/angularjs-yeoman-project/

打赏作者

Yeoman自动构建js项目

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-yeoman-intro/
yeoman

前言
有一种技术可以提高我们的工作效率,可以让我们专心做我们擅长的事,可以屏蔽复杂性,可以规范我们的架构和我们的代码,可以让我们的享受编程的乐趣。Yeoman可以做到。

很多年以前,rails刚刚出世,伴随着一个新的名词“脚手架(scaffolding)”出现了。脚手架是一种提高开发效率的工具的代名词。随后,各种编程语言都开始实现自己的“脚手架”工具。Maven重新构造了Java的世界,改变了几千万的开发者对于Java项目构建的认识。Yeoman在Javascript领域正做着同样的事情。

看着各种工具,各种语言,都在飞跃式的进化。做为一个开发者,我每天都在为变化而惊喜!

目录

  1. Yeoman介绍
  2. Yeoman工具包yo命令
  3. Yeoman工具包yo – 快速构建一个web项目
  4. Yeoman工具包bower,grunt的组合用法

1. Yeoman介绍

Yeoman是Google的团队和外部贡献者团队合作开发的,他的目标是通过Grunt(一个用于开发任务自动化的命令行工具)和Bower(一个HTML、CSS、Javascript和图片等前端资源的包管理器)的包装为开发者创建一个易用的工作流。

Yeoman主要有三部分组成:yo(脚手架工具)、grunt(构建工具)、bower(包管理器)。这三个工具是分别独立开发的,但是需要配合使用,来实现我们高效的工作流模式。

2. Yeoman工具包yo命令

yo插件都是通过npm, Node.js包管理器安装和管理的.

我的系统环境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19
~ D:\workspace\javascript>node -v
v0.10.5

~ D:\workspace\javascript>npm -v
1.2.19

在系统中,我们已经安装好了Nodejs和npm。win7安装nodejs请参考文章:Nodejs开发框架Express3.0开发手记–从零开始

全局安装yo

~ D:\workspace\javascript>npm install -g yo

如果你还没有安装grunt,bower,也需要一起安装

~ D:\workspace\javascript>npm install -g grunt-cli bower

接下我们看一下yo命令行操作
1). 通过help查看帮助


~ D:\workspace\javascript>yo --help
Yeoman is a mask worn by the following members of the open-source community:

  Paul Irish, Addy Osmani, Mickael Daniel, Sindre Sorhus, Eric Bidelman,
  Frederick Ros, Brian Ford, Pascal Hartig, Stephen Sawchuk, and countless
  other contributors.

Usage: yo GENERATOR [args] [options]

General options:
  -h, --help     # Print generator's options and usage
  -f, --force    # Overwrite files that already exist

Please choose a generator below.
Angular
  angular:app
  angular:common
  angular:constant
  angular:controller
  angular:decorator
  angular:directive
  angular:factory
  angular:filter
  angular:main
  angular:provider
  angular:route
  angular:service
  angular:value
  angular:view

Karma
  karma:app

Mocha
  mocha:app
  mocha:generator

Webapp
  webapp:app

yo的命令很简单:yo GENERATOR,例如:yo webapp, yo angular
Please choose a generator below: 下面列出了我的系统中,已经安装的generator的库,例如:Angular,Karma,Mocha,Webapp

2). 查询yo的支持库


~ D:\workspace\javascript>yo
Yeoman is a mask worn by the following members of the open-source community:

  Paul Irish, Addy Osmani, Mickael Daniel, Sindre Sorhus, Eric Bidelman,
  Frederick Ros, Brian Ford, Pascal Hartig, Stephen Sawchuk, and countless
  other contributors.

[?] What would you like to do? (Use arrow keys)
 > Install a generator
   Find some help
   Get me out of here!

命令行会提示,我们想要的操作。这里选择”Install a generator”


[?] What would you like to do? Install a generator
[?] Search NPM for generators:web

系统继续提示,我们要查找的包,我们输入web

3). 安装generator-webapp库


[?] Here's what I found. Install one? (Use arrow keys)
 > generator-armadillo
   generator-bones
   generator-btapp
   generator-fe
   generator-flight
   generator-hazdev-webapp
   generator-hbswebapp
   generator-html5-site
   generator-jing
   generator-lessapp
   generator-nodestrap
   generator-sails
   generator-server-configs
   generator-starter
   generator-starttter
   generator-ultimate
   generator-webapp
   generator-webapp-bfytw
   generator-webapp-fintan
   generator-weblog
   generator-website
   Search again
   Return home

yo列出了,所有在官方已经注册的,web关键字相关的包,我们选择“generator-webapp”

yo通过npm开始下载generator-webapp安装包


[?] What would you like to do? Install a generator
[?] Search NPM for generators: web
[?] Here's what I found. Install one? generator-webapp

generator-webapp@0.2.7 D:\toolkit\nodejs\node_modules\generator-webapp
├── cheerio@0.12.1 (entities@0.3.0, underscore@1.4.4, htmlparser2@3.1.4, cheerio-select@0.0.3)
└── yeoman-generator@0.12.3 (dargs@0.1.0, diff@1.0.5, debug@0.7.2, async@0.2.9, mime@1.2.11, mkdirp@0.3.5, isbinaryfi
le@0.1.9, underscore.string@2.3.3, shelljs@0.1.4, iconv-lite@0.2.11, lodash@1.3.1, rimraf@2.1.4, nopt@2.1.2, cli-table@0
.2.0, tar@0.1.18, glob@3.2.6, inquirer@0.2.4, request@2.21.0)

I just installed your generator by running:
    npm install -g generator-webapp

然后,我们退出yo命令行,选择“Get me out of here!”


[?] What would you like to do? Get me out of here!

Bye from us! Chat soon.
            Addy Osmani
          Sindre Sorhus
        Brian Ford
     Eric Bidelman
              Paul Irish
     Mickael Daniel
          Pasca1 Hartig
      Stephen S.wchuk
    Frederick R0s

上面的退出的文字是开发者的名字,很有意思一点是,竖着看最中间一行的文字排列是,yeoman1.0,也就是yeoman这个项目的得名了。

4). 注:上面的过程,也可以直接用命令

npm install -g generator-webapp

3. Yeoman工具包yo – 快速构建一个web项目

当我们安装了generator-webapp后,我们就可以,以非常优雅的方式来构建工程了。

1). 创建nodejs-yo目录,并构建webapp工程


~ D:\workspace\javascript>mkdir nodejs-yo
~ D:\workspace\javascript>cd nodejs-yo
~ D:\workspace\javascript\nodejs-yo>yo webapp

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

Out of the box I include HTML5 Boilerplate, jQuery and Modernizr.
[?] What more would you like?
  [ ] Twitter Bootstrap for Sass
 >[X] RequireJS
  [X] Autoprefixer for your CSS

对选项的解释:

  • Twitter Bootstrap for Sass:SASS是一种CSS的开发工具,提供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单和可维护。
  • RequireJS 可以帮助用户异步按需的加载 JavaScript 代码,并解决 JavaScript 模块间的依赖关系,提升了前端代码的整体质量和性能。
  • Autoprefixer解析CSS文件并且添加浏览器前缀到CSS规则里,使用Can I Use的数据来决定哪些前缀是需要的。

上面3个选择,由于Sass是基于Ruby的,我本机没有安装ruby,取消Twitter Bootstrap for Sass的勾选。

2). 查看安装日志:


Out of the box I include HTML5 Boilerplate, jQuery and Modernizr.
[?] What more would you like? RequireJS, Autoprefixer for your CSS
   create Gruntfile.js
   create package.json
   create .gitignore
   create .gitattributes
   create .bowerrc
   create bower.json
   create .jshintrc
   create .editorconfig
   create app/favicon.ico
   create app/404.html
   create app/robots.txt
   create app/.htaccess
   create app/styles/main.css
   create app/scripts/app.js
   create app/scripts/main.js
   create app/index.html
   create app/scripts/hello.coffee
   invoke   mocha:app
   create     test\index.html
   create     test\lib\chai.js
   create     test\lib\expect.js
   create     test\lib\mocha\mocha.css
   create     test\lib\mocha\mocha.js
   create     test\spec\test.js

I'm all done. Running bower install & npm install for you to install the required dependencies. If this fails, try running the command yourself.

项目骨架已经建好了。还包括了很多的配置文件.gitignore,bower.json,Gruntfile.js,favicon.ico, robots.txt, .htaccess。太神奇了,Cool!!

一下子装备都齐全了,打仗的事就交给我们自己吧。

4. Yeoman工具包bower,grunt的组合用法

有关bower的介绍,请参考:bower解决js的依赖管理
有关grunt的介绍,请参考:grunt让Nodejs规范起来

1). 使用bower命令查看项目依赖:


~ D:\workspace\javascript\nodejs-yo>bower list
bower check-new     Checking for new versions of the project dependencies..
nodejs-yo#0.0.0 D:\workspace\javascript\nodejs-yo
├── jquery#1.9.1 (latest is 2.0.3)
├── modernizr#2.6.2
└── requirejs#2.1.8

我们发现生成的webapp中,有4个依赖库,jquery,modernizr,requirejs。

2). 使用grunt命令执行单元测试


D:\workspace\javascript\nodejs-yo>grunt test
Running "clean:server" (clean) task
Cleaning ".tmp"...OK

Running "concurrent:test" (concurrent) task

Running "copy:styles" (copy) task

Running "coffee:dist" (coffee) task

Running "autoprefixer:dist" (autoprefixer) task
File ".tmp/styles/main.css" created.

Running "connect:test" (connect) task
Starting connect web server on localhost:9000.

Running "mocha:all" (mocha) task
Testing: http://localhost:9000/index.html

  .

  1 test complete (107 ms)

>> 1 passed! (0.11s)

Done, without errors.

3). 使用grunt命令启动一个node服务器


~ D:\workspace\javascript\nodejs-yo>grunt server
Running "server" task

Running "clean:server" (clean) task

Running "concurrent:server" (concurrent) task
    Warning: Running "compass:dist" (compass) task
Warning: You need to have Ruby and Compass installed and in your system PATH for this task to work. More info: https://g
ithub.com/gruntjs/grunt-contrib-compass Use --force to continue. Use --force to continue.

    Aborted due to warnings.

启动时,我们发现报错了。由于我没有装Ruby的环境,所以compass:dist的命令不能运行。

强制启动grunt


~ D:\workspace\javascript\nodejs-yo>grunt server --force
D:\workspace\javascript\nodejs-yo>grunt server --force
Running "server" task

Running "clean:server" (clean) task
Cleaning ".tmp"...OK

Running "concurrent:server" (concurrent) task

Running "coffee:dist" (coffee) task
    Warning: Running "compass:dist" (compass) task
Warning: You need to have Ruby and Compass installed and in your system PATH for this task to work. More info: https://
ithub.com/gruntjs/grunt-contrib-compass Use --force to continue. Used --force, continuing.

Running "compass:dist" (compass) task
Warning: You need to have Ruby and Compass installed and in your system PATH for this task to work. More info: https://
ithub.com/gruntjs/grunt-contrib-compass Use --force to continue.

Running "copy:styles" (copy) task

Running "autoprefixer:dist" (autoprefixer) task
File ".tmp/styles/main.css" created.

Running "connect:livereload" (connect) task
Starting connect web server on localhost:9000.

Running "open:server" (open) task

Running "watch" task
Waiting...

后台日志显示了“watch”的task,说明代码文件被监控着,如有修改网页会自动更新。

浏览器被自动打开,http://localhost:9000/
yeoman1

4). 修改app/index.html代码,增加对canvas的检查。


~ vi app/index.html

<script>
window.onload = function () {
if (canvasSupported()) {
alert('canvas supported');
}
};

function canvasSupported() {
var canvas = document.createElement('canvas');
return (canvas.getContext && canvas.getContext('2d'));
}
</script>

浏览器显示:
yeoman2

5). 通过bower增加库jquery-pjax


~ D:\workspace\javascript\nodejs-yo>bower install jquery-pjax
bower jquery-pjax#*         not-cached git://github.com/defunkt/jquery-pjax.git#*
bower jquery-pjax#*            resolve git://github.com/defunkt/jquery-pjax.git#*
bower jquery-pjax#*           download https://github.com/defunkt/jquery-pjax/archive/v1.7.3.tar.gz
bower jquery-pjax#*            extract archive.tar.gz
bower jquery-pjax#*           resolved git://github.com/defunkt/jquery-pjax.git#1.7.3
bower jquery#>=1.8              cached git://github.com/components/jquery.git#2.0.3
bower jquery#>=1.8            validate 2.0.3 against git://github.com/components/jquery.git#>=1.8
bower jquery-pjax#~1.7.3       install jquery-pjax#1.7.3

jquery-pjax#1.7.3 app\bower_components\jquery-pjax
└── jquery#2.0.3

~ D:\workspace\javascript\nodejs-yo>bower list
bower check-new     Checking for new versions of the project dependencies..
nodejs-yo#0.0.0 D:\workspace\javascript\nodejs-yo
├── jquery#1.9.1 (latest is 2.0.3)
├─┬ jquery-pjax#1.7.3 extraneous
│ └── jquery#1.9.1 (2.0.3 available)
├── modernizr#2.6.2
└── requirejs#2.1.8

Yeoman已经成功地帮助我们打造出一个项目的原型了,接下来我们可以针对自己的项目,自己熟悉的框架组合,构建一个属于自己的骨架模板,并发布到Yeoman的generator库中。

后面的内容会更精彩,敬请期待…!!

转载请注明出处:
http://blog.fens.me/nodejs-yeoman-intro/

打赏作者

grunt让Nodejs规范起来

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-grunt-intro/

grunt

前言
一个应用开发到一定阶段,普遍会遇到一个问题。当功能越来越多,代码量越来越大,bug修复越来越频繁,开发人员一波一波的交替,…..应该用会向着越来越不可控发展。我们不能再准确估计新功能的开发时间,也不知道一个bug修复后是否会引发另一个bug出现。所有的程序开发,都会面临着这样的问题。

C/C++程序通过makefile管理编译测试打包的过程,Java程序通过Maven,Ant实现项目构建管理功能,Python有pip,Ruby有gem。在Nodejs的领域,我们同样需要一个项目构建工具,这就是Grunt。Grunt可以执行像压缩, 编译, 单元测试, 代码检查以及打包发布的任务。

目录

  1. Grunt介绍
  2. Grunt安装
  3. Grunt使用
  4. Grunt常用插件:
    1).grunt-contrib-uglify:压缩js代码
    2).grunt-contrib-concat:合并js文件
    3).grunt-contrib-qunit:单元测试
    4).grunt-contrib-jshint:js代码检查
    5).grunt-contrib-watch:文件监控

1. Grunt介绍

Grunt是一个自动化的项目构建工具. 如果你需要重复的执行像压缩, 编译, 单元测试, 代码检查以及打包发布的任务. 那么你可以使用Grunt来处理这些任务, 你所需要做的只是配置好Grunt, 这样能很大程度的简化你的工作.

如果在团队中使用Grunt, 你只需要与其他人员约定好使用Grunt应该规避的问题, 就能够很方便的自动化的处理大部分的常见工作任务, 你所付出的努力几乎为0.

2. Grunt安装

Grunt和Grunt插件都是通过npm, Node.js包管理器安装和管理的.

我的系统环境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19
~ D:\workspace\javascript>node -v
v0.10.5

~ D:\workspace\javascript>npm -v
1.2.19

在系统中,我们已经安装好了Nodejs和npm。win7安装nodejs请参考文章:Nodejs开发框架Express3.0开发手记–从零开始

安装grunt-cli
grunt-cli并不grunt,grunt-cli的作用是管理本地各版本的grunt,让命令行可以直接执行grunt命令。

下面全局安装grunt-cli(-g)


~ D:\workspace\javascript>npm install -g grunt-cli

D:\toolkit\nodejs\grunt -> D:\toolkit\nodejs\node_modules\grunt-cli\bin\grunt
grunt-cli@0.1.9 D:\toolkit\nodejs\node_modules\grunt-cli
├── resolve@0.3.1
├── nopt@1.0.10 (abbrev@1.0.4)
└── findup-sync@0.1.2 (lodash@1.0.1, glob@3.1.21)

我们看到grunt-cli似乎做了一个软件链接,把grunt脚本复制到nodejs安装根目录里。

接下来全局安装grunt


~ D:\workspace\javascript>npm install -g grunt

~ D:\workspace\javascript>grunt
grunt-cli: The grunt command line interface. (v0.1.9)

Fatal error: Unable to find local grunt.

If you're seeing this message, either a Gruntfile wasn't found or grunt
hasn't been installed locally to your project. For more information about
installing and configuring grunt, please see the Getting Started guide:

http://gruntjs.com/getting-started

执行grunt命令,我们发现系统报错了,提示不能加载本地库。因为,grunt命令执行,是需要当前目录中包括package.json和Gruntfile.js两个文件。

package.json,是npm项目配置文件
Gruntfile.js,是专门用来配置grunt的配置文件

接下来,我们创建一个express3的项目。


~ D:\workspace\javascript>express -e nodejs-grunt
~ D:\workspace\javascript>cd nodejs-grunt && npm install	 
~ D:\workspace\javascript\nodejs-grunt>npm install grunt --save-dev

安装-save-dev,就可以,直接把grunt作为devDependencies写入的package.json中。


~ vi package.json

{
  "name": "nodejs-grunt",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "3.2.2",
    "ejs": "*"
  },
  "devDependencies": {
    "grunt": "~0.4.1",
  }
}

然后,我们再执行grunt,系统提示缺少Gruntfile文件


~ D:\workspace\javascript\nodejs-grunt>grunt
A valid Gruntfile could not be found. Please see the getting started guide for
more information on how to configure grunt: http://gruntjs.com/getting-started
Fatal error: Unable to find Gruntfile.

创建Gruntfile文件


~ vi Gruntfile.js

module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
      },
      build: {
        src: 'src/<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    }
  });
  // Load the plugin that provides the "uglify" task.
  grunt.loadNpmTasks('grunt-contrib-uglify');
  // Default task(s).
  grunt.registerTask('default', ['uglify']);
};

再次运行grunt,这时提示是grunt-contrib-uglify包找不到,是Gruntfile.js配置文件中的错误了。


~ D:\workspace\javascript\nodejs-grunt>grunt
>> Local Npm module "grunt-contrib-uglify" not found. Is it installed?
Warning: Task "uglify" not found. Use --force to continue.

我们编辑package.js, 在devDependencies中增加grunt-contrib-uglify的依赖库


~ vi package.js
{
  "name": "application-name",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "3.2.2",
    "ejs": "*"
  },
  "devDependencies": {
    "grunt": "~0.4.1"
    "grunt-contrib-uglify": "~0.1.1"
  }
}

~ D:\workspace\javascript\nodejs-grunt>npm install

我们创建两个目录src和build,和nodejs-grunt.js的文件


~ D:\workspace\javascript\nodejs-grunt>mkdir src
~ D:\workspace\javascript\nodejs-grunt>mkdir build

~ D:\workspace\javascript\nodejs-grunt>vi src/nodejs-grunt.js
var sayHello = function(name){
	return "Hello " + name;
}

我们再执行grunt


~ D:\workspace\javascript\nodejs-grunt>grunt
Running "uglify:build" (uglify) task
File "build/nodejs-grunt.min.js" created.
Uncompressed size: 59 bytes.
Compressed size: 40 bytes gzipped (43 bytes minified).

Done, without errors.

grunt运行正常,并且执行了uglify:build的任务。打开build/nodejs-grunt.min.js文件

~ D:\workspace\javascript\nodejs-grunt>vi build/nodejs-grunt.min.js

/*! nodejs-grunt 2013-08-17 */
var sayHello=function(l){return"Hello "+l};

我们可以看到一个新生成的压缩文件nodejs-grunt.min.js。

上面的例子,是一个js文件压缩的例子。

3. Grunt使用

我们可以通过help帮助,看一下grunt怎么用。


~ D:\workspace\javascript\nodejs-grunt>grunt --help
Grunt: The JavaScript Task Runner (v0.4.1)

Usage
 grunt [options] [task [task ...]]

Options
    --help, -h  Display this help text.
        --base  Specify an alternate base path. By default, all file paths are
                relative to the Gruntfile. (grunt.file.setBase) *
    --no-color  Disable colored output.
   --gruntfile  Specify an alternate Gruntfile. By default, grunt looks in the
                current or parent directories for the nearest Gruntfile.js or
                Gruntfile.coffee file.
   --debug, -d  Enable debugging mode for tasks that support it.
       --stack  Print a stack trace when exiting with a warning or fatal error.
   --force, -f  A way to force your way past warnings. Want a suggestion? Don't
                use this option, fix your code.
       --tasks  Additional directory paths to scan for task and "extra" files.
                (grunt.loadTasks) *
         --npm  Npm-installed grunt plugins to scan for task and "extra" files.
                (grunt.loadNpmTasks) *
    --no-write  Disable writing files (dry run).
 --verbose, -v  Verbose mode. A lot more information output.
 --version, -V  Print the grunt version. Combine with --verbose for more info.
  --completion  Output shell auto-completion rules. See the grunt-cli
                documentation for more information.

Options marked with * have methods exposed via the grunt API and should instead
be specified inside the Gruntfile wherever possible.

Available tasks
        uglify  Minify files with UglifyJS. *
       default  Alias for "uglify" task.

Tasks run in the order specified. Arguments may be passed to tasks that accept
them by using colons, like "lint:files". Tasks marked with * are "multi tasks"
and will iterate over all sub-targets if no argument is specified.

The list of available tasks may change based on tasks directories or grunt
plugins specified in the Gruntfile or via command-line options.

For more information, see http://gruntjs.com/

有两方面是我们需要注意的:

  1. Options: grunt支持的命令
  2. Available tasks: 当目录可执行的任务

4. Grunt常用插件

  • grunt-contrib-uglify:压缩js代码
  • grunt-contrib-concat:合并js文件
  • grunt-contrib-qunit:单元测试
  • grunt-contrib-jshint:js代码检查
  • grunt-contrib-watch:监控文件修改并重新执行注册的任务

1). grunt-contrib-uglify:是执行压缩JS代码的任务
具体操作请查看”2.Grunt安装”,

2). grunt-contrib-concat:是执行合并文件的任务。
插件安装及更新到配置


~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-concat --save-dev

修改Gruntfile.js文件


grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat:{
      options: {
        //定义一个字符串插入没个文件之间用于连接输出
        separator: ';'
      },
      dist: {
          src: ['src/*.js'],
          dest: 'build/<%= pkg.name %>.cat.js'
      }
    },
  });
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.registerTask('default', ['uglify','concat']);

在src目录,新增加文件src/sayBye.js


~ vi src/sayBye.js

var sayBye = function(name){
	return "Bye " + name;
}

执行concat任务


~ D:\workspace\javascript\nodejs-grunt>grunt concat
Running "concat:dist" (concat) task
File "build/nodejs-grunt.cat.js" created.

Done, without errors.

查看生成的文件build/nodejs-grunt.cat.js


~ vi build/nodejs-grunt.cat.js

var sayHello = function(name){
	return "Hello " + name;
};var sayBye = function(name){
	return "Bye " + name;
}

两个文件完全的合并。

3). grunt-contrib-qunit:是执行QUint单元测试的任务。

插件安装及更新到配置


~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-qunit --save-dev

修改Gruntfile.js文件


grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    qunit: {
      files: ['test/*.html']
    }
  });
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.registerTask('default', ['uglify','concat','qunit']);

创建一个test目录,并编写用于测试的qunit.html文件


~ mkdir test
~ vi test/qunit.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="http://github.com/jquery/qunit/raw/master/qunit/qunit.css" type="text/css" media="screen" />
<script type="text/javascript" src="http://github.com/jquery/qunit/raw/master/qunit/qunit.js"></script>
<script>
test("hello", function() {
ok(true, "world");
});
</script>
</head>
<body>
<h1 id="qunit-header">QUnit example</h1>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
</body>
</html>

执行qunit命令


~ D:\workspace\javascript\nodejs-grunt>grunt qunit
Running "qunit:files" (qunit) task
Testing test/qunit.html .OK
>> 1 assertions passed (67ms)

Done, without errors.

完成单元测试!!

4). grunt-contrib-jshint:是执行代码验证的任务
插件安装及更新到配置


~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-jshint --save-dev

修改Gruntfile.js文件


grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
        files: ['gruntfile.js', 'src/*.js', 'build/*.js'],
        options: {
            globals: {
                exports: true
            }
        }
    }
  });
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['uglify','concat','qunit','jshint']);

执行jshint代码检查


~ D:\workspace\javascript\nodejs-grunt>grunt jshint
Running "jshint:files" (jshint) task
Linting src/nodejs-grunt.js ...ERROR
[L3:C2] W033: Missing semicolon.
}
Linting build/nodejs-grunt.cat.js ...ERROR
[L5:C2] W033: Missing semicolon.
}
Linting build/nodejs-grunt.min.js ...ERROR
[L2:C42] W033: Missing semicolon.
var sayHello=function(l){return"Hello "+l};

Warning: Task "jshint:files" failed. Use --force to continue.

Aborted due to warnings.

好多的错误啊,细看一下,都是”丢失分号”的错误。


~ vi src/sayBye.js
var sayBye = function(name){
	return "Bye " + name;
};

增加最后一行的分号,解决上面的错误。

5). grunt-contrib-watch,是监控指定文件被修改,重新启动已注册的任务
我感觉这个插入,就点类似于supervisor的功能。

插件安装及更新到配置


~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-watch --save-dev

修改Gruntfile.js文件


grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    watch: {
        files: ['<%= jshint.files %>'],
        tasks: ['jshint', 'qunit']
    }
  });
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['uglify','concat','qunit','jshint']);

执行watch任务


~ D:\workspace\javascript\nodejs-grunt>grunt watch
Running "watch" task
Waiting...OK

#手动修改src/sayBye.js文件,下面watch的任务被触发
>> File "src\sayBye.js" changed.

Running "jshint:files" (jshint) task
Linting src/sayBye.js ...ERROR
[L3:C2] W033: Missing semicolon.
}
Linting build/nodejs-grunt.cat.js ...ERROR
[L3:C3] W032: Unnecessary semicolon.
};;var sayBye = function(name){
Linting build/nodejs-grunt.min.js ...ERROR
[L2:C42] W033: Missing semicolon.
var sayHello=function(l){return"Hello "+l};

Warning: Task "jshint:files" failed. Use --force to continue.

Aborted due to warnings.
Completed in 0.770s at Sat Aug 17 2013 20:49:15 GMT+0800 (中国标准时间) - Waiting...

上面介绍的5个任务,可能是我们比较常用配置的任务,大家也可以按照需要指定自己的任务。

下面贴上所最终的package.json和Gruntfile.js文件代码

package.json


{
  "name": "nodejs-grunt",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "3.2.2",
    "ejs": "*"
  },
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-uglify": "~0.1.1",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-qunit": "~0.2.2",
    "grunt-contrib-jshint": "~0.6.3",
    "grunt-contrib-watch": "~0.5.2"
  }
}

Gruntfile.js


module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
      },
      build: {
        src: 'src/<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    },
    concat:{
      options: {
        //定义一个字符串插入没个文件之间用于连接输出
        separator: ';'
      },
      dist: {
          src: ['src/*.js'],
          dest: 'build/<%= pkg.name %>.cat.js'
      }
    },
    qunit: {
      files: ['test/*.html']
    },
    jshint: {
        files: ['gruntfile.js', 'src/*.js', 'build/*.js'],
        options: {
            globals: {
                exports: true
            }
        }
    },
    watch: {
        files: ['<%= jshint.files %>'],
        tasks: ['jshint', 'qunit']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Default task(s).
  grunt.registerTask('default', ['uglify','concat','qunit','jshint']);

};

转载请注明出处:
http://blog.fens.me/nodejs-grunt-intro/

打赏作者