NODEJS+EXPRESS4框架應用,瞭解一下

這個年頭前端的老鐵們面試的時候基本上都能看到一條:熟悉nodeJS,對於之前沒有怎麼了解這塊技術的前端老鐵們(大神忽略),本文是一個很好的傳送門,帶你們入門nodeJs+express4框架,並寫一個簡單帶有登陸功能、增刪改列表功能的小後臺(雖爲前端,這個項目沒做樣式美化,基礎bootstrap樣式引用),全面應用nodeJs+express4框架技術點。

廢話不多說,先看效果gif

本文碼字比較長,可能部分老鐵沒有耐心看完(但比看視頻學習肯定效率更高),所以老鐵們可以選擇收藏。最後貼上這個小練習項目源碼的github地址(歡迎給個star),可以自行下載,安裝依賴,運行看看。

一、創建項目
1、Express 應用生成器

通過應用生成器工具 express 可以快速創建一個應用的骨架。

通過如下命令安裝

$ npm install express-generator -g

注:-g是全局安裝在電腦系統,如果不想全局可以把-g去掉

 express -h

express-h 選項可以列出所有可用的命令行選項:

$ 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 的應用。

$ 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選項,即可。

然後安裝所有依賴包:

$ 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

二、開發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’}

三、開發文檔列表功能
1、讀取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,運行結果如下:

2、開發添加文檔功能
在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('/');//重定向到首頁
})

運行項目,進行測試:輸入:‘的的’,點擊添加便可以顯示內容

3、開發刪除文檔功能
在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('/');//重定向到首頁})

測試:是正常刪除的

5、美化界面
在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>


5、添加修改文檔功能
修改和增加相似,點擊每一列的“修改”時候時候彈框,執行方法把當前列的內容輸入到彈出框裏,點擊保存的時候觸發form表單提交然後再執行修改操作

1、在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">&times;</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>

2、在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('/');
//重定向到首頁
});

3、對應的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(); 
}
}

測試結果:

現在我們已經做好了增刪改查功能了

四、登錄功能
這裏我們要用到會話,是用第三方插件express-session,全局安裝它:

$ npm install express-session

1、首先開發登錄接口,在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('/');
//重定向到首頁
});

2、在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">&times;</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>

測試結果:

五、Express框架組件
一、路由:

路由是由一個 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 中間件。

二、使用中間件
中間件(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★)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章