node+mongodb 建站攻略(一期)
本文档根据慕课网视频学习所总结
主题:
1.1.png开发框架介绍
1.2.png开发框架介绍对应上图
1.3.png项目开发流程
1.4.png原型图-首页、详情页
1.5.png后台录入页
1.6.png第二章:项目前后端流程打通
2.1、Node入口文件分析和目录初始化
2.1.png2.2.png
2.3.png
2.4.png
2.2、创建四个jade视图及入口文件中处理
- 创建一个项目目录imooc
- 根目录下创建app.js与views目录
终端打开项目并输入命令:
npm init
npm install express jade moment mongoose
- 新建app.js,编写入口文件:
#app.js
var express =require('express')//加载express这个模块
var port = process.env.PORT || 3000//设置端口:process是全局变量
var app = express()//启动一个web服务器
app.set('views','./views')//设置视图的根目录
app.set('view engine','jade')//设置默认的模板引擎
app.listen(port)//监听端口
console.log('imooc started on port ' + port)//打印一条日志,控制台查看服务是否能成功启动
//添加路由
//index page
app.get('/index',function(req,res){//浏览器中访问页面是以get方法提交提交的,参数一位匹配规则,参数二为回调方法
res.render('index',{
title:'imooc 首页'
})
})
//detail page
app.get('/movie/:id',function(req,res){
res.render('detail',{
title:'detail 详情页'
})
})
//list page
app.get('/list',function(req,res){
res.render('list',{
title:'imooc 列表页'
})
})
//admin page
app.get('/admin/movie',function(req,res){
res.render('admin',{
title:'imooc 后台录入页'
})
})
项目结构为:
2.2.1.png
doctype
html
head
meta(chaset="utf-8")
title #{title}
body
h1 #{title}
#将以上代码直接copy在四个jade文件中且注意变换格式
#@将该项目在终端下打开并输入 node app.js,检测是否正确启动(也可以PORT=40000 node app.js 启动)
#在浏览器的输入地址:localhost:3000/index,localhost:3000/list,localhost:3000/admin/movie,localhost:3000/admin/01查看是否正确启动。
2.3、伪造模板数据跑通前后端交互流程
2.3.1.png修改目录结构-运用jade语法可以继承的优点以及公共区块的调用
在views中新建includes文件夹以及pages文件夹以及layout.jade布局文件,将原先views中的四个文件移动至pages文件中,在includes文件中新建head.jade文件与header.jade文件
layout.jade(copy代码时请将注释去掉,有可能报错)
doctype
html
head
meta(chaset="utf-8")
title #{title}
include ./includes/head //- 将公用样式抽象成一个区块
body
include ./includes/header //-网站标题或者logo
block content //-每个页面中的内容
在编写head.jade以及header.jade文件前,我们首先安装bower,然后通过bower安装bootstrap
npm install bower -g
bower install bootstrap@3.3.7
//如果不加@3.3.7
会安装最新的,导致页面会出现一些显示问题。
head.jade (写入引入的公共样式)
link(href="/bootstrap/dist/css/bootstrap.min.css", rel="stylesheet")
script(src="/jquery/dist/jquery.min.js")
script(src="/bootstrap/dist/js/bootstrap.min.js")
header.jade
.container
.row
.page-header
h1 #{title}
h3 六个周
改变pages中四个文件内容为:
extends ../layout
2.3.2.png安装成功后的项目结构是这样的:
此时,我们再次终端输入命令
node app
查看是否可以正确访问注意app.js中访问文件的路径修改
如果依然访问成功,我们继续下一步操作。
我们开始进行index.jade
(首页)的文件编码:。
extends ../layout
block content
.container
.row
each item in movies
.col-md-2
.thumbnai
a(href="/movie/#{item._id}")
img(src="#{item/poster}",alt="#{item.title}")
.caption
h3 #{title}
p: a.btn.btn-primary(href="/movie/#{item._id}",role="button")
观看预告片
detail.jade
(详情页)文件:
extends ../layout
block content
.container
.row
.col-md-7
img(src="#{movie.poster}",alt="#{movie.title}",height="720")
.col-md-5
dl.horizontal
dt 电影名字
dd= movie.title
dt 导演
dd= movie.doctor
dt 国家
dd= movie.country
dt 语言
dd = movie.language
dt 上映年份
dd= movie.year
dt 简介
dd = movie.summary
admin.jade
(后台录入页)文件:
extends ../layout
block content
.container
.row
form.form-horizontal(method="post",action="/admin/movie/new")
.form-group
label.col-sm-2.control-label(for="inputTitle") 电影名字
.col-sm-10
input#inputTitle.form-control(type="text",name="movie[title]",value="#{movie.title}")
.form-group
label.col-sm-2.control-label(for="inputDoctor") 电影导演
.col-sm-10
input#inputTitle.form-control(type="text",name="movie[doctor]",value="#{movie.doctor}")
.form-group
label.col-sm-2.control-label(for="inputCountry") 国家
.col-sm-10
input#inputTitle.form-control(type="text",name="movie[country]",value="#{movie.country}")
.form-group
label.col-sm-2.control-label(for="inputLanguage") 语种
.col-sm-10
input#inputTitle.form-control(type="text",name="movie[language]",value="#{movie.language}")
.form-group
label.col-sm-2.control-label(for="inputPoster") 海报地址
.col-sm-10
input#inputTitle.form-control(type="text",name="movie[poster]",value="#{movie.poster}")
.form-group
label.col-sm-2.control-label(for="inputFlash") 片源地址
.col-sm-10
input#inputTitle.form-control(type="text",name="movie[flash]",value="#{movie.flash}")
.form-group
label.col-sm-2.control-label(for="inputSummary") 电影简介
.col-sm-10
textarea#inputSummary.form-control(type="text", name="movie[summary]", value="#{movie.summary}")
.form-group
.col-sm-offset-2.col-sm-10
button.btn.btn-default(type="submit") 录入
list.jade
(列表页)文件:
extends ../layout
block content
.container
.row
table.table.table-hover.table-bordered
thead
tr
th 电影名字
th 导演
th 国家
th 上映年份
//-th 录入时间
th 查看
th 更新
th 删除
tbody
each item in movies
tr(class="item-id-#{item._id}")
td #{item.title}
td #{item.doctor}
td #{item.country}
td #{item.year}
//-td #{moment(item.meta.createAt).format('MM/DD/YYYY')}
td :a(target="_blank",href="../movie/#{item._id}") 查看
td :a(target="_blank",href="../admin/update/#{item._id}") 修改
td
button.btn.btn-danger.del(type="button",data_id="#{item._id}") 删除
伪造的静态数据:app.js
var express = require('express')//加载express这个模块
var app = express()
var path =require('path')
var port = process.env.PORT || 3000//设置端口:process是全局变量
app.set('views','./views/pages') //设置视图的根目录
app.set('view engine','jade')//设置默认的模板引擎
var bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(express.static(path.join(__dirname,'bower_components')))
app.listen(port)//监听端口
console.log('六个周 started on port ' + port)//打印一条日志,控制台查看服务是否能成功启动
//添加路由
//index page
app.get('/index',function(req,res){//浏览器中访问页面是以get方法提交提交的,参数一位匹配 规则,参数二为回调方法
res.render('index',{
title:'六个周 首页',
movies:[{
title:'Node.js指南',
_id:1,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:2,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:3,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:4,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:5,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:6,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:7,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:8,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:9,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},{
title:'Node.js指南',
_id:10,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg'
},]
})
})
//detail page
app.get('/movie/:id',function(req,res){
res.render('detail',{
title:'detail 详情页',
movie:{
doctor:'六个周',
counter:'China',
title:'人生缘编程',
year:2019,
poster:'http://img4.imgtn.bdimg.com/it/u=1178625225,3686148785&fm=26&gp=0.jpg',
language:'汉语',
flash:'https://www.六个周.com/video/1226',
summary:'1.,生命太短暂,不要去做一些根本没有人想要的东西。——Ash Maurya。2,如果你交给某人一个程序,你将折磨他一整天;如果你教某人如何编写程序,你将折磨他一辈子。——David Leinweber。3,软件设计有两种方式:一种方式是,使软件过于简单,明显没有缺陷;另一种方式是,使软件过于复杂,没有明显的缺陷。——C.A.R. Hoare。4,软件开发往往是这样:最开始的 90% 代码占用了开始的 90% 的开发时间;剩下 10% 代码同样需要 90% 的开发时间。——Tom Cargill'
}
})
})
//admin page
app.get('/admin/movie',function(req,res){
res.render('admin',{
title:'六个周 后台录入页',
movie:{
title:'',
doctor:'',
country:'',
year:'',
poster:'',
flash:'',
summary:'',
language:''
}
})
})
//list page
app.get('/admin/list',function(req,res){
res.render('list',{
title:'六个周 列表页',
movies:[
{
title:'Node.js指南',
_id:1,
doctor:'六个周',
country:'China',
year:2019,
language:'汉语',
flash:'https://www.jianshu.com/u/5842abb77bd1',
summary:'1.,生命太短暂,不要去做一些根本没有人想要的东西。——Ash Maurya。2,如果你交给某人一个程序,你将折磨他一整天;如果你教某人如何编写程序,你将折磨他一辈子。——David Leinweber。3,软件设计有两种方式:一种方式是,使软件过于简单,明显没有缺陷;另一种方式是,使软件过于复杂,没有明显的缺陷。——C.A.R. Hoare。4,软件开发往往是这样:最开始的 90% 代码占用了开始的 90% 的开发时间;剩下 10% 代码同样需要 90% 的开发时间。——Tom Cargill'
}
]
})
})
然后,容易出错的地方是bootstrap版本的安装导致的页面样式以及jade的代码缩进,经过一番调整后,启动'node app
,效果图如下:
然后,至此先将代码提交至git于码云。
码云地址:https://gitee.com/ShaoBingYouTiao/movies
第三章:项目数据库实现
3.1、mongodb模式类型设计及编码以及数据库交互代码
3.1.1.png3.1.2.png 3.1.3.png 3.1.4.png 3.1.5.png
3.1.6.png 3.1.7.png 3.1.8.png
在项目根目录下新建文件夹schemas(模式声明)、models(模型)、controllers。
分别在schemas中和models中添加movie.js文件,代码如下:
schemas-movie.js
var mongoose =require('mongoose')
var MovieSchema =new mongoose.Schema({
doctor:String,
title:String,
language:String,
country:String,
summary:String,
flash:String,
poster:String,
year:Number,
meta:{
createaAt:{
type:Date,
default:Date.now()
},
updataAt:{
type:Date,
default:Date.now()
}
}
})
MovieSchema.pre('save',function(next){
if(this.isNew){
this.meta.createaAt = this.meta.updataAt =Date.now()
}else{
this.meta.updataAt =Date.now()
}
next()
})
MovieSchema.statics = {
fetch:function(cb){
return this
.find({})
.sort('meta.updateAt')
.exec(cb)
},
fingdById:function(id,cb){
return this
.findOne({_id:id})
.exec(cb)
}
}
module.exports =MovieSchema
models-movie.js
var mongoose =require('mongoose')
var MovieSchema = require('../schemas/movie')
var Movie = mongoose.model('Movie',MovieSchema)
module.exports = Movie
模式和模型代码编写结束,进入下一章节。
3.2、编写数据库交互代码
进入app.js文件,添加代码
需要在终端中安装underscore模块:npm install underscore
var mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/imooc')
var Movie =require('./models/movie')
var _ =require('underscore')
#将app.js中的所有静态数据注释掉
index路由与list路由中添加 Movie.fetch()方法
detail路由中添加 Movie.findById()方法
admin路由中在路由外添加一个方法:app.post()
// admin post movie
app.post('/admin/movie/new',function(req,res){
var id =req.body.movie._id
var movieObj =req.body.movie
var _movie
if(id!== 'undefined'){
Movie.findById(id,function(err,mmovie){
if(err){
console.log(err)
}
_movie =_.extend(movie,movieObj)
_movie.save(function(err,movie){
if(err){
console.log(err)
}
res.redirect('./movie/'+movie.id)
})
})
}else{
_movie = new Movie({
doctor:movieObj.doctor,
title:movieObj.title,
country:movieObj.country,
language:movieObj.language,
year:movieObj.year,
poster:movieObj.poster,
summary:movieObj.summary,
flash:movieObj.flash
})
_movie.save(function(err,movie){
if (err) {
console.log(err)
}
res.redirect('/movie/'+movie._id)
})
}
})
还差一个在列表页点击更新的时候重新回到后台录入页,将表单中数据重新初始化到表单中,于是添加一个路由
//admin update
app.get('/admin/update/:id',function(req,res){
var id =req.params.id
if(id){
Movie.findById(id,function(err,movie){
res.render('admin',{
title:'六个周 后台更新页',
movie: movie
})
})
}
})
最后还需要在admin录入页(admin.jade)添加一个隐藏的表单域,存储电影ID,用于更新
input(type="hidden",name="movie[_id]",value="#{movie._id}")
然后我们开始进行测试:
- 打开终端输入
mongod
node app
后台电影录入页:http://localhost:3000/admin/movie
点击录入后跳转至添加项的详情页:
detail.png
电影首页:
localhost:3000/index
index
后台列表页:
localhost:3000/admin/list
list.png
4.1删除功能及项目生成配置文件
删除功能这里我们需要使用jQuery,通过异步请求进行删除。
在此之前,我们需要改变项目结构,将bower_components
中内容放在新建的一个public文件中。--这里我们需要用的bower的一个特性(可以指定将bower下载的东西放在哪个目录下)。
- 项目根目录下新建public文件夹
- 项目根目录下新建 .bowerrc文件,在本文件下写入如下代码:
{
"directory": "public/libs"
}- 删除bower_components文件夹
- 终端中重新安装bootstrap:
bower install bootstrap@3.3.7
- 同时修改head.jade的bootstrap引入路径,添加/libs
- 最好修改app.js中的bower_components改为public。至此,可以再次输入各个路径进行测试 。
测试成功没有问题,我们继续添加删除的功能,在public文件下新建文件js,js中新建admin.js,编写代码:
$(function(){
$(".del").click(function(e){
var target = $(e.target)
var id =target.data('id')
var tr =$('.item-id-'+id)
$.ajax({
type:"DELETE",
url:'/admin/list?id='+id
})
.done(function(results){
if(results.success ===1){
if(tr.length>0){
tr.remove()
}
}
})
})
})
在list.jade中添加代码
script(src="/js/admin.js")
在app.js中添加删除路由:
//list delete movie
app.delete('/admin/list',function(req,res){
var id= req.query.id
if(id){
Movie.remove({_id: id},function(err,movie){
if(err){
console.log(err)
}else{
res.send({success:1})
}
})
}
})
然后将修改的代码文件上传至码云:https://gitee.com/ShaoBingYouTiao/movies
总结:至此建站攻略一期完毕,本项目对于很多不熟悉jade的同学来说,可能会对写jade代码处有所抵触,但是如果你按着我上面的思路一点一点写出来的话还是很有收获的。本项目中的代码给出很可能~很可能的存在一两个单词的拼写错误导致你在运行项目的时候出现问题,这个时候你要细细查看原因,我已经尽可能的将正确代码贴上,如有问题,欢迎留言,我会在看到时及时回复。
关于本项目的升级、二期学习与文档整理我会尽快更新,先将地址贴上:
二期学习视频慕课网地址:https://www.imooc.com/learn/197
二期学习简书文档整理地址:https://www.jianshu.com/p/a9192427597a