用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在國內發展的時間還不是十分久,所以文件資料都是英文的,看起來還是很痛苦。