前端nodejs+express+mongodb的一个简单案例

web前端开发入门书 前端开发新建项目 入门前端开发用书

主要的功能介绍

  1. 用户可以完成注册、登录然后登录后对商品的浏览。
  2. 登录之后,用户可以对相关商品进行选购并添加到购物车。
  3. 用户可以对购物车里面的商品进行增加、减少、删除操作。
  4. 用户可对购物车商品进行结算操作。

使用的主要技术
1.NodeJS:node.js采用Google Chrome浏览器的V8引擎,一个后端的Javascript运行环境,提供很多系统级的API,如文件操作、网络编程等。
2.MongoDB:MongoDB是一个基于分布式文件存储的一个高性能,开源,无模式的文档型数据库,数据以BSON文档的格式存储在磁盘上。
3.Express:一个简洁、灵活的基于node.jsweb应用开发框架, 支持Ejs、jade等多种模板,并且提供一系列强大的功能,比如:模板解析、静态文件服务、中间件、路由控制等等。
4.Mongoose:一个针对MongoDB操作的对象模型库,封装了MongoDB对文档的的一些增删改查等常用方法。

创建目录

  1. public目录:存放静态文件。
  2. routes目录:存放路由文件。
  3. views目录: 存放页面文件。
  4. common目录:存放公共文件。
    安装项目所需的库 npm install

1.注册页面
首先在views下添加一个注册页面,register.html页面。

如何访问到注册页面呢?看如下代码app.js
javascript 代码

var express = require(‘express’)
var ejs = require(‘ejs’)
var path = require(‘path’)
var app = express()
app.set(‘view engine’, ‘html’)
app.engine(‘.html’, ejs.__express)
app.set(‘views’, path.join(__dirname, ‘views’))
app.use(express.static(path.join(__dirname, ‘public’)))
app.get(‘/’, function(req, res) {
    res.render(‘register’)
})
app.listen(8000)
在浏览器上输入localhost:8000看看是否会跳入注册页面。
下面我们来看一下注册页面的逻辑处理的代码吧,
首先在common目录内添加models.js文件用来保存各个集合的Schema文件(集合属性),也便于我们查看和访问,具体内容如下所示:
**javascript 代码**
module.exports = {
    user: {
        name: { type: String, required: true },
        password: { type: String, required: true },
        gender: { type: Boolean, default: true },
    },
}
还是common目录,我们在新建一个公共方法 —— dbHelper.js文件,来操作这些Schema,因为后面还会涉及此问题,所以我们写成一个公共的方法,dbHelper文件内容如下:
**javascript 代码**
var mongoose = require(‘mongoose’),
    Schema = mongoose.Schema,
    models = require(‘./models’)
