《Nodejs開發加密貨幣》之三:Nodejs讓您的前端開發像子彈飛一樣

關於

《Nodejs開發加密貨幣》,是一個加密貨幣產品的詳細開發文檔,涉及到使用Nodejs開發產品的方方面面,從前端到後臺、從服務器到客戶端、從PC到移動、加密解密等各個環節。代碼完全開源、文章免費分享。 相關資源見 http://ebookchain.org

QQ交流羣: 185046161

前言

從本文開始,我們正式進入Nodejs的世界。

本文,將指引您搭建Nodejs開發環境,向您介紹Nodejs的安裝、使用,幫您快速進入Nodejs的世界。

通過本文,讓您對前端開發有一個完整、全新的認知,可以學習到如何將一些第三方平臺的資源爲己所用,比如像巴比特一樣即時顯示交易市場的交易行情。

本文的實例,就是上篇文章提到的加密貨幣開發語言統計分析項目(Statistical Analysis of Cryptocurrency Development Languages,簡稱Sacdl),點擊這裏,在線體驗。

項目需求

Sacdl項目需要具備以下幾個功能:

  • 方便地讀取第三方網站(這裏是github)的Api,實現項目搜索功能;
  • 對讀取的數據集中處理,方便地轉化爲我們需要的信息;
  • 通過柱狀圖、矩陣圖、表格等圖表格式,將數據可視化;
  • 方便擴展,爲以後添加更多圖表樣式或其他網站Api(比如交易市場的)做好準備。

技術選型

無處不選擇。大方向要選擇,具體到每個開發包都要去甄別,安全嗎?好用嗎?性能高嗎?是否有更好的方案?等等。

僅從上述需求來說,一個html文件,再加一個js文件就基本搞定,第三方包都用不着,Nodejs更是大才小用。

但事實上,很多僅僅是前端的項目,比如:Bootstrap等,都基於Nodejs,爲什麼?答案很簡單,它供了諸多方便實用的工具。

比如說:

  • 組織方便:js沒有模塊化組織代碼的能力。一個項目,js代碼通常會分割在不同的文件中,以往的方式,處理起來非常頭疼,現在利用Nodejs的模塊管理,可以讓您徹底解脫;
  • 資源廣泛:Nodejs的出現,讓js第三方包像雨後春筍一樣遍地開花。需要什麼,一條命令,Nodejs就幫您辦了,這會帶來極大便利;
  • 全棧處理:開發完,還有很多事情要做,比如:要對前端代碼js或css文件進行合併、壓縮、混淆,以及項目部署等。體驗過ruby on rails一鍵部署功能的小夥伴,都會印象深刻。Nodejs也很容易做到,而且更加自然、流暢。

總之,有了Nodejs,我們可以像開發後臺程序一樣組織前端代碼和項目了;有了Nodejs,就有了它背後強大的技術社區支持。

Nodejs簡介

有小盆友說,第一次看到Nodejs,還以爲就是一個js文件呢。呵呵,其實,很多前端的應用,比如大家吵得最歡的前端開發框架三劍客,Angular.js, Backone.js, .Ember.js等,其實就是一個js文件。那麼,

Nodejs是什麼呢?

官方解釋是這樣的:

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. 

翻譯如下:

Node.js® 是一個搭建在Chrome V8上的JavaScript即時運行平臺,採用事件驅動、非阻塞I/O模型,既輕量又高效。

用句大白話解釋就是,Nodejs是一個可以讓您利用JavaScript語言開發應用的平臺, 是構建運行在分佈式設備上的數據密集型實時程序的完美選擇。

請注意哦,這裏可沒說是Web應用,很多人認爲Nodejs僅能開發以服務器爲中心的web應用,其實不然,PC端、移動端都可以。當然,我們看到的大部分是Web應用,它是php+apache, jsp+tomcat, ruby on rails + Passenger(或thin) + nginx 等傳統web開發的絕佳替代品。

如果你還沒有直觀感受,那麼,我告訴你一個信息,Nodejs的作者原本是想開發一種取代apache、nginx、tomcat等產品的傳統服務器軟件的,結果發展成了今天Nodejs的模樣,你用Nodejs寫的每一個應用,即可以認爲是一個服務器軟件,也可以認爲是一個web應用,而且它是如此簡單、高效。

什麼是數據密集型、實時應用?

聊天室、即時通信等都是。當然,所有的交易市場(比特幣、股票、基金等),電子商務網站的即時交易等也是。甚至物聯網,比如電器設備的監控和遠程控制。本人剛完成的一個項目,是一家大型連鎖超市的電器設備綜合監控系統,就是使用Nodejs開發的。

