1. 程式人生 > >用node.js 搭建的部落格程式心得(node.js實戰讀書筆記1)

用node.js 搭建的部落格程式心得(node.js實戰讀書筆記1)

學習node已經有一段時間了,之前把了不起的node.js看完了,基本算了解了一些node的基本的用法還有一些概念了,然後就開始看第二本node.js實戰,第一章就是搭建一個部落格程式。但是不得不吐槽一下node,發展得太塊了,很多庫已經和之前的用法不一樣了,就要一直去百度google來查詢最新的用法,其實我覺得這樣並不見得是一件好事,因為不穩定,所以就不好學習,就要一直保持對於node的關注。不廢話了,這篇文章就大概說一些在這章裡面所學習到的一些東西,經驗總結吧
1、express — 基於 Node.js 平臺的 web 應用開發框架
這次的部落格程式,就是基於這個框架下面做開發的,一個mvc的架構,不過這個框架的作者在04年的時候已經轉投GO了.因為之前做php開發接觸過yii和thinkphp,都是mvc的,所以這個還是比較好入門的,也比較好理解,這個框架是node上面最多人使用的。
安裝很簡單,使用npm就可以了

$ npm install -g express-generator

這個是安裝express的程式(並不是專案,只有添加了這個程式以後才可以建立express專案)
然後就可以新建工程(專案)

$ express -e blog

$ cd blog && npm install

這樣子一個部落格工程就建立起來了,接下來就只要跑起來就可以了
可以直接在專案的根目錄執行npm start或者進入bin目錄直接node www都是可以的,大概看一下目錄結構

這裡寫圖片描述

app.js:啟動檔案,或者說入口檔案
package.json:儲存著工程的資訊及模組依賴,當在 dependencies 中新增依賴的模組時,執行npm install,npm 會檢查當前目錄下的 package.json,並自動安裝所有指定的模組
node_modules:存放 package.json 中安裝的模組,當你在 package.json 新增依賴的模組並安裝後,存放在這個資料夾下
public:存放 image、css、js 等檔案
routes:存放路由檔案
views:存放檢視檔案或者說模版檔案
bin:存放可執行檔案
先來看看app.js這個就是入口檔案,也就是程式執行的主檔案

//一開始就是引入各種模組
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

//引入兩個路由
var routes = require('./routes/index');
var users = require
('./routes/users'); //這個應該叫例項化這個專案吧 var app = express(); // 設定view的目錄和使用的模板引擎,這裡使用ejs,關於ejs,這個就是在html裡面怎麼來輸出路由裡面的變數的東西,和在html裡面巢狀php程式碼很像 app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); // 專案的ico //app.use(favicon(__dirname + '/public/favicon.ico')); //日誌記錄的中介軟體 app.use(logger('dev')); //解析son的中介軟體 app.use(bodyParser.json()); //解析url encoded請求的中介軟體 app.use(bodyParser.urlencoded({ extended: false })); //解析cookie的中介軟體 app.use(cookieParser()); //這裡是express現在唯一的內建中介軟體,靜態檔案存放的目錄 app.use(express.static(path.join(__dirname, 'public'))); //這就使用路由了,其實我們是可以在app.js裡面寫好所有的方法,但是這樣後面就不好維護,我們應該把方法單獨拿出來,放在index.js裡面去,這裡就只用應用他 app.use('/', routes); app.use('/users', users); //簡單來說就是404頁面 app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handlers // development error handler // will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); }); } // production error handler // no stacktraces leaked to user app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); }); module.exports = app;

接下來就可以在index.js裡面寫各種方法了

2、除錯
一般我們在修改程式碼以後,要先停掉之前執行的專案,然後重新開啟,這個就和php有不一樣,這樣會很麻煩,但是我們可以通過一個模組來解決這個問題

$ npm install -g supervisor

安裝 supervisor 。使用 supervisor 命令啟動 app.js:

$ supervisor app.js

