(填坑中,预计7月底完成)
最后更新时间7/28晚,
更新页面基本样式已有
范例网址:121.41.66.68
【0】涉及到的框架、引擎、数据库:
① express 4.X
② jade
③ mysql 5.6.x
注:
①内容较长,我会尽力把整个框架、结构、顺序、思考方式说明白。
②基础是《node.js开发指南》这本书,作者:BYVoid。但他书的版本较老,很多东西现在已经无法应用,故进行更新,使用目前普遍的express 4.x版本(原书似乎是2.X版本),mysql(原书是mongodb),jade(原书是ejs)
【1】基本需求:
①有首页;
②支持注册;
③支持登录、登出;
④可以发表博客,发表的博客可以保存到数据库;
⑤可以查看博客,博客从数据库中读取,为了简化,这里设置为查看所有人的博客;
⑥查看博客时,初始查看的数量有限,但可以无限加载新的博客,直到查看完全部博客;
⑦一定程度上实现多语言(即可以切换显示的语言版本),但由于复杂度所限,因此只部分实现(但框架已建立好,可以通过继续完善代码实现整体的国际化);
⑧根据登录状态,对可以访问的页面进行控制(某些允许某些禁止)
【2】前后端交互的简单过程:
【3】关于客户端请求的几种情况(涉及到数据库的)
【4】npm
npm是包管理器,在新版本里是默认安装好的,可以输入:
npm -v
来查看npm的版本
【5】express框架:
首先安装基础的express框架,他是封装好的web开发框架,里面包含了:
①路由控制、
②log显示、
③解析客户端请求、
④cookies解析、
⑤静态文件的控制
等多种功能。
安装前注:
①有的人只需要简单的
npm install -g express
npm install -g express-generator
就可以愉快的跑起express了,有的人就像向我一样苦逼,尝试各种办法,最后勉强可以用。
如果在这两行命令后,输入(V是大写的)
express -V
会返回版本号,那么直接跳到最后来看,如果不是这样,可以参考我写的记录来安装。
或者直接看后面的终极解决方案
②express设置全局方法:
ln -s /usr/nodejs4.4.7/node-v4.4.7-linux-x64/bin/express /usr/local/bin/express
其他需要全局的方法,理论上同理,即将nodejs安装目录下的bin文件夹下的模块,映射到/usr/local/bin/同名文件即可
③express命令可以使用的人:
输入:
express -t jade myblog
效果是建立一个文件夹名为myblog的文件夹,里面会有一些文件。
正常如下图:
cd myblog
npm install
这时,npm会根据package.json来安装一些东西。
按提示输入:
SET DEBUG=myblog:*
npm start
可以启动项目。
本机的话,通过访问http://127.0.0.1:3000/ 来查看效果
服务器的话,访问其公网ip,端口是3000
查看package.json
"scripts": { "start": "node ./bin/www" },
这条属性告诉我们,需要通过bin文件夹下的www来启动,这也就是上面npm start命令的作用。
www文件应该是设置自启动的,然而我这里并不需要。但若直接启动app.js是启动不了的,因为在www文件里面,设置了端口是3000,他是通过www文件来启动监听的。
解决办法:
在app.js里面,在最后一行代码之前,添加:
app.listen(80);
于是,便可以通过app.js来启动服务器了。
访问效果如图:
假如如果像我一样倒霉,无法用express命令,打开npm也特别慢,可以先找个系统,将这些文件下载好,然后将这些文件复制到不可以用express命令的linux系统下面。
或者看最后的终极解决方案
ps:
如果npm很慢的话,可以考虑装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
然后在npm install这一步,使用cnpm install来替代
————————分割线————————
④超级终极解决方案:
我传一个装好express的压缩包,直接解压缩后就可以用。
链接:
http://download.csdn.net/detail/qq20004604/9587054
⑤另一个启动方法
即不通过app.js来启动;
在之前④的基础上,打开app.js,删除
app.listen(80);
打开package.json,将
"start":"node ./app.js"
改回
start":"node ./bin/www"
然后
cd bin
vi www
将
var port =normalizePort(process.env.PORT||'3000');
修改为
var port =normalizePort(process.env.PORT||'80');
然后
cd ..
npm start
启动成功,可以直接访问地址来访问页面
【6】把启动的app.js放在后台运行,并且在操作端解除控制后依然可以运行
原本使用npm start的地方,输入
nohup npm start&
即可
【7】app.js之解释
app.js可以说是一切的根本,因此请看注释,解释了每行代码的作用:
var express = require('express'); //这个模块在node_modules文件夹中 var path = require('path'); //nodejs自带的,路径相关 var favicon = require('serve-favicon'); //node_modules文件夹中 var logger = require('morgan'); //日志相关,node_modules文件夹中,具体看第19行代码 var cookieParser = require('cookie-parser');//node_modules文件夹中,用来解析cookie的,被express所调用 var bodyParser = require('body-parser'); //用于解析请求体的(应该),被express所调用 var routes = require('./routes/index'); //这里是默认的路由,路径是routes/index.js var users = require('./routes/users'); //这里是默认的路由,路径是routes/users.js //关于路由的更多内容看下面的app.use('/', routes);和app.use('/users', users);的注释 var app = express(); //生成一个express的实例 //设置模板引擎 app.set('views', path.join(__dirname, 'views')); //__dirname表示绝对路径 //console.log(__dirname) //可以取消这一行注释,然后看看__dirname的效果 app.set('view engine', 'jade'); //表示express是使用jade格式的模板的 //下面这行代码是设置左上角的小图标的,将其命名为favicon.ico并放在public文件夹下,如果有的话,取消这行注释 //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); //设置日志显示,如果不需要的话可以注释掉这行代码,建议练手的时候不要注释掉 //可替换参数有default,combined,common,short,tiny,dev(相对tiny有染色)。可以自己试试效果,默认是dev app.use(bodyParser.json()); //解析客户端的请求,通常是通过post发送的内容 app.use(bodyParser.urlencoded({extended: false})); //另一种解析方式,具体不太明白(如果上面解析失败的话会通过这个来) app.use(cookieParser()); //cookies的解析 app.use(express.static(path.join(__dirname, 'public'))); //普通静态html文件、js文件、css文件,都放在public文件夹下,可以直接通过url来访问 //路由的处理 app.use('/', routes); //假如访问的网址是根目录,例如http://121.41.66.68/,交给routes这个js文件来处理,具体请查看routes app.use('/users', users); //假如访问的是/users这样的路径,那么交给users这个js文件来处理,具体略 //我们最后要对这个路由进行处理,让他按照我们想要的方式来做 //这里对非法路径进行处理的,next表示这是一个中间件(即执行完他之后,还会执行下一个,而不是直接在这里结束了) //如果上面没有静态文件(29行)、没有找到被路由处理的文件(32,33行),就会交给这个来处理。 app.use(function (req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); //由下面的2个app.use中的一个来处理(一般是第一个,除非第一个被注释) }); //原注释说是会对错误进行加亮处理 //这部分和下面的区别在于,这部分会将错误信息暴露给用户,而下面的不会,因此注释掉这部分 //if (app.get('env') === 'development') { // app.use(function (err, req, res, next) { // res.status(err.status || 500); // res.render('error', { // message: err.message, // error: err // }); // }); //} // production error handler // no stacktraces leaked to user app.use(function (err, req, res, next) { res.status(err.status || 500); res.render('error', { //这里的error指调用views下的error模板 message: err.message, error: {} }); }); //这是我自行添加的,用于显示服务器启动的时间 console.log("Server start at :" + new Date()); //导出app,在./bin/www里会被调用 module.exports = app;
【8】页面关系结构
请忽略文件名的大小写问题(手动微笑)
因此,首先有三个需要我们自定义的路由:
①当访问根/时,输出index;
②当访问/login时,输出login
③当访问/reg时,输出reg
其他可能的路由:
④后续可能添加的页面,暂缺;
⑤不符合要求的页面,输出404(express已经完成)
【9】index页面需要包含的功能:
【10】index的路由:
查看app.js中的代码:
app.use('/', routes);
这部分代码已经说明了,当访问根的时候,交给routes来处理;
再次查看导入routes的地方:
var routes = require('./routes/index');
说明负责这部分的文件是routes文件夹的index文件
这时打开index.js,
router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); });
这部分代码,表示当访问根的 ’/’ 页面时,由index这个引擎模板来处理,模板中的title变量,其值为Express。
————————————————————————
代码说明:
router.get('/'
以上这段代码是在路由基础上进行二段捕获url的,举例:
①假如在app.js里,
app.use('/',
然后在路由的处理里,是
outer.get('/'
那么最终针对的url是/
②假如在app.js里,
app.use('/test',
然后在路由的处理里,是
outer.get('/'
那么最终针对的url是/test
③假如在app.js里,
app.use('/,
然后在路由的处理里,是
outer.get('/test'
那么最终针对的url依然是/test
④假如在app.js里,
app.use('/testA,
然后在路由的处理里,是
outer.get('/testB'
那么最终针对的url是/testA/testB
⑤捕获优先度说明:
首先根据app.js中的app.use出现顺序决定,假如②和③同时出现,那么看②和③在app.js中的代码谁先出现,就交给谁来处理;
再不调用next()方法的情况下,只会被最先出现的那个处理;
如果第一个调用了next()方法,那么第一个处理完后会执行第二个的
————————————————————————
index.js代码说明:
var express = require('express'); //调用express var router = express.Router(); //生成express的Router方法的一个实例 //处理函数 router.get('/', function (req, res, next) { //捕获根url res.render('index', {title: 'Express'}); //res.render方法渲染一个引擎模板, //第二个参数是一个对象,对象里的变量可以在引擎中使用, //第三个参数是回调函数,提供两个参数,分别是err和html,err是错误,html是渲染后的页面。如果使用这个回调函数,那么将不会自动响应,即要用户自己写返回html的命令 }); module.exports = router;
注意,被引擎渲染的文件,默认是在myblog/views文件夹下
【11】使用Bootstrap界面
让我们自己设计界面不是不可以,不过太麻烦,因此我和原书保持一致,使用Twitter Bootstrap风格的页面。
下载他的压缩包文件,并解压缩他,
将css文件放在项目myblog/public/stylesheets下;
将img文件夹直接复制粘贴在myblog/public下;
将js文件放在myblog/public/javascripts下,
我的Bootstrap的版本是v2.3.2
另外,下载jquery,命名为jq.js,放在myblog/public/javascripts下
【12】index.jade说明
下来我们就要修改jade文件了,如果不知道怎么使用的话,可以看我的博客:
http://blog.csdn.net/qq20004604/article/details/51773574
extends layout block content h1= title p Welcome to #{title}
第一行的extends layout表示他导入的layout模板(即layout.jade)
block content表示以下这部分将套入layout的content位置
内容略。
这时候再过去看layout.jade
doctype html html head title= title link(rel='stylesheet', href='/stylesheets/style.css') body block content
他导入的是style.css这个样式表,页面的title是变量title
上面的index.jade中的内容将导入这里的block content区域(因为变量名一样,会对应替换)。
其结构大概如下:
然后我们根据自己实际需求来改造:
直接给出代码:
首先在stylesheets文件夹下创建一个blog.css文件,内里样式为:
body { padding-top: 60px; padding-botom: 40px; } #textarea { resize: none; width: 300px; height: 100px; cursor: text; } #postBlog { position: relative; left: 20px; vertical-align: top; } #clearBlog { position: relative; left: -90px; top: 27px; width: 110px; height: 44px; } .myalert { position: absolute; } .displayNONE { display: none; } #scrollToFoot { border: 1px solid #ccc; text-align: center; font-size: 18px; padding: 20px 0; } fotter p { margin-top: 40px; padding-top: 20px; border-top: 1px solid #aaa; font-size: 18px; } .row { color: #555; }
其次是layout.jade
doctype html html head title MyBlog By qq20004604 link(rel='stylesheet', href='./stylesheets/bootstrap.min.css') link(rel='stylesheet', href='./stylesheets/bootstrap-responsive.min.css') link(rel='stylesheet', href='./stylesheets/blog.css') script(type="text/javascript",src='javascripts/jq.js') script(type="text/javascript",src='javascripts/bootstrap.min.js') script(type="text/javascript",src='javascripts/blog.js') body div.navbar.navbar-fixed-top div.navbar-inner div.container a.btn.btn-navbar(data-toggle="collapse",data-target=".nav-collapse") span.icon-bar span.icon-bar span.icon-bar a.brand(href="/") MyBlog div.nav-collapse ul.nav li a(href="/") 首页 li a(href="/logout") 登出 li a(href="/login") 登入 li a(href="/reg") 注册 li a(href="/language") 切换语言 div#contrainer.container block content hr fotter p 作者:王冬 QQ:20004604
效果如图:
然后是index.jade:
extends layout block content div.hero-unit h1 我的博客 p 这个是基于Nodejs作为后台,jade作为模板来,使用了Express作为框架 br br //这部分暂时用1替代,后续会被更新 if(1) br br a.btn.btn-primary.btn-large(href="/login") 登录 a.btn.btn-large(href="/reg") 立即注册 else textarea#textarea.uneditable-input button#postBlog.btn.btn-large 提交微博 button#clearBlog.btn.btn-large 清空 div#submitError.alert.alert-error.displayNONE.myalert div#submitSuccess.alert.alert-success.displayNONE div.row.content div.span4 h2 烟雨江南说 p 当欲望没有了枷锁,就没有了向前的路 p 只有转左,或者向右 p 左边是地狱,右边也是地狱 div.span4 h2 烟雨江南说 p 那些都是极好极好的 p 可是我偏偏不喜欢 div.span4 h2 烟雨江南说 p 我不怕傻 p 只怕 p 遇不到 p 可以让我变傻的人 div.span4 h2 烟雨江南说 p 人在年轻的时候总会有些莫名的坚持, p 并且以此感动着自己, p 却时常会在不经意间让真正重要的东西从指间流走。 div.span4 h2 烟雨江南说 p 记忆真是一种奇怪的东西, p 有时候会涤荡所有的苦难,只留下温情, p 有时候却磨灭掉曾有的欢乐,唯剩下苍白和丑陋。 div.span4 h2 烟雨江南说 p 那存在的,都是幻影。 p 那永恒的,终将毁灭。 p 世界万物,缤纷色彩,都是被蒙蔽的人心罢了。 div.span4 h2 烟雨江南说 p 诸神以真相示人,而世人却视而不见 div.span4 h2 烟雨江南说 p 只有绵羊会向狮子要求平等, p 而狮子们从来不会这样想。 div.span4 h2 烟雨江南说 p 愿迷途的旅人,从此得享安息。 p 因理想而不朽,因归返而救赎。 div#scrollToFoot 滚动到底部然后加载内容
效果如图:
但此时,上面的页面切换目前还都是无效状态;
滚动然后加载内容,也正处于无效状态;
注册和登录按钮,点击后也无法正常跳转;
我们需要依次解决这些问题。
【13】登录页面
接下来我们添加登录页面的路由和模板。
路由,打开app.js,在
app.use('/', routes); app.use('/users', users);
之前添加
var reg = require('./routes/reg'); var login = require('./routes/login');
之后添加:
app.use('/reg', reg); //注册的,reg.js来处理 app.use('/login', login); //登录的,login来处理
这样的话,就添加了注册和登录页面的路由了,但目前,我们还缺其具体文件和代码。
进入routes文件夹,创建reg.js和login.js
reg.js
var express = require('express'); //调用express模块 var router = express.Router(); //调用模块的Router方法 router.get('/', function (req, res, next) { res.render('reg') }); module.exports = router;
login.js
var express = require('express'); //调用express模块 var router = express.Router(); //调用模块的Router方法 router.get('/', function (req, res, next) { res.render('login') }); module.exports = router;
现在又缺模板文件了。
进入views文件夹,创建reg.jade和login.jade
reg.jade
extends layout block content form.form-horizontal(method="post") fieldset legend 注册 div.control-group label.control-label(for="username") 用户名 div.controls input.input-xlarge#username(type="text",name="username") p.help-block 你的账户名称,用于登录和提示 div.control-group label.control-label(for="password") 口令 div.controls input.input-xlarge#password(type="password",name="password") div.control-group label.control-label(for="password-repeat") 重复输入口令 div.controls input.input-xlarge#password-repeat(type="password",name="password-repeat") div.form-actions button.btn.btn-priamry(type="submit") 注册
login.jade
extends layout block content form.form-horizontal(method='post') fieldset legend 登录 div.control-group label.control-label(for='username') 用户名 div.controls input.input-xlarge#username(name='username',type='text') div.control-group label.control-label(for='password') 密码 div.controls input.input-xlarge#password(name='password',type='password') div.form-actions button.btn.btn-primary(type="submit") 登录
这个时候,注册和登录页面已经可以访问了(虽然还没有添加逻辑)