開發步驟

下面的過程會有點羅嗦,耐心點,很簡單。

1.搭建環境

對於初學者,建議先去[Nodejs官方網站][]瀏覽一遍。這裏有幣友推薦的一箇中文網站,runoob.com,對於英文不太好的用戶,有一定幫助。

我個人的開發環境是這樣的:

  • 操作系統是Ubuntu系統:您可以在現有系統上,使用虛擬機軟件安裝它。我們的全部示例和截圖都是在ubuntu上完成;

  • IDE工具:Sublime Text

(1)Nodejs的安裝

強烈建議參考官網信息([Nodejs官方網站][],見參考資料)

我在Ubuntu上通過nvm安裝管理Nodejs,具體方法,這裏有一篇詳細文檔,快速搭建 Node.js 開發環境以及加速 npm,請務必閱讀一遍。這裏摘錄其中關鍵命令(下面的命令都要在Ubuntu的命令行程序下運行):

安裝Nvm

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash

用Nvm安裝Nodejs

$ nvm install 5.1.0
$ nvm alias default 5.1.0

說明:5.1.0 是Nodejs版本信息,寫作本文時,最新穩定版是5.4.0,長期支持版是4.2.4

安裝使用CNpm

使用淘寶npm鏡像,可以提高我們的組件下載數度

http://npm.taobao.org/

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

查看版本信息

$ nvm -v
$ node -v
$ npm -v

我的版本信息如下:

nvm 0.29.0
node v5.1.0 
npm v3.3.12

2.新建工程

在您電腦上,新建一個文件夾sacdl-project,作爲工程目錄,路徑如下:

/home/yourname/projects/sacdl-project

我們通常會把前端代碼放在public目錄下,然後分別建立js,css,images等目錄,最後建立文件index.htmljs/app.js,用於顯示頁面和寫我們的js代碼,結構如下:

project-folder

上述結構中,

`js/searcher.js`是搜索框處理代碼,
`js/utils.js`是數據處理代碼,
`js/bar.js`文件是d3.js的柱狀圖代碼,
`js/treemap.js`是樹狀矩陣圖的代碼,
'js/app.js'用戶綜合調用,類似於控制器或路由。

前端第三方組件,比如d3.js等都存放在bower_components,由bower自動生成; 後臺第三方模塊在node_modules,由npm自動生成。

3.前端組件

在命令行,進入上述工程目錄,安裝前端管理工具bower

cnpm install -g bower # 也可以使用 npm install * 命令,二者一樣,只不過cnpm使用淘寶鏡像,在中國安裝會快些

說明:bower是一個npm包,是專門用來管理web前端(包含js,css,images,fonts等)依賴包的。我們可以簡單類比,bower用於管理前端包,npm管理後臺庫(包),二者用法十分相似。

初始化

bower init

結果如下:

bower-init

這樣會生成一個bower.json文件,這樣我們的代碼就被作爲一個完整的前端組件來管理了。

通過bower,安裝d3.js

bower install d3 --save

選項--save將在bower.json文件裏,寫入下面的信息:

  "dependencies": {
    ...
    "d3": "~3.5.12",
    ...
  }

這樣,在另一臺電腦開發時,克隆完代碼,就可以直接運行下面的命令,自動安裝全部依賴的第三方組件了

bower intall

說明:d3.js是提供了前端顯示的柱狀圖、餅狀圖等,是數據可視化非常出名的前端開發包。國人的有百度的echarts,還有一個highcharts,這三者經常被拿來比較。簡單的區分,就是,d3.js像開發包,可以任由您編程開發,但據說入門較難;其他兩個更像是模板,拿來就用。

這裏,我選擇了d3.js,純屬個人喜好,一方面,我個人喜歡完全控制代碼;另一方面,在開發電子書版權保護和交易系統,用到了它。

4.前端流程

按照上面的需求,我們的流程大致是這樣的:

front-data-follow

1.接受請求:提供一個輸入框,接受用戶輸入,獲得查詢關鍵字,並轉化爲github.com的Api請求地址;
2.獲得數據:根據上述地址,通過ajax請求數據(這裏是d3.js的d3.json()方法),對數據進行處理;
3.展示數據:使用d3.js編寫圖表樣式,將上述數據展示出來。

5.學習Api

第一步非常簡單,只要提供一個輸入框就是了。我們直接從第二步開始研究吧。

github是用ruby on rails開發的,它的api具有典型的ror的restful風格。下面是,官方搜索示例

請求下面的地址

