本项目是本人2018年学习Express的练手项目,此文记录项目练习过程中的的一些细节和难点。(文章最后更新时间:2019/5/31)
本文目录
- 1.本项目使用的环境
- 2.项目结构
- 3.启动express
- 4.配置文件
- 5.加载静态文件中间件
- 6.使用ejs模板引擎
- 7.路由规划
- 8.拆分公共html模版
- 9.业务逻辑分析与模块化
- 10.业务逻辑示例
1.本项目使用的环境
为了减少按照本文档建立的项目的报错,请按照下方的列表锁定安装版本。
- 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.项目结构
- config ---- 存放项目配置文件
- public ---- 存放项目静态文件
- routes ---- 存放路由文件
- views ---- 存放视图文件(html模板)
- app.js ---- 项目入口文件
- package.json ---- 存储项目名、描述、作者、依赖等等信息
3.启动express
1.初始化项目,进入到项目目录,执行命令:
npm init -y
2.安装express
npm install [email protected]
3.app.js 中添加以下作为初始代码
var express = require('express')
var app = express()
app.get('/', function(req, res){
res.send('hello world!!!')
})
app.listen(5000)
4.安装nodemon监听文件并重启服务器
npm install -g nodemon
5.进入到项目目录,启动服务器
nodemon app.js
在浏览器查看
http://localhost:5000
4.配置文件
不管项目大小,比较好的做法是把配置文件和代码分离,通常我们会把配置信息写到一个配置文件里面去,例如:config.js,由于实际开发中,环境非常的多,例如:本地开发环境,本地测试环境以及线上环境等,不同的环境有不同的配置,我们通常将不同的环境配置放到不同的文件中去,例如,测试环境配置我们放到test.env.js文件中,开发环境配置放到dev.env.js文件中,线上生产环境放到prod.env.js文件中,由于配置文件比较多,我们需要很方便的去读取这些配置文件里面的内容,config-lite这个npm包可以解决我们的需求
1.安装config-lite
config-lite文档地址:
npm install config-lite --save
2.在config目录下新建default.js文件
module.exports = {
port: 5000
}
3.读取配置文件,在app.js中增加以下内容
// 引入config-lite
var config = require('config-lite')(__dirname)
// 引入项目依赖包配置文件
var package = require('./package.json')
config-lite会自动寻找config文件夹中的default.js,接下来就可以通过config+点的形式使用default.js里面的属性。同时通过引入package.json文件还可以使用自定义的变量package来使用package.json中的属性。
使用示例:
app.listen(config.port, function(){
console.log(`${package.name} is listening on ${config.port}`)
})
5.加载静态文件中间件
加载静态文件需要使用express内置的中间件
首先需要引用pathvar path = require('path')
然后通过app.use(express.static(path.resolve('./public')))
加载静态文件中间件,在express中就这一句话,就可以实现自动读取public目录中的静态文件了,这时候别的文件再去加载静态文件,直接'/'就可以直接访问到/public了。
6.使用ejs模板引擎
1.安装ejs模板引擎
npm install [email protected] --save
2.在app.js中引入ejs,并做相关设置
// 引入ejs
var ejs = require('ejs')
// 设置ejs模板目录
app.set('views', path.resolve('views'))
// 设置模板文件后缀名为html
app.engine('html', ejs.__express)
// 设置模板引擎为html
app.set('view engine', 'html')
3.在views文件夹下新建一个index.html来测试一下(文件中随便填写一点内容)
接下来在app.js文件中修改一下:
app.get('/', function(req, res){
res.render('index')
})
render代表的意思就是渲染,后面仅仅一个index就可以成功的找到index.html文件是因为前面的ejs配置了我们可以直接访问到views文件夹,并且后缀为html。
最后,在浏览器中输入[http://localhost:5000](http://localhost:5000/)
查看效果,我们可以成功看到index.html文件里的内容。
7.路由规划
7.1.项目路由结构
整个项目可以分为前台部分和后台部分,以下是需要渲染的前台部分静态文件路径
- pc端首页 /
- 移动端首页 /m
- 公司概括 /signin
- 品质品牌 /signup
- 人才招聘 /signout
- 联系我们 /foundation
- 登陆页面 /progress
- 注册页面 /senior
后台部分需要渲染的静态文件路径
后台首页 /admin
- 用户模块:/user
- 列表渲染 /list
- 增加用户 /add
- 编辑用户 /edit
- 修改密码 /repass
- 导航模块:/nav
- 列表渲染 /list
- 导航编辑 /alter
- 轮播模块:/carousel
- 列表渲染 /list
- 轮播编辑 /alter
- 留言模块:/message
- 列表渲染 /list
- 留言编辑 /alter
- 产品模块:/product
- 列表渲染 /list
- 产品编辑 /alter
7.2.创建路由模块
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
其它几个模块和user模块类似
以下是前台的路由模块比较集中,数量不是很多,所以暂时都写在index.js文件中,并没有进行细化。
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')
})
//品质品牌
router.get('/brand',function (req,res) {
res.render('home/brand')
})
......
module.exports = router
7.3.使用各个路由模块
1.将后台各个模版挂载到index.js模块下
routes/admin/index.js
var express = require('express')
var router = express.Router()
router.get('/',function (req,res) {
res.render('admin/index')
})
//引入后台的各个模块
var carousel = require('./carousel')
var message = require('./message')
var nav = require('./nav')
var product = require('./product')
var user = require('./user')
//加载使用各个模块
router.use('/carousel',carousel)
router.use('/message',message)
router.use('/nav',nav)
router.use('/product',product)
router.use('/user',user)
module.exports = router
2.在项目的入口文件app.js中引入routes/admin/index.js模块
引入admin路由模块
var admin = require('./routes/admin')
接下来作为中间件使用这个模块,同样在app.js中添加一条代码,使用admin路由模块
app.use('/admin', admin)
3.因为前台只有一个routes/home/index.js模块,只需要将这个模块引入到app.js,然后使用它就行了
引入home路由模块var home = require('./routes/home')
使用home路由模块
app.use('/', home)
7.4.渲染静态模板页面
渲染举例:
当用户访问,
这个时候需要渲染前台首页, 前台首页模板在views/home/index.html
这个请求是在routes/home/index.js路由模块中控制的,因此,在这个文件中做如下修改:
router.get('/', function (req, res) {
res.render('home/index')
})
以此类推,如果用户访问的是注册页面,
, 前台对应的模板位置在views/home/register.html
需要在routes/home/index.js文件中做如下修改:
router.get('/signup', function (req, res) {
res.render('home/register')
})
总结,如果是前台html文件需要渲染,那么就从views/home目录下去找,如果是后台html文件需要渲染,那么就从views/admin目录下去找
举个后台渲染的例子,如果用户访问;
, 这个路径对应的是后台首页,那么需要渲染的页面位置为 views/admin/index.html
接下来需要在routes/admin/index.js文件中加入下面代码:
router.get('/', function (req, res) {
res.render('admin/index.html')
})
render渲染的根目录位置和可以省略掉的.html后缀在ejs模板引擎的配置信息里面可以进行修改。
8.拆分公共html模版
在我们渲染页面的时候,发现有很多地方是可以公用的,例如头部和底部,因此,我们需要将头部代码和底部代码拆分出来,通过ejs的导入机制引入这些公共html模板
在/views/home下面新建文件:header.html和footer.html
在其他模板文件中使用上面这两个公共文件
<%- include('header.html')%>
<%- include('footer.html') %>
9.业务逻辑分析与模块化
1.前端发起请求
前端页面发起请求的方式:
1.a标签的href
2.img标签的src
3.script标签的src
4.link标签的href
5.ajax
6.form
1-4是页面加载时自动发起的请求,通过在express配置处理静态资源的中间件就可以实现自动处理,5和6是手动发起的请求
2.后端接收并处理请求
处理请求:1.直接返回相关资源
2.连接数据库,操作数据库 路由:发送sql
在express中进行mysql数据库连接的步骤:
首先需要安装mysqlnpm install mysql --save
接下来就是连接数据库
本来按照最开始的写法,是把链接数据的代码写在项目的入口文件app.js中的,但是这样写的坏处,每次接收到请求,就自动连接一次数据库,很浪费服务器性能,但是在routes里面很多个路由又都需要用到连接数据库,不能每次连接都写一遍,解决方法就是把连接数据库的代码单独封装出来,在需要的时候进行引用,同时又可以实现开发中的MVC分层(在这个项目里,M对应的是models文件夹,主要用来写数据库操作逻辑,V对应views,主要用来渲染页面,C对应routes,主要用来处理路由逻辑)。
在项目的根目录下新建一个libs文件夹,专门用来存放一些自己封装的库。
在libs里新建一个mysql.js
在这个文件中,我们主要是用来处理数据库连接的,并最终导出一个pool,当别的文件引入这个pool的时候,就可以成功连接数据库,并且通过使用pool.query来进行数据库的相关操作。
同时我们将连接数据库的配置信息写在了config文件夹中。
config文件夹的default.js中的代码如下:
module.exports = {
port: 5001,
mysql:{
//数据库的指向地址
host: '106.13.5.134',
user: 'root',
password: '0BlfztnqiyP',
database : 'rx_admin'
}
}
在models文件夹下新建一个userModel.js文件,专门用来处理数据库操作。
10.业务逻辑示例
需求:当email框失去焦点的时候,发送请求到后台,验证用户名是否存在**
// 验证用户名能不能注册
$('#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', '用户名已经存在'))
}else{
res.json(result.createResult('success', '用户名合法'))
}
})
})
在上面的代码中,我们使用到了userModel这个模型,根据mvc这种分层模式,model层是专门用来处理数据的,所有关于数据处理的操作我们都放到model层里面,在项目目录下新建models文件夹,存放所有数据模型
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这个文件中,我们引入了一个mysql.js的文件,这个是用来连接数据库的,另外还引入了一个文件userSqlMap.js,这个文件是用来定义sql语句的,以下为两个文件的代码
libs/mysql.js
var mysql = require('mysql')
var config = require('config-lite')(__dirname)
// 创建连接池
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;