node.js博客項(xiàng)目開發(fā)手記
需要安裝的模塊
- body-parser 解析post請求
- cookies 讀寫cookie
- express 搭建服務(wù)器
- markdown Markdown語法解析生成器
- mongoose 操作Mongodb數(shù)據(jù)庫
- swig 模板解析引擎
目錄結(jié)構(gòu)
- db 數(shù)據(jù)庫存儲目錄
- models 數(shù)據(jù)庫模型文件目錄
- public 公共文件目錄(css,js,img)
- routers 路由文件目錄
- schemas 數(shù)據(jù)庫結(jié)構(gòu)文件
- views 模板視圖文件目錄
- app.js 啟動文件
- package.json
app.js 文件
1.創(chuàng)建應(yīng)用、監(jiān)聽端口
const app = express();
app.get('/',(req,res,next) => {
res.send("Hello World !");
});
app.listen(3000,(req,res,next) => {
console.log("app is running at port 3000");
});
2.配置應(yīng)用模板
- 定義使用的模板引擎 app.engine('html',swig.renderFile) 參數(shù)1:模板引擎的名稱,同時(shí)也是模板文件的后綴 參數(shù)2:表示用于解析處理模板內(nèi)容的方法
- 設(shè)置模板文件存放的目錄 app.set('views','./views')
- 注冊所使用的模板引擎 app.set('view engine','html')
3.用模板引擎去解析文件
/**
* 讀取views目錄下的指定文件,解析并返回給客戶端
* 參數(shù)1:模板文件
* 參數(shù)2:給模板傳遞的參數(shù)
*/
res.render('index',{
title:'首頁 ',
content: 'hello swig'
});
4.開發(fā)過程中需要取消模板緩存的限制
swig.setDefaults({
cache: false
});
app.set('view cache', false);
5.設(shè)置靜態(tài)文件托管
// 當(dāng)用戶訪問的是/public路徑下的文件,那么直接返回
app.use('/public',express.static(__dirname + '/public'));
劃分模塊
- 前臺模塊
- 后臺模塊
- API模塊
// 根據(jù)不同的功能劃分模塊
app.use('/',require('./routers/main'));
app.use('/admin',require('./routers/admin'));
app.use('/api',require('./routers/api'));
對于管理員模塊 admin.js
var express = require('express');
var router = express.Router();
// 比如訪問 /admin/user
router.get('/user',function(req,res,next) {
res.send('User');
});
module.exports = router;
前臺路由 + 模板
main 模塊
/ 首頁
/view 內(nèi)容頁
api模塊
/首頁
/register 用戶注冊
/login 用戶登錄
/comment 評論獲取
/comment/post 評論提交
后臺(admin)路由+模板
首頁
/ 后臺首頁
用戶管理
/user 用戶列表
分類管理
/category 分類列表
/category/add 分類添加
/category/edit 分類修改
/caterory/delete 分類刪除
文章內(nèi)容管理
/article nei內(nèi)容列表
/article/add 內(nèi)容添加
/article/edit 內(nèi)容修改
/article/delete 內(nèi)容刪除
評論內(nèi)容管理
/comment 評論列表
/comment/delete 評論刪除
功能開發(fā)順序
功能模塊開發(fā)順序
- 用戶
- 欄目
- 內(nèi)容
- 評論
編碼順序
- 通過Schema定義設(shè)計(jì)數(shù)據(jù)存儲結(jié)構(gòu)
- 功能邏輯
- 頁面展示
連接數(shù)據(jù)庫(mongoDB)
啟動MongoDB服務(wù)端:
mongod --dbpath=G:\data\db --port=27017
啟動服務(wù)設(shè)置數(shù)據(jù)庫的存儲地址以及端口
var mongoose = require('mongoose');
// 數(shù)據(jù)庫鏈接
mongoose.connect("mongodb://localhost:27017/blog",(err) => {
if(err){
console.log("數(shù)據(jù)庫連接失敗");
}else{
console.log("數(shù)據(jù)庫連接成功");
// 啟動服務(wù)器,監(jiān)聽端口
app.listen(3000,(req,res,next) => {
console.log("app is running at port 3000");
});
}
});
定義數(shù)據(jù)表結(jié)構(gòu)和模型
對于用戶數(shù)據(jù)表(users.js)在schema文件夾下:
var mongoose = require('mongoose');
module.exports = new mongoose.Schema({
// 用戶名
username:String,
// 密碼
password:String
});
在models目錄下創(chuàng)建user.js模型類
var mongoose = require('mongoose');
var userSchema = require('../schemas/users');
module.exports = mongoose.model('User',userSchema);
處理用戶注冊
前端通過ajax提交用戶名和密碼
url: /api/register
后端對前端提交(POST)的數(shù)據(jù)解析
var bodyParser = require('body-parser');
// bodyParser 配置
// 通過使用這一方法,可以為req對象添加一個(gè)body屬性
app.use( bodyParser.urlencoded({extended:true}));
// 在api模塊中:
// 1.可以定義一個(gè)中間件,來統(tǒng)一返回格式
var responseData;
router.use( function(req,res,next){ // path默認(rèn)為'/',當(dāng)訪問該目錄時(shí)這個(gè)中間件被調(diào)用
responseData = {
code:0,
message:''
};
next();
});
router.post('/register',(req,res,next) => {
console.log(req.body);
// 去判斷用戶名、密碼是否合法
// 判斷是否用戶名已經(jīng)被注冊
// 通過 res.json(responseData) 給客戶端返回json數(shù)據(jù)
// 查詢數(shù)據(jù)庫
User.findOne({ // 返回一個(gè)promise對象
username: username
}).then(function( userInfo ) {
if( userInfo ){ // 數(shù)據(jù)庫中有該條記錄
...
res.json(responseData);
return;
}
// 給數(shù)據(jù)庫中添加該條信息
var user = new User({ username:username,password:password });
return user.save(); // 返回promise對象
}).then(function( newUserInfo ){
console.log(newUserInfo);
res.json(responseData); // 數(shù)據(jù)保存成功
});
});
cookies 模塊的使用
全局(app.js)注冊使用
// 設(shè)置cookie
// 只要客戶端發(fā)送請求就會通過這個(gè)中間件
app.use((req, res, next) => {
req.cookies = new cookies(req, res);
/**
* 解析用戶的cookies信息
* 查詢數(shù)據(jù)庫判斷是否為管理員 isAdmin
* 注意:查詢數(shù)據(jù)庫是異步操作,next應(yīng)該放在回調(diào)里邊
*/
req.userInfo = {};
if (req.cookies.get("userInfo")) {
try {
req.userInfo = JSON.parse(req.cookies.get("userInfo"));
// 查詢數(shù)據(jù)庫判斷是否為管理員
User.findById(req.userInfo._id).then(function (result) {
req.userInfo.isAdmin = Boolean(result.isAdmin);
next();
});
} catch (e) {
next();
}
} else {
next();
}
});
// 當(dāng)用戶登錄或注冊成功之后,可以為其設(shè)置cookies
req.cookies.set("userInfo",JSON.stringify({
_id:result._id,
username:result.username
}));
swig模板引擎
1.變量
{{ name }}
2.屬性
{{ student.name }}
3.if判斷
{ % if name === '郭靖' % }
hello 靖哥哥
{ % endif % }
4.for循環(huán)
// arr = [1, 2, 3]
{ % for key, val in arr % }
<p>{ { key } } -- { { val } }</p>
{ % endfor % }
5.set命令
用來設(shè)置一個(gè)變量,在當(dāng)前上下文中復(fù)用
{% set foo = [0, 1, 2, 3, 4, 5] %}
{% extends 'layout.html' %} // 繼承某一個(gè)HTML模板
{% include 'page.html' %} // 包含一個(gè)模板到當(dāng)前位置
{% block main %} xxx {% endblock %} //重寫某一區(qū)塊
6.autoescape 自動編碼
當(dāng)想在某個(gè)div中顯示后端生成的HTML代碼,模板渲染時(shí)會自動編碼,
以字符串的形式顯示。通過以下方式,可以避免這個(gè)情況:
<div id="article-content" class="content">
{% autoescape false %}
{{ data.article_content_html }}
{% endautoescape %}
</div>
用戶管理和分頁
CRUD用戶數(shù)據(jù)
const User = require('../models/user');
// 查詢所有的用戶數(shù)據(jù)
User.find().then(function(users){
});
// 根據(jù)某一字段查詢數(shù)據(jù)
User.findOne({
username:username
}).then(function(result){
});
// 根據(jù)用戶ID查詢數(shù)據(jù)
User.findById(id).then(function(user){
});
// 根據(jù)ID刪除數(shù)據(jù)
User.remove({
_id: id
}).then(function(){
});
// 修改數(shù)據(jù)
User.update({
_id: id
},{
username: name
}).then(function(){
});
數(shù)據(jù)分頁管理
兩個(gè)重要方法
limit(Number): 限制獲取的數(shù)據(jù)條數(shù)
skip(Number): 忽略數(shù)據(jù)的條數(shù) 前number條
忽略條數(shù):(當(dāng)前頁 - 1) * 每頁顯示的條數(shù)
// 接收傳過來的page
let query_page = Number(req.query.page) || 1;
query_page = Math.max(query_page, 1); // 限制最小為1
query_page = Math.min(Math.ceil(count / limit), query_page); // 限制最大值 count/limit向上取整
var cur_page = query_page; // 當(dāng)前頁
var limit = 10; // 每頁顯示的條數(shù)
var skip = (cur_page - 1) * limit; //忽略的條數(shù)
User.find().limit(limit).skip(skip).then(function(users){
...
// 將當(dāng)前頁 page 傳給頁面
// 將最大頁碼 maxPage 傳給頁面
});
文章的表結(jié)構(gòu)
// 對于content.js
var mongoose = require('mongoose');
var contentSch = require('../schemas/contentSch');
module.exports = mongoose.model('Content',contentSch);
// contentSch.js
module.exports = new mongoose.Schema({
// 關(guān)聯(lián)字段 - 分類的id
category:{
// 類型
type:mongoose.Schema.Types.ObjectId,
// 引用
ref:'Category'
},
// 內(nèi)容標(biāo)題
title: String,
// 簡介
description:{
type: String,
default: ''
},
// 內(nèi)容
content:{
type:String,
default:''
}
});
// 文章查詢時(shí)關(guān)聯(lián)category字段
Content.find().populate('category').then(contents => {
// 那么通過這樣的方式,我們就可以找到Content表中的
// 關(guān)聯(lián)信息 content.category.category_name
});
MarkDown語法高亮
在HTML中直接使用
<link rel="stylesheet" >
<script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script src="https://cdn.bootcss.com/marked/0.3.17/marked.min.js"></script>
// marked相關(guān)配置
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: true,
smartLists: true,
smartypants: false,
highlight: function (code) {
return hljs.highlightAuto(code).value;
}
});
// MarkDown語法解析內(nèi)容預(yù)覽
$('#bjw-content').on('keyup blur', function () {
$('#bjw-previous').html(marked($('#bjw-content').val()));
});
node環(huán)境中使用
// 在模板頁面引入默認(rèn)樣式
<!--語法高亮-->
<link rel="stylesheet" >
const marked = require('marked');
const hljs = require('highlight.js');
// marked相關(guān)配置
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: true,
smartLists: true,
smartypants: false,
highlight: function (code) {
return hljs.highlightAuto(code).value;
}
});
// 對內(nèi)容進(jìn)行markdown語法轉(zhuǎn)換
data.article_content_html = marked(article.content);
使文本域支持Tab縮進(jìn)
$('#bjw-content').on('keydown',function(e){
if(e.keyCode === 9){ // Tab鍵
var position = this.selectionStart + 2; // Tab === 倆空格
this.value = this.value.substr(0,this.selectionStart) + " " + this.value.substr(this.selectionStart);
this.selectionStart = position;
this.selectionEnd = position;
this.focus();
e.preventDefault();
}
});
layer 彈框
// 顯示彈框
function showDialog(text, icon, callback) {
layer.open({
time: 1500,
anim: 4,
offset: 't',
icon: icon,
content: text,
btn: false,
title: false,
closeBtn: 0,
end: function () {
callback && callback();
}
});
});
隨機(jī)用戶頭像生成
// 引入對應(yīng)的庫
const crypto = require('crypto');
const identicon = require('identicon.js');
// 當(dāng)用戶注冊時(shí),根據(jù)用戶的用戶名生成隨機(jī)頭像
let hash = crypto.createHash('md5');
hash.update(username);
let imgData = new identicon(hash.digest('hex').toString());
let imgUrl = 'data:/image/png;base64,'+imgData;
orm表單提交的小問題
當(dāng)使用form表單提交一些代碼的時(shí)候,會出現(xiàn)瀏覽器攔截的現(xiàn)象,原因是:瀏覽器誤以為客戶進(jìn)行xss攻擊。所以呢解決這個(gè)問題也很簡單,就是對提交的內(nèi)容進(jìn)行base64或者其他形式的編碼,在服務(wù)器端進(jìn)行解碼,即可解決。
相關(guān)文章
nodejs不用electron實(shí)現(xiàn)打開文件資源管理器并選擇文件
最近在開發(fā)一些小腳本,用 nodejs 實(shí)現(xiàn),其中很多功能需要選擇一個(gè)/多個(gè)文件,或者是選擇一個(gè)文件夾,這種情況下網(wǎng)上給出的解決方案都是 electron,但是我一個(gè)小腳本用 electron 屬實(shí)有點(diǎn)夸張了,后來轉(zhuǎn)念一想可以通過 powershell 來實(shí)現(xiàn)類似的功能,需要的朋友可以參考下2024-01-01
Ubuntu22.04系統(tǒng)下升級nodejs到v18版本
ubuntu默認(rèn)安裝的nodejs版本比較老,要安裝到最新的,下面這篇文章主要給大家介紹了關(guān)于Ubuntu22.04系統(tǒng)下升級nodejs到v18版本的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06
淺談NodeJs之?dāng)?shù)據(jù)庫異常處理
這篇文章主要介紹了淺談NodeJs之?dāng)?shù)據(jù)庫異常處理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10