https://api.github.com/search/repositories?q=tetris+language:assembly&sort=stars&order=desc

可以得到對應的json格式的數據。 官方是使用的curl命令行工具,我們直接使用瀏覽器即可,有圖爲證:

github-search-example

這就是我們得到的原始數據結構。大部分情況下,需要重新整理,不然就不用費勁開發了。這裏,我們先把它轉化爲樹形矩陣圖需要的數據格式,如下:

{
    "name": "languages",
    "children": [{
        "name": "javascript",
        "children": [{
            "name": "imfly/myIDE",
            "watchers_count": 100,
            "forks_count": 50
        }]
    }]
}

這裏的意思是,整個數據根節點就是languages(自己建就是了), 它以各個語言爲子節點;各語言節點,則以它們的版本庫爲節點,這裏才存儲着我們需要的基本信息。

6.數據整理

我們在public/js文件夾下,新建utils.js(名字隨便起),然後使用文本編輯器打開,我使用的是Sublime text.

(1)模塊化前端代碼

爲了實現模塊化編程,採取下面的格式組織前端代碼(當然,這並不是Nodejs的模塊形式,不過異曲同工),

var Utils = (function(){
    //局部變量定義
    var a = 0;

    //公共方法
    return {
           settings: function(){},
       init: function(){},
       ...
    }

    //私有方法
    function name(){}
}())

在引入該文件的index.html文件裏,就可以這樣調用

Utils.init();

而無法這樣調用

Utils.name();

(2)轉換數據格式

如何將api讀取的數據整理成我們想要的格式呢?代碼如下:

// 一定會有一個地方傳入dataset,先彆着急
function getTreeData(dataSet) {
    var languages = {};

    //新建根節點
    var result = {
        "name": "languages",
        "children": []
    }

    //循環處理子節點
    if (dataSet && dataSet.items) {
        var items = dataSet.items;

        //先找出涉及到語言
        items.forEach(function(item, index) {
        if (typeof languages[item.language] === "undefined") {
            languages[item.language] = index;
        };
        })

            //根據語言進行整理
        for (var language in languages) {
        //原來有些版本庫,是沒有語言信息。github的語言識別並不是完美的
        if (language === "null") {
            language = "others";
        };

        //每種語言的子節點
        var root = {
            "name": language,
            "children": []
        };

        //從全局數據中再次查找我們的數據
        items.forEach(function(item, index) {
            var child = {
                "name": item.full_name,
                "watchers_count": item.watchers_count,
                "forks_count": item.forks_count
            };

            if (item.language === language || (item.language === "null" && language === "others")) {
                root.children.push(child);
            };

        })

        result.children.push(root);
        }
    }

    //返回結果
    return result;
}

顯然,這是一個私有方法。因爲類似這樣對數據的整理,每一個圖表都要做。我們是把上面的方法作爲第一步處理,然後把結果緩存,其他格式的數據都以它爲基礎獲得(公共方法)。請參考源碼js/utils.js,·點這裏

7.D3.js渲染

數據有了,終於有機會弄成我們想要的樣式了。

(1)瞭解d3.js流程

有人說,對於初學者,d3.js的入門有點困難。如果您嘗試了之後,真覺得難,可以選擇echarts,或xcharts(來自於d3.js,下面有鏈接),方法相同。

d3.js的基本流程是:

  • 在html中,提供展示圖表的位置,通常是給一個div#Id;
  • 請求並填充數據;
  • 渲染圖表,用append()新增元素,用remove()刪除多餘元素;

我們用最簡單的例子,演示一下(代碼在工程源碼的test文件夾下):

test.html頁面添加一個div元素,如下:

<div id="testId"></div>

新建test.js文件,寫下如下代碼:

//這是要渲染的數據,可以動態獲得
var dataset = [1, 2, 3, 4];

//填充數據,通常要使用d3.layout提供的數據模板進行處理,然後用data()方法去填
var chart = d3.select('#testId')
        .selectAll('p')
        .data(dataset, function(d) { return d; });

//渲染視圖,主要是下面2個方法
//data()之後纔可以調用的enter()方法,意思是有數據填充的那部分圖表元素,通常去增加`append`元素
chart
.enter()
.append('p')
.text(function(d, i) {
    return [d, i];
})

//data()之後纔可以調用的exit()方法,意思是無法獲得數據填充的那部分圖表元素,通常要刪除`remove`
chart.exit().remove();

比如上面,我們默認提供了dataset的4個數值,第一次渲染,會正常顯示4個元素;接着,數據dataset換成[5,6],再此渲染,enter()方法將獲得原來渲染[1,2]的元素,並將其值換成[5,6],而[3,4]位置的元素因爲沒有了數據,被刪除掉。這樣就實現了圖表動態轉換。

