node博客項目開發手記

NodeJs開發我的博客項目

預覽地址:http://baijiawei.topjavascript

GitHub地址:https://github.com/bjw1234/blogcss

須要安裝的模塊

  • body-parser 解析post請求
  • cookies 讀寫cookie
  • express 搭建服務器
  • markdown Markdown語法解析生成器
  • mongoose 操做Mongodb數據庫
  • swig 模板解析引擎

目錄結構

  • db 數據庫存儲目錄
  • models 數據庫模型文件目錄
  • public 公共文件目錄(css,js,img)
  • routers 路由文件目錄
  • schemas 數據庫結構文件
  • views 模板視圖文件目錄
  • app.js 啓動文件
  • package.json

app.js 文件

1.建立應用、監聽端口 html

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.配置應用模板 前端

  • 定義使用的模板引擎 app.engine('html',swig.renderFile) 參數1:模板引擎的名稱,同時也是模板文件的後綴 參數2:表示用於解析處理模板內容的方法
  • 設置模板文件存放的目錄 app.set('views','./views')
  • 註冊所使用的模板引擎 app.set('view engine','html')

3.用模板引擎去解析文件 java

/**
 * 讀取views目錄下的指定文件,解析並返回給客戶端 
 * 參數1:模板文件
 * 參數2:給模板傳遞的參數 
 */
 
res.render('index',{
    title:'首頁 ',
    content: 'hello swig'
});

4.開發過程當中須要取消模板緩存的限制 node

swig.setDefaults({
  cache: false
});

app.set('view cache', false);

5.設置靜態文件託管 git

// 當用戶訪問的是/public路徑下的文件,那麼直接返回
app.use('/public',express.static(__dirname + '/public'));

劃分模塊

  • 前臺模塊
  • 後臺模塊
  • API模塊
// 根據不一樣的功能劃分模塊
app.use('/',require('./routers/main'));
app.use('/admin',require('./routers/admin'));
app.use('/api',require('./routers/api'));

對於管理員模塊 admin.js github

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 內容頁
  • api模塊
    / 首頁
    /register 用戶註冊
    /login 用戶登陸
    /comment 評論獲取
    /comment/post 評論提交

後臺(admin)路由+模板

  • 首頁
    / 後臺首頁
  • 用戶管理
    /user 用戶列表
  • 分類管理
    /category 分類列表
    /category/add 分類添加
    /category/edit 分類修改
    /caterory/delete 分類刪除
  • 文章內容管理
    /article nei內容列表
    /article/add 內容添加
    /article/edit 內容修改
    /article/delete 內容刪除
  • 評論內容管理
    /comment 評論列表
    /comment/delete 評論刪除

功能開發順序

功能模塊開發順序 ajax

  • 用戶
  • 欄目
  • 內容
  • 評論

編碼順序 mongodb

  • 經過Schema定義設計數據存儲結構
  • 功能邏輯
  • 頁面展現

鏈接數據庫(mongoDB)

啓動MongoDB服務端:
mongod --dbpath=G:\data\db --port=27017
啓動服務設置數據庫的存儲地址以及端口

var mongoose = require('mongoose');
// 數據庫連接
mongoose.connect("mongodb://localhost:27017/blog",(err) => {
    if(err){
        console.log("數據庫鏈接失敗");
    }else{
        console.log("數據庫鏈接成功");
      // 啓動服務器,監聽端口  
      app.listen(3000,(req,res,next) => {
            console.log("app is running at port 3000");
        });
    }
});

定義數據表結構和模型

對於用戶數據表(users.js)在schema文件夾下:

var mongoose = require('mongoose');
module.exports = new mongoose.Schema({
    // 用戶名
   username:String,
   // 密碼
   password:String
});

在models目錄下建立user.js模型類

var mongoose = require('mongoose');
var userSchema = require('../schemas/users');

module.exports = mongoose.model('User',userSchema);

處理用戶註冊

  • 前端經過ajax提交用戶名和密碼

url: /api/register

  • 後端對前端提交(POST)的數據解析
var bodyParser = require('body-parser');
// bodyParser 配置
// 經過使用這一方法,能夠爲req對象添加一個body屬性
app.use( bodyParser.urlencoded({extended:true}));

// 在api模塊中:
// 1.能夠定義一箇中間件,來統一返回格式
var responseData;
router.use( function(req,res,next){ // path默認爲'/',當訪問該目錄時這個中間件被調用
    responseData = {
         code:0,
       message:''
    };
    next();
});

router.post('/register',(req,res,next) => {
    console.log(req.body);
   // 去判斷用戶名、密碼是否合法
   // 判斷是否用戶名已經被註冊
   // 經過 res.json(responseData) 給客戶端返回json數據
   
   // 查詢數據庫
   User.findOne({    // 返回一個promise對象
           username: username
   }).then(function( userInfo ) {
           if( userInfo ){ // 數據庫中有該條記錄
            ...
          res.json(responseData);
          return;
       }
       // 給數據庫中添加該條信息
       var user = new User({ username:username,password:password });
       return user.save(); // 返回promise對象
   }).then(function( newUserInfo ){
            console.log(newUserInfo);
       res.json(responseData);  // 數據保存成功  
   });
});

