這個年頭前端的老鐵們面試的時候基本上都能看到一條:熟悉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★)