本項(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)
- 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
在這個(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ù)庫操作。
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;