cookies 模塊的使用

  • 全局(app.js)註冊使用
// 設置cookie
// 只要客戶端發送請求就會經過這個中間件
app.use((req, res, next) => {
    req.cookies = new cookies(req, res);

    /**
     * 解析用戶的cookies信息
     * 查詢數據庫判斷是否爲管理員 isAdmin
     * 注意:查詢數據庫是異步操做,next應該放在回調裏邊
     */
    req.userInfo = {};
    if (req.cookies.get("userInfo")) {
        try {
            req.userInfo = JSON.parse(req.cookies.get("userInfo"));
            // 查詢數據庫判斷是否爲管理員
            User.findById(req.userInfo._id).then(function (result) {
                req.userInfo.isAdmin = Boolean(result.isAdmin);
                next();
            });
        } catch (e) {
            next();
        }
    } else {
        next();
    }
});

// 當用戶登陸或註冊成功以後,能夠爲其設置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循環
// arr = [1, 2, 3]
{ % for key, val in arr % }
<p>{ { key } } -- { { val } }</p>
{ % endfor % }

5.set命令
用來設置一個變量,在當前上下文中複用

{% set foo = [0, 1, 2, 3, 4, 5] %}

  • {% extends 'layout.html' %} // 繼承某一個HTML模板
  • {% include 'page.html' %} // 包含一個模板到當前位置
  • {% block main %} xxx {% endblock %} //重寫某一區塊

6.autoescape 自動編碼
當想在某個div中顯示後端生成的HTML代碼,模板渲染時會自動編碼,
以字符串的形式顯示。經過如下方式,能夠避免這個狀況:

<div id="article-content" class="content">
    {% autoescape false %}
    {{ data.article_content_html }}
    {% endautoescape %}
</div>

用戶管理和分頁

  • CRUD用戶數據
const User = require('../models/user');

// 查詢全部的用戶數據
User.find().then(function(users){

});

// 根據某一字段查詢數據
User.findOne({
    username:username
}).then(function(result){

});

// 根據用戶ID查詢數據
User.findById(id).then(function(user){

});

// 根據ID刪除數據
User.remove({
    _id: id
}).then(function(){

});

// 修改數據
User.update({
    _id: id
},{
    username: name
}).then(function(){
    
});
  • 數據分頁管理

兩個重要方法
limit(Number): 限制獲取的數據條數
skip(Number): 忽略數據的條數 前number條

忽略條數:(當前頁 - 1) * 每頁顯示的條數

// 接收傳過來的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;  // 當前頁
var limit = 10; // 每頁顯示的條數
var skip = (cur_page - 1) * limit; //忽略的條數

User.find().limit(limit).skip(skip).then(function(users){
    ...
  // 將當前頁 page 傳給頁面
  // 將最大頁碼 maxPage 傳給頁面
});

文章的表結構

// 對於content.js
var mongoose = require('mongoose');
var contentSch = require('../schemas/contentSch');

module.exports = mongoose.model('Content',contentSch);


// contentSch.js
module.exports = new mongoose.Schema({
    
   // 關聯字段 - 分類的id
   category:{
        // 類型
        type:mongoose.Schema.Types.ObjectId,
        // 引用
        ref:'Category'  
    },
    
    // 內容標題
    title: String,
    
    // 簡介
    description:{
        type: String,
        default: ''  
    },
    
    // 內容
    content:{
        type:String,
        default:''
    }
});

// 文章查詢時關聯category字段
Content.find().populate('category').then(contents => {
    // 那麼經過這樣的方式,咱們就能夠找到Content表中的
   // 關聯信息     content.category.category_name 
});

MarkDown語法高亮

  • 在HTML中直接使用
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<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相關配置
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語法解析內容預覽
$('#bjw-content').on('keyup blur', function () {
    $('#bjw-previous').html(marked($('#bjw-content').val()));
});
  • node環境中使用
// 在模板頁面引入默認樣式
<!--語法高亮-->
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">

const marked = require('marked');
const hljs = require('highlight.js');

// marked相關配置
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語法轉換
data.article_content_html = marked(article.content);

使文本域支持Tab縮進

$('#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();
        }
    });
});

隨機用戶頭像生成

// 引入對應的庫
const crypto = require('crypto');
const identicon = require('identicon.js');

// 當用戶註冊時,根據用戶的用戶名生成隨機頭像
let hash = crypto.createHash('md5');
hash.update(username);
let imgData = new identicon(hash.digest('hex').toString());
let imgUrl = 'data:/image/png;base64,'+imgData;

form表單提交的小問題

當使用form表單提交一些代碼的時候,會出現瀏覽器攔截的現象,緣由是:瀏覽器誤覺得客戶進行xss攻擊。因此呢解決這個問題也很簡單,就是對提交的內容進行base64或者其餘形式的編碼,在服務器端進行解碼,便可解決。

項目地址:https://github.com/bjw1234/blog

完結 撒花 ^_^ ~

相關文章
相關標籤/搜索