Express練手項(xiàng)目:門戶網(wǎng)站

本項(xiàng)目是本人2018年學(xué)習(xí)Express的練手項(xiàng)目罢猪,此文記錄項(xiàng)目練習(xí)過程中的的一些細(xì)節(jié)和難點(diǎn)。(文章最后更新時(shí)間:2019/5/31)

本文目錄

  • 1.本項(xiàng)目使用的環(huán)境
  • 2.項(xiàng)目結(jié)構(gòu)
  • 3.啟動express
  • 4.配置文件
  • 5.加載靜態(tài)文件中間件
  • 6.使用ejs模板引擎
  • 7.路由規(guī)劃
  • 8.拆分公共html模版
  • 9.業(yè)務(wù)邏輯分析與模塊化
  • 10.業(yè)務(wù)邏輯示例

1.本項(xiàng)目使用的環(huán)境

為了減少按照本文檔建立的項(xiàng)目的報(bào)錯(cuò),請按照下方的列表鎖定安裝版本茂翔。

  • node v10.15.1
  • npm 6.9.0
  • mysql Ver 14.14 Distrib 5.7.19, for osx10.9 (x86_64) using EditLine wrapper
  • express 4.16.4

2.項(xiàng)目結(jié)構(gòu)

image.png
  • config ---- 存放項(xiàng)目配置文件
  • public ---- 存放項(xiàng)目靜態(tài)文件
  • routes ---- 存放路由文件
  • views ---- 存放視圖文件(html模板)
  • app.js ---- 項(xiàng)目入口文件
  • package.json ---- 存儲項(xiàng)目名、描述履腋、作者珊燎、依賴等等信息

3.啟動express

1.初始化項(xiàng)目,進(jìn)入到項(xiàng)目目錄遵湖,執(zhí)行命令:
npm init -y
2.安裝express
npm install express@4.16.4
3.app.js 中添加以下作為初始代碼

var express = require('express')
var app = express()

app.get('/', function(req, res){
  res.send('hello world;谡!延旧!')
})
app.listen(5000)

4.安裝nodemon監(jiān)聽文件并重啟服務(wù)器
npm install -g nodemon
5.進(jìn)入到項(xiàng)目目錄谋国,啟動服務(wù)器
nodemon app.js
在瀏覽器查看
http://localhost:5000

4.配置文件

不管項(xiàng)目大小,比較好的做法是把配置文件和代碼分離迁沫,通常我們會把配置信息寫到一個(gè)配置文件里面去芦瘾,例如:config.js捌蚊,由于實(shí)際開發(fā)中,環(huán)境非常的多近弟,例如:本地開發(fā)環(huán)境缅糟,本地測試環(huán)境以及線上環(huán)境等,不同的環(huán)境有不同的配置祷愉,我們通常將不同的環(huán)境配置放到不同的文件中去溺拱,例如,測試環(huán)境配置我們放到test.env.js文件中谣辞,開發(fā)環(huán)境配置放到dev.env.js文件中迫摔,線上生產(chǎn)環(huán)境放到prod.env.js文件中,由于配置文件比較多泥从,我們需要很方便的去讀取這些配置文件里面的內(nèi)容句占,config-lite這個(gè)npm包可以解決我們的需求
1.安裝config-lite
config-lite文檔地址: <https://www.npmjs.com/package/config-lite>
npm install config-lite --save
2.在config目錄下新建default.js文件

module.exports = {
  port: 5000
}

3.讀取配置文件,在app.js中增加以下內(nèi)容

// 引入config-lite
var config = require('config-lite')(__dirname)
// 引入項(xiàng)目依賴包配置文件
var package = require('./package.json')

config-lite會自動尋找config文件夾中的default.js,接下來就可以通過config+點(diǎn)的形式使用default.js里面的屬性躯嫉。同時(shí)通過引入package.json文件還可以使用自定義的變量package來使用package.json中的屬性纱烘。
使用示例:

app.listen(config.port, function(){
  console.log(`${package.name} is listening on ${config.port}`)
})

5.加載靜態(tài)文件中間件

加載靜態(tài)文件需要使用express內(nèi)置的中間件
首先需要引用pathvar path = require('path')
然后通過app.use(express.static(path.resolve('./public')))加載靜態(tài)文件中間件,在express中就這一句話祈餐,就可以實(shí)現(xiàn)自動讀取public目錄中的靜態(tài)文件了擂啥,這時(shí)候別的文件再去加載靜態(tài)文件,直接'/'就可以直接訪問到/public了帆阳。

6.使用ejs模板引擎