for (var m in models) {
    mongoose.model(m, new Schema(models[m]))
}
module.exports = {
    getModel: function(type) {
        return _getModel(type)
    },
}
var _getModel = function(type) {
    return mongoose.model(type)
}
关于dbHelper.js文件里方法的访问很简单,如下所示:global.dbHelper = require( './common/dbHelper' );
然后我们就开始修改register视图页面,添加单击事件,例如:<input type="button"  onclick="register()" value="注 册" />
**javascript 代码**
function register() {
    //通过serialize()方法进行序列化表单值,创建文本字符串。
    var data = $(‘form’).serialize()
    //例如:”username=张三&password=12345″
    $.ajax({
        url: ‘/register’,
        type: ‘POST’,
        data: data,
        success: function(data, status) {
            if (status == ‘success’) {
                location.href = ‘register’
            }
        },
        error: function(res, err) {
            location.href = ‘register’
        },
    })
}
这里我们需要新建一个文件register.js,专门用来处理来之register页面的post请求, 在后面的学习中还会有多个不同处理文件,万所以我们统一管理在routes目录下,在实际开发中我们可能需要针对不同文件请求给出相应文件的处理,所以我们就做分开处理。
这里贴出register.js文件处理get和post请求的相关代码以供参考,如下:
**javascript 代码**
// app:express对象
module.exports = function(app) {
    app.get(‘/register’, function(req, res) {
        res.render(‘register’)
    })
    app.post(‘/register’, function(req, res) {
        var User = global.dbHelper.getModel(‘user’),
            uname = req.body.uname
        User.findOne({ name: uname }, function(error, doc) {
            if (doc) {
                req.session.error = ‘用户名已存在!’
                res.send(500)
            } else {
                User.create(
                    {
                        name: uname,
                        password: req.body.upwd,
                    },
                    function(error, doc) {
                        if (error) {
                            res.send(500)
                        } else {
                            req.session.error = ‘用户名创建成功!’
                            res.send(200)
                        }
                    },
                )
            }
        })
    })
}
register的post请求处理中,我们使用了session(express-session模块)还有处理post请求数据的body属性(body-parser和multer模块),需先安装他们,然后引用即可,如下参考:
**javascript 代码**
//引用模块
var bodyParser = require(‘body-parser’)
var multer = require(‘multer’)
var session = require(‘express-session’)
//调用中间件使用
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(multer())
后面我们还会再次添加多个路由记录,所以便于管理和访问,我们可以把他们统一放到一起,比如routes目录下新建index.js文件专门用来存放添加的文件,代码如下:
**javascript 代码**
module.exports = function(app) {
    require(‘./register’)(app)
}
那么我们在app.js文件中直接引用index.js文件就可以访问这些文件了,如下所示:
**javascript 代码**

require(‘./routes’)(app); //app:express对象。;

这里我们就一步到位,在register的post请求处理中我们使用了express-session模块来保存相关信息,这里我们就使用中间件来传递这些提示信息,中间件内容如下所示:
**javascript 代码**
app.use(function(req, res, next) {
    res.locals.user = req.session.user //保存用户信息
    var err = req.session.error //保存结果响应信息
    res.locals.message = ” // 保存html标签
    if (err)
        res.locals.message =
            ‘<div class=”alert alert-danger” style=”margin-bottom: 20px;color:red;”>’ +
            err +
            ‘</div>’
    next()
})
这里注意中间件的安放位置,还有我们设置了变量message并为其简单添加了样式,这里我们在register视图里就用它来作为操作结果的信息提示,直接添加<%- message %>到视图第一个div内即可。
关于注册我们基本已经准备就绪,开始打开连接数据库并设置用户过期时间(注意执行顺序,应放置在首个中间件位置),app.js条件内容如下:
**javascript 代码**
mongoose.connect(‘mongodb://127.0.0.1:27017/test’)
app.use(
    session({
        secret: ‘secret’,
        cookie: {
            maxAge: 1000 * 60 * 30,
        },
    }),
)

登录页面
我们还是在views目录下添加登录视图页面 —— login.html

这里我们还是添加一个相对应的文件用来处理login页面的请求,routes目录下新建名为login.js的文件,先来增加一个处理get请求的方法,代码参考如下:
javascript 代码

module.exports = function(app) {
    app.get(‘/login’, function(req, res) {
        res.render(‘login’)
    })
}

和register文件一样添加到index.js中,如下:require(‘./login’)(app);
我们为登陆按钮增加单击事件和对应函数login(),参考如下:
javascript 代码

function login() {
    //通过serialize()方法获取form表单内输入框用户名、密码的值
    //如:”username=张三&password=12345″
    var data = $(‘form’).serialize()
    $.ajax({
        url: ‘/login’,
        type: ‘POST’,
        data: data,
        success: function(data, status) {
            if (status == ‘success’) {
                location.href = ‘home’
            }
        },
        error: function(data, status) {
            if (status == ‘error’) {
                location.href = ‘login’
            }
        },
    })
}
关于login视图页的post请求处理,我们需要判断用户所输入用户名是否存在,密码是否正确,并使用变量保存相应提示信息,当用户名和密码全部正确时,则返回成功并保存用户的个人信息,用作来判断用户的登陆状态,具体可参考register视图页的post请求。
login.js文件的post请求处理代码,参考如下:
**javascript 代码**
app.post(‘/login’, function (req, res) {
    var User = global.dbHelper.getModel(‘user’),uname = req.body.uname;
    User.findOne({name: uname}, function (error, doc) {
             if (用户不存在) {
                 req.session.error = ‘用户名不存在!’;
                 res.send(404);
             } else if(用户存在,密码错误) {
                     req.session.error = “密码错误!”;
                     res.send(404);
                 }else{ //用户名、密码正确
                     req.session.user=doc;
                     res.send(200);
                 }
             });
     });
}

