这个年头前端的老铁们面试的时候基本上都能看到一条:熟悉nodeJS,对于以前没有怎么了解这块技术的前端老铁们(大神忽略),本文是一个很好的传送门,带大家入门nodeJs+express4框架,并写一个简单带有登录功能、增删改列表功能的小后台(虽为前端,这个项目没作样式美化,基础bootstrap样式引用),全面应用nodeJs+express4框架技术点。javascript
废话很少说,先看效果gifcss
本文码字比较长,可能部分老铁没有耐心看完(但比看视频学习确定效率更高),因此老铁们能够选择收藏。最后贴上这个小练习项目源码的github地址(欢迎给个star),能够自行下载,安装依赖,运行看看。html
1、建立项目
一、Express 应用生成器前端
经过应用生成器工具 express 能够快速建立一个应用的骨架。java
经过以下命令安装node
$ npm install express-generator -g
注:-g是全局安装在电脑系统,若是不想全局能够把-g去掉jquery
express -h
express-h 选项能够列出全部可用的命令行选项:git
$ express -h Usage: express [options] [dir] Options: -h, --help output usage information -V, --version output the version number -e, --ejs add ejs engine support (defaults to jade) --hbs add handlebars engine support -H, --hogan add hogan.js engine support -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory
例如,下面的示例就是在当前工做目录下建立一个命名为 myapp 的应用。github
$ express myapp create : myapp create : myapp/package.json create : myapp/app.js create : myapp/public create : myapp/public/javascripts create : myapp/public/images create : myapp/routes create : myapp/routes/index.js create : myapp/routes/users.js create : myapp/public/stylesheets create : myapp/public/stylesheets/style.css create : myapp/views create : myapp/views/index.jade create : myapp/views/layout.jade create : myapp/views/error.jade create : myapp/bin create : myapp/bin/www
注意:默认是安装的jade模板引擎,若是要安装ejs模板引擎的话,输入:express -e选项,便可。web
而后安装全部依赖包:
$ cd myapp $ npm install
启动这个应用(MacOS 或 Linux 平台):
$ DEBUG=myapp npm start
Windows 平台使用以下命令:
> set DEBUG=myapp & npm start
而后在浏览器中打开 http://localhost:3000/ 网址就能够看到这个应用了。
经过 Express 应用生成器(本机测试是-e)建立的应用通常都有以下目录结构:
. ├── app.js ├── bin │ └── www ├── package.json ├── public │ ├── images │ ├── javascripts │ └── stylesheets │ └── style.css ├── routes │ ├── index.js │ └── users.js └── views ├── error.ejs ├── index.ejs
2、开发json数据库
在myapp下建立db.js添加数据的增、删、改、查、保存功能
const repos = require('./data'); const fs = require('fs'); module.exports ={ //持久化写入,保存 store(){ fs.writeFileSync(__dirname+'/data.json', JSON.stringify(repos)); //_dirname得到当前文件所在目录的完整目录名 }, //获取索引 get(index){ return repos[index]; }, get list(){ return repos; }, add(article){ repos.push(article); this.store(); }, del(index){ repos.splice(index,1); this.store(); }, update(index,newArticle){ respos.splice(index,1,newArticle); this.store(); } }
在myapp下建立data.json,里面空json文件
接下来咱们在在myapp下建立test文件,建立test_db.js测试写入功能:
'use strict'; let db = require('../db'); db.add({name:'nihao'}); console.log(db.list);
运行测试:node test_db.js
会看到data.json已经插入{name:’nihao’}
3、开发文档列表功能
一、读取data.json里数据,响应在页面上
首先,启动app.js把路由入口修改一下,让他渲染index.ejs,里面的内容由db.js里的方法将data.json里的数据渲染在index.ejs
在views文件夹里的index.ejs改写
<!DOCTYPE html> <html> <head> <title></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <ul> <% list.forEach(function(article){ %><li><%= article.name %></li><% })%> </ul> </body> </html>
填写几条测试数据在 data.json
[ {"name":"nihaonew"}, {"name":"nihao1"}, {"name":"nihao2"}, {"name":"nihao3"} ]
启动项目:node bin/www,运行结果以下:
二、开发添加文档功能
在views文件夹里的index.ejs添加,form 表单提交,触发/add功能
<!DOCTYPE html> <html> <head> <title></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <div> <form action="/add" method="post"> <label><input type="text" name="name" />名字</label> <input type="submit" value="添加" /> </form> </div> <ul> <% list.forEach(function(article){ %><li><%= article.name %></li><% })%> </ul> </body> </html>
在app.js里添加路由的处理,post处理
//app.use('/', index); //app.use('/users', users); app.get('/',function(req,res){ res.render('index',{list:db.list}) }) app.post('/add',function(req,res){ db.add({name:req.body.name}); res.redirect('/');//重定向到首页 })
运行项目,进行测试:输入:‘的的’,点击添加即可以显示内容
三、开发删除文档功能
在views文件夹里的index.ejs添加
<!DOCTYPE html> <html> <head> <title></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <div> <form action="/add" method="post"> <label><input type="text" name="name" />名字</label> <input type="submit" value="添加" /> </form> </div> <ul> <% list.forEach(function(article){ %><li><%= article.name %><a href="/del?index=<%=index%>">删除</a></li><% })%> </ul> </body> </html>
在app.js里添加删除路由的处理,获取索引参数执行删除操做
//app.use('/', index); //app.use('/users', users); app.get('/',function(req,res){ res.render('index',{list:db.list}) }) app.post('/add',function(req,res){ db.add({name:req.body.name}); res.redirect('/');//重定向到首页 }); //删除文档选项 app.get('/del',function(req,res){ let index = req.query.index; db.del(index); res.redirect('/');//重定向到首页})
测试:是正常删除的
五、美化界面
在index.ejs里引入bootstrap,添加点击的时候弹框
<link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
五、添加修改文档功能
修改和增长类似,点击每一列的“修改”时候时候弹框,执行方法把当前列的内容输入到弹出框里,点击保存的时候触发form表单提交而后再执行修改操做
一、在index.ejs里添加bootstrap弹框代码
<!-- 更改表单--> <form action="/update" method="post"> <div class="modal fade" id="myModal2" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title" id="myModalLabel">Modal title</h4> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> </div> <div class="modal-body"> <div class="form-group"> <label for="exampleInputEmail1">名字</label> <input type="hidden" name="index" id="updateIndex"/> <input type="text" name="name" class="form-control" id="updateName" placeholder="name"> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <input type="submit" class="btn btn-primary" value="更改"></input> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> </form> <ul class="list-group"> <% list.forEach(function(article,index){ %><li class="list-group-item"><%= article.name %><a href="/del?index=<%=index%>" class="btn btn-default" >删除</a><a href="#" onclick="edit(<%=index%>)" class="btn btn-default" data-toggle="modal" data-target="#myModal2">更改</a></li><% })%> </ul> <script> function edit(index){ //alert(index) $.get('/get/'+index,function(result){ $('#updateIndex')[0].value = index; $('#updateName')[0].value = result.name; }) }; </script>
二、在app.js里面配置路由:
//经过索引获取当前数据 app.get('/get/:index',function(req,res){ var index = req.params.index; console.log(index) var name = db.get(index); res.send(name); //返回一个json对象 }); //更新文档 app.post('/update',function(req,res){ var index = req.body.index; var name = req.body.name; db.update(index,{name}); res.redirect('/'); //重定向到首页 });
三、对应的db.js执行的是
const repos = require('./data'); const fs = require('fs'); module.exports ={ //持久化写入,保存 store(){ fs.writeFileSync(__dirname+'/data.json', JSON.stringify(repos)); //_dirname得到当前文件所在目录的完整目录名 }, get(index){ return repos[index]; }, get list(){ return repos; }, add(article){ repos.push(article); this.store(); }, del(index){ repos.splice(index,1); this.store(); }, update(index,newArticle){ repos.splice(index,1,newArticle); this.store(); } }
测试结果:
如今咱们已经作好了增删改查功能了
4、登陆功能
这里咱们要用到会话,是用第三方插件express-session,全局安装它:
$ npm install express-session
一、首先开发登陆接口,在app.js里引入
var session = require('express-session') app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); //TODO:加入会话插件 app.use(session({ resave:false,//添加这行 saveUninitialized: true,//添加这行 secret: 'keyboard cat' }) //登陆接口 app.post('/login',function(req,res){ let loginName = req.body.loginName; let passWord = req.body.passWord; if (loginName === 'test' &&passWord === '123456'){ //会话功能 req.session.logined = true; res.send("success"); }else{ res.send("error"); }}); //登出 app.get('/logout',function(req,res){ req.session.logined = false; res.redirect('/'); //重定向到首页 });
二、在index.ejs里添加判断是否登陆,若是没有登陆就是出现登陆,而后点击登陆弹框,把输入的帐号和密码进行验证经过,放行,如今添加和退出
<ul class="nav nav-pills"> <% if(logined){%> <li role="presentation"> <a href="javascript:;" data-toggle="modal" data-target="#myModal" class="btn btn-info" role="button">添加</a> </li> <li role="presentation"><a href="/logout">退出</a></li> <%}else{%> <li role="presentation"> <a href="javascript:;" data-toggle="modal" data-target="#loginDialog">登陆</a> </li> <%}%> </ul> <!-- 登陆表单--> <form action="/login" method="post" id="loginDialogForm"> <div class="modal fade" id="loginDialog" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title" id="myModalLabel">登陆框</h4> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> </div> <div class="modal-body"> <div class="form-group"> <label for="loginName">登陆名:</label> <input type="text" name="loinName" class="form-control" id="loginName" placeholder="loginName"> </div> <div class="form-group"> <label for="password">密码:</label> <input type="text" name="passWord" class="form-control" id="passWord" placeholder="passWord"> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <input type="submit" class="btn btn-primary" value="登陆"></input> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> </form> <script> function edit(index){ //alert(index) $.get('/get/'+index,function(result){ $('#updateIndex')[0].value = index; $('#updateName')[0].value = result.name; }) }; $('#loginDialogForm')[0].onsubmit = function(event){ event.preventDefault(); $.post('/login',{loginName:$('#loginName')[0].value,passWord:$('#passWord')[0].value},function(result){ if(result ==='success'){ window.location.reload();//刷新页面 }else{ alert('登陆失败!') } }); return false; } </script>
测试结果:
5、Express框架组件
1、路由:
路由是由一个 URI、HTTP 请求(GET、POST等)和若干个句柄组成,它的结构以下: app.METHOD(path, [callback...], callback), app 是 express 对象的一个实例, METHOD 是一个 HTTP 请求方法, path 是服务器上的路径, callback 是当路由匹配时要执行的函数
var express = require('express'); var app = express(); // respond with "hello world" when a GET request is made to the homepage app.get('/', function(req, res) { res.send('hello world'); });
路由句柄:
能够为请求处理提供多个回调函数,其行为相似 中间件。惟一的区别是这些回调函数有可能调用 next('route') 方法而略过其余路由回调函数。能够利用该机制为路由定义前提条件,若是在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。
路由句柄有多种形式,能够是一个函数、一个函数数组,或者是二者混合,以下所示.
使用一个回调函数处理路由:
app.get('/example/a', function (req, res) { res.send('Hello from A!'); });
使用多个回调函数处理路由(记得指定 next 对象):
app.get('/example/b', function (req, res, next) { console.log('response will be sent by the next function ...'); next(); }, function (req, res) { res.send('Hello from B!'); });
响应方法
下表中响应对象(res)的方法向客户端返回响应,终结请求响应的循环。若是在路由句柄中一个方法也不调用,来自客户端的请求会一直挂起。
方法 描述 res.download() 提示下载文件。 res.end() 终结响应处理流程。 res.json() 发送一个 JSON 格式的响应。 res.jsonp() 发送一个支持 JSONP 的 JSON 格式的响应。 res.redirect() 重定向请求。 res.render() 渲染视图模板。 res.send() 发送各类类型的响应。 res.sendFile 以八位字节流的形式发送文件。 res.sendStatus() 设置响应状态代码,并将其以字符串形式做为响应体的一部分发送。 app.route()
可以使用 app.route() 建立路由路径的链式路由句柄。因为路径在一个地方指定,这样作有助于建立模块化的路由,并且减小了代码冗余和拼写错误。
app.route('/book') .get(function(req, res) { res.send('Get a random book'); }) .post(function(req, res) { res.send('Add a book'); }) .put(function(req, res) { res.send('Update the book'); }); express.Router
可以使用 express.Router 类建立模块化、可挂载的路由句柄。Router 实例是一个完整的中间件和路由系统,所以常称其为一个 “mini-app”。
下面的实例程序建立了一个路由模块,并加载了一个中间件,定义了一些路由,而且将它们挂载至应用的路径上。
在 app 目录下建立名为 birds.js 的文件,内容以下:
var express = require('express'); var router = express.Router(); // 该路由使用的中间件 router.use(function timeLog(req, res, next) { console.log('Time: ', Date.now()); next(); }); // 定义网站主页的路由 router.get('/', function(req, res) { res.send('Birds home page'); }); // 定义 about 页面的路由 router.get('/about', function(req, res) { res.send('About birds'); }); module.exports = router;
而后在应用中加载路由模块:
var birds = require('./birds'); ... app.use('/birds', birds);
应用便可处理发自 /birds 和 /birds/about 的请求,而且调用为该路由指定的 timeLog 中间件。
2、使用中间件
中间件(Middleware) 是一个函数,它能够访问请求对象(request object (req)), 响应对象(response object (res)), 和 web 应用中处于请求-响应循环流程中的中间件,通常被命名为 next 的变量。
中间件的功能包括:
执行任何代码。
修改请求和响应对象。
终结请求-响应循环。
调用堆栈中的下一个中间件。
若是当前中间件没有终结请求-响应循环,则必须调用 next() 方法将控制权交给下一个中间件,不然请求就会挂起。
Express 应用可以使用以下几种中间件:
应用级中间件
路由级中间件
错误处理中间件
内置中间件
第三方中间件
使用可选则挂载路径,可在应用级别或路由级别装载中间件。另外,你还能够同时装在一系列中间件函数,从而在一个挂载点上建立一个子中间件栈。
应用级中间件
应用级中间件绑定到 app 对象 使用 app.use() 和 app.METHOD(), 其中, METHOD 是须要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,所有小写。例如:
var app = express(); // 没有挂载路径的中间件,应用的每一个请求都会执行该中间件 app.use(function (req, res, next) { console.log('Time:', Date.now()); next(); }); // 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它 app.use('/user/:id', function (req, res, next) { console.log('Request Type:', req.method); next(); }); // 路由和句柄函数(中间件系统),处理指向 /user/:id 的 GET 请求 app.get('/user/:id', function (req, res, next) { res.send('USER'); });
下面这个例子展现了在一个挂载点装载一组中间件。
// 一个中间件栈,对任何指向 /user/:id 的 HTTP 请求打印出相关信息 app.use('/user/:id', function(req, res, next) { console.log('Request URL:', req.originalUrl); next(); }, function (req, res, next) { console.log('Request Type:', req.method); next(); });
做为中间件系统的路由句柄,使得为路径定义多个路由成为可能。在下面的例子中,为指向 /user/:id 的 GET 请求定义了两个路由。第二个路由虽然不会带来任何问题,但却永远不会被调用,由于第一个路由已经终止了请求-响应循环。
// 一个中间件栈,处理指向 /user/:id 的 GET 请求 app.get('/user/:id', function (req, res, next) { console.log('ID:', req.params.id); next(); }, function (req, res, next) { res.send('User Info'); }); // 处理 /user/:id, 打印出用户 id app.get('/user/:id', function (req, res, next) { res.end(req.params.id); });
若是须要在中间件栈中跳过剩余中间件,调用 next('route') 方法将控制权交给下一个路由。 注意: next('route') 只对使用 app.VERB() 或 router.VERB() 加载的中间件有效。
// 一个中间件栈,处理指向 /user/:id 的 GET 请求 app.get('/user/:id', function (req, res, next) { // 若是 user id 为 0, 跳到下一个路由 if (req.params.id == 0) next('route'); // 不然将控制权交给栈中下一个中间件 else next(); // }, function (req, res, next) { // 渲染常规页面 res.render('regular'); }); // 处理 /user/:id, 渲染一个特殊页面 app.get('/user/:id', function (req, res, next) { res.render('special'); });
路由级中间件
路由级中间件和应用级中间件同样,只是它绑定的对象为 express.Router()。
var router = express.Router();
路由级使用 router.use() 或 router.VERB() 加载。
上述在应用级建立的中间件系统,可经过以下代码改写为路由级:
var app = express(); var router = express.Router(); // 没有挂载路径的中间件,经过该路由的每一个请求都会执行该中间件 router.use(function (req, res, next) { console.log('Time:', Date.now()); next(); }); // 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息 router.use('/user/:id', function(req, res, next) { console.log('Request URL:', req.originalUrl); next(); }, function (req, res, next) { console.log('Request Type:', req.method); next(); }); // 一个中间件栈,处理指向 /user/:id 的 GET 请求 router.get('/user/:id', function (req, res, next) { // 若是 user id 为 0, 跳到下一个路由 if (req.params.id == 0) next('route'); // 负责将控制权交给栈中下一个中间件 else next(); // }, function (req, res, next) { // 渲染常规页面 res.render('regular'); }); // 处理 /user/:id, 渲染一个特殊页面 router.get('/user/:id', function (req, res, next) { console.log(req.params.id); res.render('special'); }); // 将路由挂载至应用 app.use('/', router);
错误处理中间件
错误处理中间件有 4 个参数,定义错误处理中间件时必须使用这 4 个参数。即便不须要 next 对象,也必须在签名中声明它,不然中间件会被识别为一个常规中间件,不能处理错误。
错误处理中间件和其余中间件定义相似,只是要使用 4 个参数,而不是 3 个,其签名以下: (err, req, res, next)。
app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); });
请参考 错误处理 一章了解更多关于错误处理中间件的内容。
内置中间件
从 4.x 版本开始,, Express 已经再也不依赖 Connect 了。除了 express.static, Express 之前内置的中间件如今已经所有单独做为模块安装使用了。请参考 中间件列表。
express.static(root, [options])
express.static 是 Express 惟一内置的中间件。它基于 serve-static,负责在 Express 应用中提托管静态资源。
参数 root 指提供静态资源的根目录。
可选的 options 参数拥有以下属性。
属性 描述 类型 缺省值 dotfiles 是否对外输出文件名以点(.)开头的文件。可选值为 “allow”、“deny” 和 “ignore” String “ignore” etag 是否启用 etag 生成 Boolean true extensions 设置文件扩展名备份选项 Array [] index 发送目录索引文件,设置为 false 禁用目录索引。 Mixed “index.html” lastModified 设置 Last-Modified 头为文件在操做系统上的最后修改日期。可能值为 true 或 false。 Boolean true maxAge 以毫秒或者其字符串格式设置 Cache-Control 头的 max-age 属性。 Number 0 redirect 当路径为目录时,重定向至 “/”。 Boolean true setHeaders 设置 HTTP 头以提供文件的函数。 Function
下面的例子使用了 express.static 中间件,其中的 options 对象通过了精心的设计。
var options = { dotfiles: 'ignore', etag: false, extensions: ['htm', 'html'], index: false, maxAge: '1d', redirect: false, setHeaders: function (res, path, stat) { res.set('x-timestamp', Date.now()); } } app.use(express.static('public', options)); 每一个应用可有多个静态目录。 app.use(express.static('public')); app.use(express.static('uploads')); app.use(express.static('files'));
更多关于 serve-static 和其参数的信息,请参考 serve-static 文档。
第三方中间件
经过使用第三方中间件从而为 Express 应用增长更多功能。
安装所需功能的 node 模块,并在应用中加载,能够在应用级加载,也能够在路由级加载。
下面的例子安装并加载了一个解析 cookie 的中间件: cookie-parser
$ npm install cookie-parser var express = require('express'); var app = express(); var cookieParser = require('cookie-parser'); // 加载用于解析 cookie 的中间件 app.use(cookieParser());
项目源码的github地址(厚脸皮求个star★)