注意:上面提到的d3.layout可能是一個顛覆三觀的概念。layout作爲層的概念,通常在html視圖中用作全局共享的模板文件,比如:layout.html, layout.ejs等。但是,這裏d3.js是用在數據上的,提供了d3.layout.treemap()等方法,用於對各種圖表數據進行計算和處理,即:數據模板。d3.js的視圖處理,就是使用append()和remove()去增加或刪除元素來處理,配合諸如.style()元素樣式格式化的方法,實現頁面控制。顯然,這樣做的意義就是真正的數據驅動

(2)渲染我們的數據

d3.js提供了d3.json(),d3.csv()等請求數據的方法,我們上述數據是json格式,自然就用前者了

我們以矩陣圖爲例(我也是參考官方的示例,見參考資源),在index.html加入如下元素

<div id="sacdlTreemap"></div>

然後,編寫js/treemap.js代碼,用於渲染圖表。

最後,在js/app.js裏,加載數據:

----部分代碼------
d3.json(url, function(err, data) {
    if (err) {
    ...
        alert("加載數據失敗,請檢查您的網絡設置。")
    };

    Utils.getData(data);

    Treemap.show();

    ...
});
----部分代碼------

具體請看源碼。

(3)查看效果

前端不用服務器,因此直接右鍵,選擇在瀏覽器中打開就是了。看看效果如何。

front-url.jpg

8.代碼調試

如果達到預期效果,如何發現問題所在呢?前端調試和測試也是一門學問,內容所限,無法細說。告訴您本人常用的調試前端代碼的工具,就是火狐瀏覽器的firebug擴展插件。當然,對於本應用,用火狐或谷歌瀏覽器默認的控制檯就可以了。

具體用法是,在打開的瀏覽器頁面,按下F12,就會在頁面底部彈出控制檯窗口,如下:

firefox-console

錯誤信息,斷點信息等一目瞭然。

9.部署發佈

經過一番調試,代碼終於達到預期效果。爲了提高頁面加載速度,增強用戶體驗,需要對代碼進行合併、壓縮,如果要保護自己的勞動,不想被別人無償使用,還需要對代碼進行混淆,最好部署到專門的服務器空間上去。這些工作,可以實現一鍵操作。

Nodejs圈子裏,有2個最爲流行的工具,一個是grunt,出現的最早。另一個是gulp,後來居上,號稱是爲了解決前者的問題而生的,目的就是爲了消滅前者。事實證明,gulp確實很好用,簡單、高效。我們就用它。

(1)原理

gulp用到的核心概念就是管道流,你可以理解成我們生活中的各種管道的概念,比如自來水管道。文件或數據就是水,gulp各類插件就是過濾網等水處理器械。

設計一個任務,就是建設一條管道,涉及到5個方法,分別是:

1>構建管道並起個名字用`gulp.task()`,
2>管道入口方法叫`gulp.src()`(src代表源文件),
每一節管道叫`.pipe()`(要用在入口和出口中間,在其中放入各種插件方法,就相當於加了層過濾網),
3>一直流向管道出口,方法叫`gulp.dest()`(dest英文意思是目標),
4>監控水流變化(文件變化)用`gulp.watch`,
5>綜合調度各個管道的運行,用`gulp.run`

最後在命令行啓動管道,就用gulpgulp taskname命令

如圖,看看下面的幾條管道,是不是很容易理解:

pipe

注:pipe管道,是linux或Nodejs等對於文件處理的一個重要概念,我們會在以後的文章中進一步說明。

(2)安裝

首先,

cnpm install gulp --global

這裏使用--global進行全局安裝,這樣我們纔可以在任何路徑下使用gulp命令。

然後,

cnpm install gulp --save-dev

這裏安裝在工程目錄下,目的是方便管理。同時,因爲gulp僅僅是開發輔助工具,只在本地開發機器上使用,因此上述命令添加--save-dev選項,把gulp模塊安裝在開發依賴裏。

(3)建管道

gulp命令默認請求gulpfile.js文件,手動建一個吧,上面說各類管道(任務)都在這個文件裏,本工程對js進行處理的代碼如下:

----其他代碼-----
// 開建管道,名字叫`js`
gulp.task('js', ['clean'], function() {
    // 合併、壓縮、混淆,並拷貝js文件
    return es.merge(                   //這是個workflow插件,是Nodejs模塊,都是Nodejs應用,當然也可以使用了
            gulp.src(assets.js.vendor) //管道1入口
            .pipe(gulp.dest(settings.destFolder + '/js/')), // 直接流到管道1出口,相當於簡單拷貝

            gulp.src(assets.js.paths) //管道2入口
            .pipe(order(assets.js.order)) //過濾網1:排序
            .pipe(sourcemaps.init())  //過濾網2:建sourcemaps
            .pipe(uglify())   //這算是管道中的管道了,過濾網3:混淆處理
            .pipe(concat(settings.prefix.destfile + '.js')) //過濾網4:合併處理
            .pipe(sourcemaps.write()) //建maps結束,輸出sourcemaps
            .pipe(gulp.dest(settings.destFolder + '/js')) //管道2出口
        )
        .pipe(concat(settings.prefix.mergefile + '.js'))  //彙總管道:對上述2個管道的輸出再合併
        .pipe(gulp.dest(settings.destFolder + '/js/'))    //彙總管道出口
});
----其他代碼-----

詳情請看源碼。

在命令行,輸入如下命令,運行該任務

gulp js

(4)插件

上述代碼中用到的ordersourcemaps,uglify等對應3個gulp插件,可以從官網找到,本工程涉及到的,算是幾個最常用的插件,如下:

"gulp-concat": "^2.6.0",        //合併js,css等
"gulp-cssnano": "^2.1.0",       //css壓縮,取代了gulp-minify-css
"gulp-gh-pages": "^0.5.4",      //部署到github的`gh-pages`,本工程在線演示就是這麼部署的
"gulp-imagemin": "^2.4.0",      //圖片壓縮
"gulp-order": "^1.1.1",         //js,css等順序合併等
"gulp-processhtml": "^1.1.0",   //將處理完的代碼,替換到.html、.ejs等模板文件裏
"gulp-sourcemaps": "^1.6.0",    //產生sourcemaps文件
"gulp-uglify": "^1.5.1",        //混淆和壓縮js文件

(5)部署

上面的插件列表裏,有一個gulp-gh-pages插件,可以幫我們部署到gh-pages

var ghPages = require('gulp-gh-pages');

//Deploy
gulp.task('deploy', function() {
    return gulp.src('./dist/**/*')
               .pipe(ghPages());
});

運行如下命令,即可

gulp deploy

當然,最好在運行部署命令之前,先運行合併、壓縮等處理命令,如果想省事,就定義在上述部署任務裏。請參考源碼。

總結

寫完這一章,好累。回頭看看,發現上面的每一個小節,其實都可以用一章來說明。缺乏細節,會讓讀者,特別是新手,很辛苦。相反,太注重細節,又會讓我們失去主題。所以,這也是一個很難取捨的過程,歡迎您提供寶貴意見或建議。

這裏提供了完整的程序源碼。源碼提供的功能比文章描述的多,比如對輸入框的處理、事件的監聽、多數據格式的處理,還包括bootstrap的使用等。但文章僅摘錄了部分核心內容,在閱讀的時候,要注意結合源碼,實在不明白就參考下面提供的資源,或給我留言。

現在,您應該可以自己動手試試,應該能夠輕鬆的把比特時代、okcoin等交易市場的交易行情,即時的顯示在自己的網站上了。如果,掌握些比特幣核心代碼,它也提供了很多Api,能不能像本文這樣直接讀取呢?如果可以的話,豈不是很容易就能開發一個blockchain.info

具體分析,請看下一篇:《Nodejs開發加密貨幣》之三:Nodejs讓後臺開發像前端一樣簡單,簡單介紹Nodejs後臺開發實踐,寫Nodejs模塊,爲以後的代碼分析打好基礎。

鏈接

項目源碼: https://github.com/imfly/sacdl-project

試用地址:https://imfly.github.io/sacdl-project

本文源地址: https://github.com/imfly/bitcoin-on-nodejs

電子書閱讀: http://bitcoin-on-nodejs.ebookchain.org/

參考

(1)參考用例

d3.layout.treemap: http://mbostock.github.io/d3/talk/20111018/treemap.html

Grouped horizontal bar chart: http://bl.ocks.org/erikvullings/51cc5332439939f1f292

(2)官方網站

Nodejs官方網站: https://nodejs.org/

Bower官方網站: http://bower.io/

d3.js官方網站: https://d3js.org

(3)其他文檔

xcharts一個封裝d3.js的圖表展示包

大數據時代的圖表可視化利器——highcharts,D3和百度的echarts

d3的使用心得和學習資料彙總

發佈了34 篇原創文章 · 獲贊 5 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章