node.js博客項(xiàng)目開(kāi)發(fā)手記
需要安裝的模塊
- body-parser 解析post請(qǐng)求
- cookies 讀寫(xiě)cookie
- express 搭建服務(wù)器
- markdown Markdown語(yǔ)法解析生成器
- mongoose 操作Mongodb數(shù)據(jù)庫(kù)
- swig 模板解析引擎
目錄結(jié)構(gòu)
- db 數(shù)據(jù)庫(kù)存儲(chǔ)目錄
- models 數(shù)據(jù)庫(kù)模型文件目錄
- public 公共文件目錄(css,js,img)
- routers 路由文件目錄
- schemas 數(shù)據(jù)庫(kù)結(jié)構(gòu)文件
- views 模板視圖文件目錄
- app.js 啟動(dòng)文件
- package.json
app.js 文件
1.創(chuàng)建應(yīng)用、監(jiān)聽(tīng)端口
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')
- 注冊(cè)所使用的模板引擎 app.set('view engine','html')
3.用模板引擎去解析文件
/** * 讀取views目錄下的指定文件,解析并返回給客戶端 * 參數(shù)1:模板文件 * 參數(shù)2:給模板傳遞的參數(shù) */ res.render('index',{ title:'首頁(yè) ', content: 'hello swig' });
4.開(kāi)發(fā)過(guò)程中需要取消模板緩存的限制
swig.setDefaults({ cache: false }); app.set('view cache', false);
5.設(shè)置靜態(tài)文件托管
// 當(dāng)用戶訪問(wèn)的是/public路徑下的文件,那么直接返回 app.use('/public',express.static(__dirname + '/public'));
劃分模塊
- 前臺(tái)模塊
- 后臺(tái)模塊
- API模塊
// 根據(jù)不同的功能劃分模塊 app.use('/',require('./routers/main')); app.use('/admin',require('./routers/admin')); app.use('/api',require('./routers/api'));
對(duì)于管理員模塊 admin.js
var express = require('express'); var router = express.Router(); // 比如訪問(wèn) /admin/user router.get('/user',function(req,res,next) { res.send('User'); }); module.exports = router;
前臺(tái)路由 + 模板
main 模塊
/ 首頁(yè)
/view 內(nèi)容頁(yè)
api模塊
/首頁(yè)
/register 用戶注冊(cè)
/login 用戶登錄
/comment 評(píng)論獲取
/comment/post 評(píng)論提交
后臺(tái)(admin)路由+模板
首頁(yè)
/ 后臺(tái)首頁(yè)
用戶管理
/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)容刪除
評(píng)論內(nèi)容管理
/comment 評(píng)論列表
/comment/delete 評(píng)論刪除
功能開(kāi)發(fā)順序
功能模塊開(kāi)發(fā)順序
- 用戶
- 欄目
- 內(nèi)容
- 評(píng)論
編碼順序
- 通過(guò)Schema定義設(shè)計(jì)數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)
- 功能邏輯
- 頁(yè)面展示
連接數(shù)據(jù)庫(kù)(mongoDB)
啟動(dòng)MongoDB服務(wù)端:
mongod --dbpath=G:\data\db --port=27017
啟動(dòng)服務(wù)設(shè)置數(shù)據(jù)庫(kù)的存儲(chǔ)地址以及端口
var mongoose = require('mongoose'); // 數(shù)據(jù)庫(kù)鏈接 mongoose.connect("mongodb://localhost:27017/blog",(err) => { if(err){ console.log("數(shù)據(jù)庫(kù)連接失敗"); }else{ console.log("數(shù)據(jù)庫(kù)連接成功"); // 啟動(dòng)服務(wù)器,監(jiān)聽(tīng)端口 app.listen(3000,(req,res,next) => { console.log("app is running at port 3000"); }); } });
定義數(shù)據(jù)表結(jié)構(gòu)和模型
對(duì)于用戶數(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);
處理用戶注冊(cè)
前端通過(guò)ajax提交用戶名和密碼
url: /api/register
后端對(duì)前端提交(POST)的數(shù)據(jù)解析
var bodyParser = require('body-parser'); // bodyParser 配置 // 通過(guò)使用這一方法,可以為req對(duì)象添加一個(gè)body屬性 app.use( bodyParser.urlencoded({extended:true})); // 在api模塊中: // 1.可以定義一個(gè)中間件,來(lái)統(tǒng)一返回格式 var responseData; router.use( function(req,res,next){ // path默認(rèn)為'/',當(dāng)訪問(wèn)該目錄時(shí)這個(gè)中間件被調(diào)用 responseData = { code:0, message:'' }; next(); }); router.post('/register',(req,res,next) => { console.log(req.body); // 去判斷用戶名、密碼是否合法 // 判斷是否用戶名已經(jīng)被注冊(cè) // 通過(guò) res.json(responseData) 給客戶端返回json數(shù)據(jù) // 查詢數(shù)據(jù)庫(kù) User.findOne({ // 返回一個(gè)promise對(duì)象 username: username }).then(function( userInfo ) { if( userInfo ){ // 數(shù)據(jù)庫(kù)中有該條記錄 ... res.json(responseData); return; } // 給數(shù)據(jù)庫(kù)中添加該條信息 var user = new User({ username:username,password:password }); return user.save(); // 返回promise對(duì)象 }).then(function( newUserInfo ){ console.log(newUserInfo); res.json(responseData); // 數(shù)據(jù)保存成功 }); });
cookies 模塊的使用
全局(app.js)注冊(cè)使用
// 設(shè)置cookie // 只要客戶端發(fā)送請(qǐng)求就會(huì)通過(guò)這個(gè)中間件 app.use((req, res, next) => { req.cookies = new cookies(req, res); /** * 解析用戶的cookies信息 * 查詢數(shù)據(jù)庫(kù)判斷是否為管理員 isAdmin * 注意:查詢數(shù)據(jù)庫(kù)是異步操作,next應(yīng)該放在回調(diào)里邊 */ req.userInfo = {}; if (req.cookies.get("userInfo")) { try { req.userInfo = JSON.parse(req.cookies.get("userInfo")); // 查詢數(shù)據(jù)庫(kù)判斷是否為管理員 User.findById(req.userInfo._id).then(function (result) { req.userInfo.isAdmin = Boolean(result.isAdmin); next(); }); } catch (e) { next(); } } else { next(); } }); // 當(dāng)用戶登錄或注冊(cè)成功之后,可以為其設(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命令
用來(lái)設(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 %} //重寫(xiě)某一區(qū)塊
6.autoescape 自動(dòng)編碼
當(dāng)想在某個(gè)div中顯示后端生成的HTML代碼,模板渲染時(shí)會(huì)自動(dòng)編碼,
以字符串的形式顯示。通過(guò)以下方式,可以避免這個(gè)情況:
<div id="article-content" class="content"> {% autoescape false %} {{ data.article_content_html }} {% endautoescape %} </div>
用戶管理和分頁(yè)
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ù)分頁(yè)管理
兩個(gè)重要方法
limit(Number): 限制獲取的數(shù)據(jù)條數(shù)
skip(Number): 忽略數(shù)據(jù)的條數(shù) 前number條
忽略條數(shù):(當(dāng)前頁(yè) - 1) * 每頁(yè)顯示的條數(shù)
// 接收傳過(guò)來(lái)的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)前頁(yè) var limit = 10; // 每頁(yè)顯示的條數(shù) var skip = (cur_page - 1) * limit; //忽略的條數(shù) User.find().limit(limit).skip(skip).then(function(users){ ... // 將當(dāng)前頁(yè) page 傳給頁(yè)面 // 將最大頁(yè)碼 maxPage 傳給頁(yè)面 });
文章的表結(jié)構(gòu)
// 對(duì)于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, // 簡(jiǎn)介 description:{ type: String, default: '' }, // 內(nèi)容 content:{ type:String, default:'' } }); // 文章查詢時(shí)關(guān)聯(lián)category字段 Content.find().populate('category').then(contents => { // 那么通過(guò)這樣的方式,我們就可以找到Content表中的 // 關(guān)聯(lián)信息 content.category.category_name });
MarkDown語(yǔ)法高亮
在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語(yǔ)法解析內(nèi)容預(yù)覽 $('#bjw-content').on('keyup blur', function () { $('#bjw-previous').html(marked($('#bjw-content').val())); });
node環(huán)境中使用
// 在模板頁(yè)面引入默認(rèn)樣式 <!--語(yǔ)法高亮--> <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; } }); // 對(duì)內(nèi)容進(jìn)行markdown語(yǔ)法轉(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ī)用戶頭像生成
// 引入對(duì)應(yīng)的庫(kù) const crypto = require('crypto'); const identicon = require('identicon.js'); // 當(dāng)用戶注冊(cè)時(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表單提交的小問(wèn)題
當(dāng)使用form表單提交一些代碼的時(shí)候,會(huì)出現(xiàn)瀏覽器攔截的現(xiàn)象,原因是:瀏覽器誤以為客戶進(jìn)行xss攻擊。所以呢解決這個(gè)問(wèn)題也很簡(jiǎn)單,就是對(duì)提交的內(nèi)容進(jìn)行base64或者其他形式的編碼,在服務(wù)器端進(jìn)行解碼,即可解決。
相關(guān)文章
Node.js使用Express創(chuàng)建Web項(xiàng)目詳細(xì)教程
如果需要入門使用node.js進(jìn)行web開(kāi)發(fā),正在學(xué)習(xí) nodejs web開(kāi)發(fā)指南 的和想快速了解node.js web開(kāi)發(fā)模式的朋友,相信本文是有一定幫助意義的。2017-03-03express + jwt + postMan驗(yàn)證實(shí)現(xiàn)持久化登錄
這篇文章主要介紹了express + jwt + postMan驗(yàn)證實(shí)現(xiàn)持久化登錄,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06package.json與package-lock.json創(chuàng)建及使用詳解
這篇文章主要為大家介紹了package.json與package-lock.json創(chuàng)建及使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07使用Node.js實(shí)現(xiàn)一個(gè)多人游戲服務(wù)器引擎
這篇文章主要給大家介紹了關(guān)于如何使用Node.js實(shí)現(xiàn)一個(gè)多人游戲服務(wù)器引擎的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Node.js具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03詳解express使用vue-router的history踩坑
這篇文章主要介紹了express 使用 vue-router 的 history 踩坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06