home页面
用户登录成功之后则跳转至home视图页面(商品主页),就可以进行对商品的浏览和选择了。
还是views目录,添加home商品视图页;

用户成功登录之后跳转至home页,这里我们还是做分开处理,routes目录下新建home.js文件用来处理来之home也的get请求。
这里我们假设如果用户未登录将不能查看商品主页,所以,在请求处理中我们还需要判断用户的登陆状态,这个可以使用我们在登录处理时所保存的用户个人信息。
关于商品页的视图展示我们只需要有其名称、价格、图片,这里使用ejs模板循环展示,可参考如下方式:
html 代码

<!DOCTYPE html>
<html>
<head>
    <title>主页</title>
    <meta charset=”utf-8″>
    <link href=”/bootstrap.min.css” rel=”stylesheet” media=”screen”>
    <script src=”/jquery-2.1.1.min.js” type=”text/javascript”></script>
    <script src=”/bootstrap.min.js” type=”text/javascript”></script>
    <style type=”text/css”>
        .panel-title {
            font-size: 24px;
            font-weight: bold;
        }
        .spys {
            list-style-type: none;
        }
        .spys li {
            float: left;
            margin: 10px 10px;
            width: 180px;
            height: 230px;
        }
        .spys li div strong {
            color: red;
        }
    </style>
</head>
<body>
    <div style=”margin:50px auto;width: 900px;”>
        <div class=”panel panel-default”>
            <div class=”panel-heading” style=”height: 40px;”>
                <div style=”text-align: left”>
                    <span style=”font-size:20px; font-weight:bold;”>商品展示页</span>
                    <div style=”float: right;”>
                        <a class=”btn btn-xs btn-success” href=”addcommodity” style=”margin-right: 35px;”>添加商品</a>
                        <a class=”btn btn-xs btn-success” href=”cart” style=”margin-right: 35px;”>购物车</a>
                        <a class=”btn btn-xs btn-info” href=”logout”>退 出</a>
                    </div>
                </div>
            </div>
            <div class=”panel-body”>
                <ul class=”spys”>
                    <%for(var i in Commoditys){ if(!Commoditys[i].name)continue;%>
                    <li class=”spys li”>
                        <div>
                            <img width=”80″ height=”100″ src=”/img/<%=Commoditys[i].imgSrc%>”>
                        </div>
                        <div>
                            <a>
                                <%=Commoditys[i].name%></a>
                            <strong style=”color:red;”>¥
                                <%=Commoditys[i].price%></strong>
                        </div>
                        <div>
                            <a class=”btn btn-success” style=”width: 120px;” href=”/addToCart/<%=Commoditys[i]._id%>”>加入购物车</a>
                        </div>
                    </li>
                    <%}%>
                </ul>
            </div>
        </div>
    </div>