這樣就不用每次關掉然後再開啟,但是實際上,這樣當代碼有錯的時候還是會一直重啟執行,難以看清錯誤的提示,其實不好用。

3、中介軟體
什麼是中介軟體,其實這個我也想了很久找了很多資料,後面發現簡單理解為類似php裡面的外掛,引入了就可以實現一些功能,不需要你自己再大費周章來實現
4、頁面通知flash
在php裡面,我們可以使用一些alter之類的來提醒使用者一些資訊,在node這或者說express裡面,可以使用flash功能,也是一箇中間件,後面程式碼裡面就可以使用來做提示了,例如這樣的

這樣在下面return的哪個跳轉頁面就有這個登出成功的提示
5、crypto
這個是node上面一個加密演算法,因為js自帶並沒有md5的之類的加密演算法,所以就要用到這些,內建了一些md5、sha1、sha256、sha512等演算法,使用方法。

首先包含進來

crypto  = require('crypto');
var md5  = crypto.createHash('md5');
        password = md5.update(‘需要加密的字串').digest('hex');

最後的password就是加密以後的字串了,最後面的digest(‘hex’)的作用是以16進位制的格式做輸出,因為預設的是2進位制的,會出現亂碼,其實還有很多其他的加密方式。具體可以參考這篇文章
https://cnodejs.org/topic/504061d7fef591855112bab5

6、multer上傳模組

var express = require('express')
var multer  = require('multer')
//這裡是定義上傳所在資料夾,這個是絕對路徑,建議在前面加上dirname這些,要不就回上傳到你所在伺服器的根目錄了
var upload = multer({ dest: 'uploads/' })

var app = express()

//第一種,這個表示你上傳的只有一個檔案,而且檔名字是avatar

app.post('/profile', upload.single('avatar'), function (req, res, next) {
  // req.file is the `avatar` file
  // req.body will hold the text fields, if there were any
})

//最多12同樣名字為photos的資料夾,假如多了就會報錯了
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
  // req.files is array of `photos` files
  // req.body will contain the text fields, if there were any
})

//這個就是一次性定義比較多名稱的檔案了
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
  // req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
  //
  // e.g.
  //  req.files['avatar'][0] -> File
  //  req.files['gallery'] -> Array
  //
  // req.body will contain the text fields, if there were any
})

但是其實上面這些都不是很好,因為都是在上傳的時候還需要重新寫一次檔案的名字,因為上傳上去到伺服器的檔名稱是隨機生成的,還沒有後綴,所以我就寫了一個model來統一實現

var multer  = require('multer');
var storage = multer.diskStorage({
     //設定上傳後文件路徑,uploads資料夾會自動建立。
    destination: function (req, file, cb) {
        cb(null, '../public/images')
     },
    //給上傳檔案重新命名,獲取新增字尾名
    filename: function (req, file, cb) {
        var fileFormat = (file.originalname).split(".");
        cb(null, file.fieldname + '-' + Date.now() + "." + fileFormat[fileFormat.length - 1]);
    }
 });

 var multerUtil = multer({
    storage: storage
 });

 module.exports = multerUtil;

這裡就是定義好了上傳資料夾和上傳以後檔案的命名方式,使用方法如下

var muilter = require('../models/multerUtil.js');
//在這裡定義好前端的幾個上傳檔案
var upload = muilter.fields([
    {name : 'file1'} ,
    {name : 'file2'} ,
    {name : 'file3'} ,
    {name : 'file4'} ,
    {name : 'file5'}
]);

//實際呼叫
app.post('/upload',function(req,res,next) {
    upload(req,res,function(err){
        if (err) {
            req.flash('error','上傳失敗');
            return res.redirect('/upload');
        }
        req.flash('success','檔案上傳成功');
        res.redirect('/');
    })
});

這樣的話,就使用起來比較有模組的思維