1.安裝ejs模板引擎
npm install ejs@2.6.1 --save
2.在app.js中引入ejs哺壶,并做相關(guān)設(shè)置

// 引入ejs
var ejs = require('ejs')
// 設(shè)置ejs模板目錄
app.set('views', path.resolve('views'))
// 設(shè)置模板文件后綴名為html
app.engine('html', ejs.__express)
// 設(shè)置模板引擎為html
app.set('view engine', 'html')

3.在views文件夾下新建一個(gè)index.html來測試一下(文件中隨便填寫一點(diǎn)內(nèi)容)
接下來在app.js文件中修改一下:

app.get('/', function(req, res){
  res.render('index')
})

render代表的意思就是渲染,后面僅僅一個(gè)index就可以成功的找到index.html文件是因?yàn)榍懊娴膃js配置了我們可以直接訪問到views文件夾蜒谤,并且后綴為html山宾。
最后,在瀏覽器中輸入[http://localhost:5000](http://localhost:5000/)查看效果鳍徽,我們可以成功看到index.html文件里的內(nèi)容资锰。

7.路由規(guī)劃

7.1.項(xiàng)目路由結(jié)構(gòu)

整個(gè)項(xiàng)目可以分為前臺部分和后臺部分,以下是需要渲染的前臺部分靜態(tài)文件路徑

  • pc端首頁 /
  • 移動端首頁 /m
  • 公司概括 /signin
  • 品質(zhì)品牌 /signup
  • 人才招聘 /signout
  • 聯(lián)系我們 /foundation
  • 登陸頁面 /progress
  • 注冊頁面 /senior

后臺部分需要渲染的靜態(tài)文件路徑

后臺首頁 /admin

  • 用戶模塊:/user
    • 列表渲染 /list
    • 增加用戶 /add
    • 編輯用戶 /edit
    • 修改密碼 /repass
  • 導(dǎo)航模塊:/nav
    • 列表渲染 /list
    • 導(dǎo)航編輯 /alter
  • 輪播模塊:/carousel
    • 列表渲染 /list
    • 輪播編輯 /alter
  • 留言模塊:/message
    • 列表渲染 /list
    • 留言編輯 /alter
  • 產(chǎn)品模塊:/product
    • 列表渲染 /list
    • 產(chǎn)品編輯 /alter

7.2.創(chuàng)建路由模塊

routes目錄為存放路由模塊的目錄
以下是后臺模塊路由文件
routes/admin/user.js

var express = require('express')
var router = express.Router()
router.get('/list',function (req,res) {
    res.render('admin/user_list')
})
router.get('/edit',function (req,res) {
    res.render('admin/user_edit')
})
router.get('/add',function (req,res) {
    res.render('admin/user_add')
})
router.get('/repass',function (req,res) {
    res.render('admin/user_repass')
})
module.exports = router

其它幾個(gè)模塊和user模塊類似
以下是前臺的路由模塊比較集中阶祭,數(shù)量不是很多绷杜,所以暫時(shí)都寫在index.js文件中,并沒有進(jìn)行細(xì)化濒募。
routes/home/index.js

var express = require('express')
var router = express.Router()
var userModel = require('../../models/userModel')
var result = require('../../libs/result')
//渲染首頁
router.get('/',function (req,res) {
    res.render('home/index')
})
//公司概括
router.get('/intro',function (req,res) {
    res.render('home/intro')
})
//品質(zhì)品牌
router.get('/brand',function (req,res) {
    res.render('home/brand')
})
......
module.exports = router

7.3.使用各個(gè)路由模塊

1.將后臺各個(gè)模版掛載到index.js模塊下
routes/admin/index.js

var express = require('express')
var router = express.Router()
router.get('/',function (req,res) {
    res.render('admin/index')
})
//引入后臺的各個(gè)模塊
var carousel = require('./carousel')
var message = require('./message')
var nav = require('./nav')
var product = require('./product')
var user = require('./user')
//加載使用各個(gè)模塊
router.use('/carousel',carousel)
router.use('/message',message)
router.use('/nav',nav)
router.use('/product',product)
router.use('/user',user)
module.exports = router

2.在項(xiàng)目的入口文件app.js中引入routes/admin/index.js模塊
引入admin路由模塊
var admin = require('./routes/admin')
接下來作為中間件使用這個(gè)模塊鞭盟,同樣在app.js中添加一條代碼,使用admin路由模塊
app.use('/admin', admin)
3.因?yàn)榍芭_只有一個(gè)routes/home/index.js模塊萨咳,只需要將這個(gè)模塊引入到app.js懊缺,然后使用它就行了
引入home路由模塊var home = require('./routes/home')
使用home路由模塊
app.use('/', home)