</body>
</html>
在home的get请求处理中,我们需要首先判断用户的登陆状态,只有用户登录了方可跳转到商品页,如果为登陆呢则跳转到登录页,而且在进入商品页的时候并传入Commodity集合的所有数据数据在页面展示。
routes目录下添加home.js文件(index.js文件中引用)。
**javascript 代码**
module.exports = function(app) {
    app.get(‘/home’, function(req, res) {
        if (req.session.user) {
            var Commodity = global.dbHelper.getModel(‘commodity’)
            Commodity.find({}, function(error, docs) {
                //将Commoditys变量传入home模板
                res.render(‘home’, { Commoditys: docs })
            })
        } else {
            req.session.error = ‘请先登录’
            res.redirect(‘/login’)
        }
    })
}
添加商品,views目录下添加addcommodity视图页面用来对商品的添加,这里简单样式参考如下:
相对应的addcommodity函数参考代码如下:
**javascript 代码**
//imgSrc表示图片路径),这里内置了5张图片,格式为:xmsz-X.jpg(X为1-5数字)。
var data =
    $(‘form’).serialize() +
    ‘&imgSrc=’ +
    ‘xmsz-‘ +
    Math.floor(Math.random() * 5 + 1) +
    ‘.jpg’
$.ajax({
    url: ‘/addcommodity’,
    type: ‘POST’,
    data: data,
    success: function(data, status) {
        if (status == ‘success’) {
            alert(‘添加成功!’)
        }
    },
    error: function(data, err) {
        alert(‘添加失败!’)
    },
})
这里我们就直接在home.js文件中添加保存商品的处理方法;
javascript 代码
app.get(‘/addcommodity’, function(req, res) {
    res.render(‘addcommodity’)
})
app.post(‘/addcommodity’, function(req, res) {
    var Commodity = global.dbHelper.getModel(‘commodity’)
    Commodity.create(
        {
            name: req.body.name,
            price: req.body.price,
            imgSrc: req.body.imgSrc,
        },
        function(error, doc) {
            if (doc) {
                res.send(200)
            } else {
                res.send(404)
            }
        },
    )
})

结算页面
我们先定义购物车(cart)集合的Schema属性,包含:uId(用户ID)、cId(商品ID)、cName(商品名称)、cPrice(商品价格)、cImgSrc(商品展示图片路径)、cQuantity(商品数量)、cStatus(商品结算状态,默认为false),参考如下:
javascript 代码