7、markdown

 這個東西其實沒有太多好多的,其實就是因為我們平時儲存的東西可能含有一些html的格式。但是儲存到資料庫然後輸出出來的時候,系統可能無法識別,就要轉化一下
var markdown = require('markdown').markdown;
doc.post = markdown.toHTML(doc.post);

然後在前端做輸出的時候需要這樣

<%- post %>

這樣就可以識別到那些HTML的標籤了

8、mongoose

在這個專案裡面使用的資料庫是mongodb,其實我也是第一次接觸mongodb,大概瞭解了一下,就是一個介於關係型資料庫(mysql)和非關係型資料庫(redis)之間的一種資料庫,支援的資料結構很多,也很鬆散,有點類似json這樣子,而且查詢語言很強大,node的很多專案都採用它,書裡面使用的是原生的寫法,例如下面

  //開啟資料庫
  mongodb.open(function (err, db) {
    if (err) {
      return callback(err);
    }
    //讀取 posts 集合
    db.collection('posts', function (err, collection) {
      if (err) {
        mongodb.close();
        return callback(err);
      }
      //根據使用者名稱、發表日期及文章名進行查詢
      collection.findOne({
        "name": name,
        "time.day": day,
        "title": title
      }, function (err, doc) {
        if (err) {
          mongodb.close();
          return callback(err);
        }
      });
    });
  });

我只是要查詢一個數據,又open又close太麻煩了,後面發現有這個mongoose,發現用起來很簡單,就是儲存的時候,需要整理好一下概念

連結我寫了一個db.js

var mongoose = require('mongoose');
var Schema   = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var url      = 'mongodb://localhost/blog';
mongoose.connect(url);

var db = mongoose.connection;
db.on('error',console.error.bind(console,'連線錯誤:'));
db.once('open',function(callback){
  //第一次開啟記錄,這裡可以寫一些連結上以後的資訊
});

module.exports = {
    db       : db ,
    Schema   : Schema ,
    mongoose : mongoose ,
};

然後使用的時候

var mongodb  = require('./db');
var Schema   = mongodb.Schema;
var db       = mongodb.db;

//這個類似我們要定義好一個數據庫的欄位一樣,我們也需要對你操作的這個資料庫進行一個初始化
var PostSchema = new Schema({
    name         : String ,
    title        : String ,
    post         : String ,
    tags         : [] ,
    comments     : [] ,
    pv           : {type:Number,default:0},
    reprint_info : {
        reprint_to : [{
            name: String,
            head: String
        }],
        reprint_from : {
            name  : String,
            title : String,
            day   : String
        }
    },
    time         : {
        date   : {type:Date , default:Date.now} ,
        year   : String ,
        month  : String ,
        day    : String ,
        minute : String ,
    },
});

//這個就例項化了這個物件了,可以使用這個Model來進行資料庫的操作(不包含儲存.save())

var PostModel = db.model('post',PostSchema);

//使用方法,對比上面那一段,這裡簡直簡潔太多了
PostModel.find({}).sort('-_id').exec(function (err, rs) {
    if (err) {
        return callback(err);
    }
    callback(null,rs);
})

//但是要重點說明一下關於儲存資料的方法,首先我們要對上面定義好的Model進行賦值,這裡面的資料都是我在專案裡面的,實際使用要實際的編寫

var postEntity = new PostModel({
    name  : this.name ,
    time  : time ,
    title : this.title ,
    post  : this.post ,
    tags  : this.tags
});

//然後就save就可以了

postEntity.save(function(err , rs) {
    if (err) {
        return callback(err);
    }
    callback(null , rs[0]);
})

整個專案學習下來就是一個感受,node發展很快,做頁面很方便,但是暫時還沒有感受到非同步的優勢,畢竟這個專案只是一個入門的專案,瞭解到一些基本的操作,還有一個感受就是英文真的很重要,因為這個node在國內發展的時間還不是十分久,所以文件資料都是英文的,看起來還是很痛苦。