Express练手项目:门户网站

本项目是本人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.项目结构

image.png
  • 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

image.png

在这个文件中,我们主要是用来处理数据库连接的,并最终导出一个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文件,专门用来处理数据库操作。


image.png

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;

你可能感兴趣的:(Express练手项目:门户网站)