cart:{
uId:{type:String},
cId:{type:String},
cName:{type:String},
cPrice:{type:String},
cImgSrc:{type:String},
cQuantity:{type:Number},
cStatus:{type:Boolean,default:false}
}
接着views目录下添加购物车(cart.html)视图页面,代码如下:
html 代码
<!DOCTYPE html>
<html>
<head>
    <title>购物车</title>
    <meta charset=”utf-8″>
    <link href=”/bootstrap.min.css” rel=”stylesheet” media=”screen”>
    <script src=”/jquery-2.1.1.min.js” type=”text/javascript”></script>
    <script src=”/bootstrap.min.js” type=”text/javascript”></script>
    <script type=”text/javascript”>
        $(function () {
            // 商品+-
            $(‘.li-quantity a’).click(function () {
                var self = $(this);
                var type = self.attr(‘data-type’),
                    num = parseFloat(self.siblings(‘input’).val());
                if (type == ‘add’) {
                    num += 1;
                } else if (type == ‘subtr’) {
                    if (num > 1) {
                        num -= 1;
                    } else {
                        return false;
                    }
                }
                self.siblings(‘input’).val(num);
                tamount();
            });
            //checkbox 单选事件
            $(‘input[name=”chkItem”]:checkbox’).click(function () {
                var isCheck = $(‘input[name=”chkItem”]:not(:checked)’).length ? false : true;
                $(‘#CheckAll’).prop(“checked”, isCheck);
                tamount();
            });
            //checkbox 全选事件
            $(‘#CheckAll’).click(function () {
                var self = $(this);
                $(‘input[name=”chkItem”]’).each(function () {
                    $(this).prop(“checked”, self.is(‘:checked’));
                });
                tamount();
            });
        });
        var sum = 0;
        //用户结算
        function Clearing() {
            $(‘input[name=”chkItem”]:checked’).each(function () {
                var self = $(this),
                    index = self.attr(‘data-index’),
                    cid = self.attr(‘data-id’);
                var quantity = $(‘#Q’ + index).val();
                var data = { “cid”: cid, “cnum”: quantity };
                $.ajax({
                    url: ‘/cart/clearing’,
                    type: ‘post’,
                    data: data,
                    success: function (data, status) {
                    },
                    error: function (data, status) {
                    }
                });
            });
            alert(‘¥’ + sum);
            location.href = “cart”;
        }
        //计算商品总价格
        function tamount() {
            sum = 0;
            $(‘input[name=”chkItem”]:checked’).each(function () {
                var self = $(this),
                    price = self.attr(‘data-price’),
                    index = self.attr(‘data-index’);
                var quantity = $(‘#Q’ + index).val();
                sum += (parseFloat(price) * parseFloat(quantity));
            });
            $(“#money”).html(‘¥’ + sum + ‘.00’);
        }
    </script>
    <style type=”text/css”>
        .cart-heading {
            height: 40px;
            background-color: #EFEDED;
        }
        .cart-body {
            background-color: #F7F7F7;
        }
        .cart-body ul li {
            list-style-type: none;
            margin-left: -30px;
            width: 870px;
        }
        .cart-body ul li div {
            float: left;
            height: 80px;
        }
        .li-checkbox input {
            margin: 20px 5px 0 0;
        }
        .li-img a img {
            width: 40px;
            height: 50px;
        }
        .li-content {
            margin: 20px 0 0 30px;
            width: 280px;
        }
        .li-price {
            margin: 20px 0 0 60px;
            width: 100px;
        }
        .li-quantity {
            margin: 20px 0 0 130px;
            width: 100px;
        }
        .li-del {
            margin: 20px 0 0 30px;
            width: 50px;
        }
        .li-img {
            margin: 0 0 0 10px;
        }
    </style>
</head>
<body>
    <div style=”margin:50px auto;width: 900px;”>
        <div>
            <div>
                <div style=”float:right;”>
                    <a class=”btn btn-xs btn-success” href=”home” style=”margin-right: 35px;”>商品页</a>
                    <a class=”btn btn-xs btn-info” href=”logout”>退 出</a>
                </div>
                <h2>购物车</h2>
                <hr>
            </div>
            <div>
                <div class=”cart-heading”>
                    <div style=”padding: 10px 0 0 10px”>
                        <span style=”margin-right: 200px;”>
                            <input id=”CheckAll” type=”checkbox”> 全选
                        </span>
                        <span style=”margin-right: 180px;”>商品</span>
                        <span style=”margin-right: 210px;”>价格</span>
                        <span style=”margin-right: 80px;”>数量</span>
                        <span style=”padding-right: 0px;”>操作</span>
                    </div>
                </div>
                <div class=”cart-body”>
                    <ul>
                        <%for(var i in carts){ if(!carts[i].cId)continue%>
                        <li>
                            <div class=”li-checkbox”>
                                <input data-id=”<%=carts[i]._id%>” data-index=”<%=i%>” data-price=”<%=carts[i].cPrice%>” name=”chkItem” class=”li-checkbox input”
                                 type=”checkbox” />
                            </div>
                            <div class=”li-img”>
                                <a>
                                    <img class=”li-img a img” src=”/img/<%=carts[i].cImgSrc%>”>
                                </a>
                            </div>
                            <div class=”li-content”>
                                <a>
                                    <%=carts[i].cName%></a>
                            </div>
                            <div class=”li-price”>
                                <span>
                                    <%=carts[i].cPrice%></span>
                            </div>
                            <div class=”li-quantity”>
                                <a data-type=”add” href=”javascript:void(0);” class=”btn btn-default btn-xs “>+</a>
                                <input id=”Q<%=i%>” style=”width: 40px;” type=”text” value=”<%=carts[i].cQuantity%>”>
                                <a data-type=’subtr’ href=”javascript:void(0);” class=”btn btn-default btn-xs”>-</a>
                            </div>
                            <div class=”li-del”>
                                <a href=”/delFromCart/<%=carts[i]._id%>” class=”btn btn-primary btn-xs”>删除</a>
                            </div>
                        </li>
                        <%}%>
                    </ul>
                </div>
                <div style=”float: right;height: 35px;width:330px;”>
                    总计:<span id=”money” style=”color: red;font-size: 25px”>¥0.00</span>
                    <input type=”button” style=”width: 130px;float:right;” class=”btn btn-success” onclick=” Clearing();” value=”结 算” />
                </div>
            </div>
        </div>
    </div>
</body>
</html>

javascript 代码

module.exports = function(app) {
    //查看购物车商品
    app.get(‘/cart’, function(req, res) {
        var Cart = global.dbHelper.getModel(‘cart’)
        if (!req.session.user) {
            req.session.error = ‘用户已过期,请重新登录:’
            res.redirect(‘/login’)
        } else {
            Cart.find({ uId: req.session.user._id, cStatus: false }, function(
                error,
                docs,
            ) {
                res.render(‘cart’, { carts: docs })
            })
        }
    })
    //添加购物车商品
    app.get(‘/addToCart/:id’, function(req, res) {
        //req.params.id 获取商品ID号
        if (!req.session.user) {
            req.session.error = ‘用户已过期,请重新登录:’
            res.redirect(‘/login’)
        } else {
            var Commodity = global.dbHelper.getModel(‘commodity’),
                Cart = global.dbHelper.getModel(‘cart’)
            Cart.findOne({ uId: req.session.user._id, cId: req.params.id }, function(
                error,
                doc,
            ) {
                //商品已存在 +1
                if (doc) {
                    Cart.update(
                        { uId: req.session.user._id, cId: req.params.id },
                        { $set: { cQuantity: doc.cQuantity + 1 } },
                        function(error, doc) {
                            //成功返回1 失败返回0
                            if (doc > 0) {
                                res.redirect(‘/home’)
                            }
                        },
                    )
                    //商品未存在,添加
                } else {
                    Commodity.findOne({ _id: req.params.id }, function(error, doc) {
                        if (doc) {
                            Cart.create(
                                {
                                    uId: req.session.user._id,
                                    cId: req.params.id,
                                    cName: doc.name,
                                    cPrice: doc.price,
                                    cImgSrc: doc.imgSrc,
                                    cQuantity: 1,
                                },
                                function(error, doc) {
                                    if (doc) {
                                        res.redirect(‘/home’)
                                    }
                                },
                            )
                        } else {
                        }
                    })
                }
            })
        }
    })
    //删除购物车商品
    app.get(‘/delFromCart/:id’, function(req, res) {
        //req.params.id 获取商品ID号
        var Cart = global.dbHelper.getModel(‘cart’)
        Cart.remove({ _id: req.params.id }, function(error, doc) {
            //成功返回1 失败返回0
            if (doc > 0) {
                res.redirect(‘/cart’)
            }
        })
    })
    //购物车结算
    app.post(‘/cart/clearing’, function(req, res) {
        var Cart = global.dbHelper.getModel(‘cart’)
        Cart.update(
            { _id: req.body.cid },
            { $set: { cQuantity: req.body.cnum, cStatus: true } },
            function(error, doc) {
                //更新成功返回1 失败返回0
                if (doc > 0) {
                    res.send(200)
                }
            },
        )
    })
}

前端开发做项目的经历 前端开发从入门到精通 前端项目开发流程

» 本文来自:前端开发者 » 《前端nodejs+express+mongodb的一个简单案例》
» 本文链接地址:https://www.rokub.com/5721.html
» 您也可以订阅本站:https://www.rokub.com
赞(0)
64K

评论 抢沙发

评论前必须登录!