node.js博客项目开发手记


Posted in Javascript onMarch 16, 2018

需要安装的模块

  • 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.创建应用、监听端口

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.用模板引擎去解析文件

/**
 * 读取views目录下的指定文件,解析并返回给客户端 
 * 参数1:模板文件
 * 参数2:给模板传递的参数 
 */
 
res.render('index',{
  title:'首页 ',
  content: 'hello swig'
});

4.开发过程中需要取消模板缓存的限制

swig.setDefaults({
 cache: false
});
app.set('view cache', false);

5.设置静态文件托管

// 当用户访问的是/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

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 评论删除

功能开发顺序

功能模块开发顺序

  • 用户
  • 栏目
  • 内容
  • 评论

编码顺序

  • 通过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;

orm表单提交的小问题

当使用form表单提交一些代码的时候,会出现浏览器拦截的现象,原因是:浏览器误以为客户进行xss攻击。所以呢解决这个问题也很简单,就是对提交的内容进行base64或者其他形式的编码,在服务器端进行解码,即可解决。

源码地址:https://github.com/bjw1234/blog

Javascript 相关文章推荐
JS面向对象、prototype、call()、apply()
May 14 Javascript
jQuery中live方法的重复绑定说明
Oct 21 Javascript
javascript:window.open弹出窗口的位置问题
Mar 18 Javascript
js 弹出新页面避免被浏览器、ad拦截的一种新方法
Apr 30 Javascript
基于JavaScript怎么实现让歌词滚动播放
Nov 03 Javascript
js 声明数组和向数组中添加对象变量的简单实例
Jul 28 Javascript
jquery 动态合并单元格的实现方法
Aug 26 Javascript
Node.js获取前端ajax提交的request信息
Feb 20 Javascript
JavaScript禁止微信浏览器下拉回弹效果
May 16 Javascript
解决Vue.js由于延时显示了{{message}}引用界面的问题
Aug 25 Javascript
Vue表单提交点击事件只允许点击一次的实例
Oct 23 Javascript
微信小程序组件生命周期的踩坑记录
Mar 03 Javascript
vue iView 上传组件之手动上传功能
Mar 16 #Javascript
p5.js入门教程之平滑过渡(Easing)
Mar 16 #Javascript
JavaScript 隐性类型转换步骤浅析
Mar 15 #Javascript
JavaScript的数据类型转换原则(干货)
Mar 15 #Javascript
p5.js入门教程之小球动画示例代码
Mar 15 #Javascript
JavaScript实现写入文件到本地的方法【基于FileSaver.js插件】
Mar 15 #Javascript
JS实现导出Excel的五种方法详解【附源码下载】
Mar 15 #Javascript
You might like
php ucwords() 函数将字符串中每个单词的首字符转换为大写(实现代码)
2016/05/12 PHP
thinkPHP简单调用函数与类库的方法
2017/03/15 PHP
PHP使用XMLWriter读写xml文件操作详解
2018/07/31 PHP
从ThinkPHP3.2.3过渡到ThinkPHP5.0学习笔记图文详解
2019/04/03 PHP
关于Curl在Swoole协程中的解决方案详析
2019/09/12 PHP
在PHP中实现使用Guzzle执行POST和GET请求
2019/10/15 PHP
jQuery中[attribute!=value]选择器用法实例
2014/12/31 Javascript
深入了解JavaScript中的Symbol的使用方法
2015/07/28 Javascript
jQuery超简单选项卡完整实例
2015/09/26 Javascript
老生常谈 关于JavaScript的类的继承
2016/06/24 Javascript
微信小程序开发之相册选择和拍照详解及实例代码
2017/02/22 Javascript
nodejs中使用HTTP分块响应和定时器示例代码
2017/03/19 NodeJs
jQuery日期范围选择器附源码下载
2017/05/23 jQuery
JavaScript数据结构与算法之队列原理与用法实例详解
2017/11/22 Javascript
js比较两个单独的数组或对象是否相等的实例代码
2019/04/28 Javascript
浅谈一种让小程序支持JSX语法的新思路
2019/06/16 Javascript
JS字符串常用操作方法实例小结
2019/06/24 Javascript
python 矩阵增加一行或一列的实例
2018/04/04 Python
解决pycharm安装后代码区不能编辑的问题
2018/10/28 Python
使用Python正则表达式操作文本数据的方法
2019/05/14 Python
python 采用paramiko 远程执行命令及报错解决
2019/10/21 Python
CSS3制作酷炫的条纹背景
2017/11/09 HTML / CSS
佳能英国官方网站:Canon UK
2017/08/08 全球购物
毕业生求职自荐信怎么写
2014/01/08 职场文书
《伯牙绝弦》教学反思
2014/03/02 职场文书
农林环境专业求职信
2014/03/13 职场文书
亮剑精神演讲稿
2014/05/23 职场文书
医德考评自我评价
2014/09/14 职场文书
学校领导班子群众路线整改措施
2014/09/16 职场文书
2019年励志签名:致拼搏路上的自己
2019/10/11 职场文书
Mac M1安装mnmp (Mac+Nginx+MySQL+PHP) 开发环境
2021/03/29 PHP
Python通过m3u8文件下载合并ts视频的操作
2021/04/16 Python
Mybatis是这样防止sql注入的
2021/12/06 Java/Android
vue实现列表拖拽排序的示例代码
2022/04/08 Vue.js
python通过新建环境安装tfx的问题
2022/05/20 Python
Java数据结构之堆(优先队列)
2022/05/20 Java/Android