7.4.渲染靜態(tài)模板頁面

渲染舉例:
當(dāng)用戶訪問,<http://localhost:5000/>這個(gè)時(shí)候需要渲染前臺首頁, 前臺首頁模板在views/home/index.html
這個(gè)請求是在routes/home/index.js路由模塊中控制的鹃两,因此遗座,在這個(gè)文件中做如下修改:

router.get('/', function (req, res) {
  res.render('home/index')
})

以此類推,如果用戶訪問的是注冊頁面俊扳,<http://localhost:5000/signup>, 前臺對應(yīng)的模板位置在views/home/register.html
需要在routes/home/index.js文件中做如下修改:

router.get('/signup', function (req, res) {
  res.render('home/register')
})

總結(jié)途蒋,如果是前臺html文件需要渲染,那么就從views/home目錄下去找馋记,如果是后臺html文件需要渲染号坡,那么就從views/admin目錄下去找
舉個(gè)后臺渲染的例子,如果用戶訪問梯醒;<http://localhost:5000/admin>, 這個(gè)路徑對應(yīng)的是后臺首頁宽堆,那么需要渲染的頁面位置為 views/admin/index.html
接下來需要在routes/admin/index.js文件中加入下面代碼:

router.get('/', function (req, res) {
  res.render('admin/index.html')
})

render渲染的根目錄位置和可以省略掉的.html后綴在ejs模板引擎的配置信息里面可以進(jìn)行修改。

8.拆分公共html模版

在我們渲染頁面的時(shí)候茸习,發(fā)現(xiàn)有很多地方是可以公用的畜隶,例如頭部和底部,因此号胚,我們需要將頭部代碼和底部代碼拆分出來籽慢,通過ejs的導(dǎo)入機(jī)制引入這些公共html模板

在/views/home下面新建文件:header.html和footer.html
在其他模板文件中使用上面這兩個(gè)公共文件
<%- include('header.html')%>
<%- include('footer.html') %>

9.業(yè)務(wù)邏輯分析與模塊化

1.前端發(fā)起請求
前端頁面發(fā)起請求的方式:
1.a標(biāo)簽的href
2.img標(biāo)簽的src
3.script標(biāo)簽的src
4.link標(biāo)簽的href
5.ajax
6.form
1-4是頁面加載時(shí)自動發(fā)起的請求,通過在express配置處理靜態(tài)資源的中間件就可以實(shí)現(xiàn)自動處理猫胁,5和6是手動發(fā)起的請求
2.后端接收并處理請求
處理請求:1.直接返回相關(guān)資源
2.連接數(shù)據(jù)庫箱亿,操作數(shù)據(jù)庫 路由:發(fā)送sql
在express中進(jìn)行mysql數(shù)據(jù)庫連接的步驟:
首先需要安裝mysqlnpm install mysql --save
接下來就是連接數(shù)據(jù)庫
本來按照最開始的寫法,是把鏈接數(shù)據(jù)的代碼寫在項(xiàng)目的入口文件app.js中的弃秆,但是這樣寫的壞處届惋,每次接收到請求,就自動連接一次數(shù)據(jù)庫驾茴,很浪費(fèi)服務(wù)器性能盼樟,但是在routes里面很多個(gè)路由又都需要用到連接數(shù)據(jù)庫氢卡,不能每次連接都寫一遍锈至,解決方法就是把連接數(shù)據(jù)庫的代碼單獨(dú)封裝出來,在需要的時(shí)候進(jìn)行引用译秦,同時(shí)又可以實(shí)現(xiàn)開發(fā)中的MVC分層(在這個(gè)項(xiàng)目里峡捡,M對應(yīng)的是models文件夾,主要用來寫數(shù)據(jù)庫操作邏輯筑悴,V對應(yīng)views们拙,主要用來渲染頁面,C對應(yīng)routes阁吝,主要用來處理路由邏輯)砚婆。
在項(xiàng)目的根目錄下新建一個(gè)libs文件夾,專門用來存放一些自己封裝的庫。
在libs里新建一個(gè)mysql.js

image.png

在這個(gè)文件中装盯,我們主要是用來處理數(shù)據(jù)庫連接的坷虑,并最終導(dǎo)出一個(gè)pool,當(dāng)別的文件引入這個(gè)pool的時(shí)候埂奈,就可以成功連接數(shù)據(jù)庫迄损,并且通過使用pool.query來進(jìn)行數(shù)據(jù)庫的相關(guān)操作。
同時(shí)我們將連接數(shù)據(jù)庫的配置信息寫在了config文件夾中账磺。
config文件夾的default.js中的代碼如下:

module.exports = {
  port: 5001,
  mysql:{
    //數(shù)據(jù)庫的指向地址
    host: '106.13.5.134',
    user: 'root',
    password: '0BlfztnqiyP',
    database : 'rx_admin'
  }
}

在models文件夾下新建一個(gè)userModel.js文件芹敌,專門用來處理數(shù)據(jù)庫操作。


image.png

10.業(yè)務(wù)邏輯示例

需求:當(dāng)email框失去焦點(diǎn)的時(shí)候垮抗,發(fā)送請求到后臺氏捞,驗(yàn)證用戶名是否存在**

// 驗(yàn)證用戶名能不能注冊
$('#email').blur(function () {
    $.get('/getuser', { email: $('#email').val() }, function (result) {
        if (result.success == 'error') {
            $('.message-email').html(result.data).css('color', 'red')
        } else {
            $('.message-email').html(result.data).css('color', 'green')
        }
    })
})

后臺寫“/getuser”路由,處理請求 routes/home/index.js

// 查詢是否可以注冊
router.get('/getuser', function(req, res){
  userModel.findOne(req.query.email, function (data) {
    if (data) {
      res.json(result.createResult('error', '用戶名已經(jīng)存在'))
    }else{
      res.json(result.createResult('success', '用戶名合法'))
    }   
  })
})

在上面的代碼中冒版,我們使用到了userModel這個(gè)模型幌衣,根據(jù)mvc這種分層模式,model層是專門用來處理數(shù)據(jù)的壤玫,所有關(guān)于數(shù)據(jù)處理的操作我們都放到model層里面豁护,在項(xiàng)目目錄下新建models文件夾,存放所有數(shù)據(jù)模型
models/userModel.js

var pool = require('../libs/mysql')
var userSqlMap = require('../libs/userSqlMap')
var userModel = {
    findOne: function (email, callback){
    pool.query(userSqlMap.getByEmail, email, function (error, result) {
      if (error) {
        throw error
      }
      callback && callback(result[0])
    })
  },
}
module.exports = userModel

在userModel.js這個(gè)文件中欲间,我們引入了一個(gè)mysql.js的文件楚里,這個(gè)是用來連接數(shù)據(jù)庫的,另外還引入了一個(gè)文件userSqlMap.js猎贴,這個(gè)文件是用來定義sql語句的班缎,以下為兩個(gè)文件的代碼
libs/mysql.js

var mysql = require('mysql')
var config = require('config-lite')(__dirname)
// 創(chuàng)建連接池
var pool = mysql.createPool(config.mysql)
module.exports = pool

libs/userSqlMap.js

var userSqlMap = {
  add: 'insert into user(username, email,  password, create_time) values(?, ?, ?, ?)',
  deleteById: 'delete from user where id = ?',
  update: 'update user set username=?, email=?, role=?, status=? where id=?',
  list: 'select * from user',
  getById: 'select * from user where id = ?',
  getByEmail: 'select * from user where email = ?'
};
module.exports = userSqlMap;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市她渴,隨后出現(xiàn)的幾起案子达址,更是在濱河造成了極大的恐慌,老刑警劉巖趁耗,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沉唠,死亡現(xiàn)場離奇詭異,居然都是意外死亡苛败,警方通過查閱死者的電腦和手機(jī)满葛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罢屈,“玉大人嘀韧,你說我怎么就攤上這事〔疲” “怎么了锄贷?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我谊却,道長蹂随,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任因惭,我火速辦了婚禮岳锁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹦魔。我一直安慰自己激率,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布勿决。 她就那樣靜靜地躺著乒躺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪低缩。 梳的紋絲不亂的頭發(fā)上嘉冒,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機(jī)與錄音咆繁,去河邊找鬼讳推。 笑死,一個(gè)胖子當(dāng)著我的面吹牛玩般,可吹牛的內(nèi)容都是我干的银觅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坏为,長吁一口氣:“原來是場噩夢啊……” “哼究驴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匀伏,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤洒忧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后够颠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熙侍,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年摧找,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了核行。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹬耘,死狀恐怖兄淫,靈堂內(nèi)的尸體忽然破棺而出汽摹,到底是詐尸還是另有隱情,我是刑警寧澤刻诊,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站如筛,受9級特大地震影響堡牡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杨刨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一晤柄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧妖胀,春花似錦芥颈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涂臣,卻和暖如春盾计,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赁遗。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工署辉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岩四。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓涨薪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炫乓。 傳聞我的和親對象是個(gè)殘疾皇子刚夺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容