开箱即用:Vue和Express搭建的一个功能完善的先后端分离项目

前言

对于已有前端开发经验的朋友来讲,深刻学习node.js而且掌握它是一个必要的过程;对于即将毕业的软件开发学生来讲,创造拥有本身的开源小产品是对大学最好的总结。javascript

那么想和你们分享一个运用Vue框架写界面+Express框架写后端接口的这样一个开箱即用的先后端分离的项目,能更好更快的帮助你们学习node+vue,而且也可在项目开发中使用,减轻你们的工做负担。html

整理已久的文档和项目,还望你们手动点赞鼓励哈~~

因我以前是用博客记录本身的成长,个人博客地址为:Miss_hhl的博客,汇总了个人全部博客,也欢迎你们的关注。

本项目 github 地址为:project项目

1、项目结构

1.一、项目运行环境

node版本:10.16.3前端

express版本:4.16.1vue

npm版本:6.9.0java

vue版本: ^2.6.10node

@vue/cli版本:^3.4.0mysql

1.二、后端Express框架的项目目录

express-demo文件夹下的目录以下:ios

|  app.js     express 入口
│  package-lock.json
│  package.json       项目依赖包配置
│
├─bin
│      www
|
|——common            公共方法目录
|   |——corsRequest.js
|   |——errorLog.js
|   |——loginFilter.js
|   |——request.log
│
|
|——config             配置文件
|   |——index.js
|     
|——log                日志文件夹
|  
├─public              静态文件目录
│  |——images
│  |——javascripts
│  └─stylesheets
│
├─routes               url路由配置
│      
│—sql                 数据库基本配置目录
|   |——db.js
|__
复制代码

1.三、前端Vue框架的项目目

vue-demo文件夹下的目录以下:git

|——src                         源码目录
|  |——api                      请求文件
|  |——components               vue公共组件
|  |——pages                    页面组件文件
|     |——login                 登陆页面
|  |——default.vue              默认组件展现区
|  |——router                   vue的路由管理文件
|  |——store                    vue的状态管理文件
|  |——App.vue                  页面入口文件
|  |—— main.js                 程序入口文件,加载各类公共组件
|
|—— public                     静态文件,好比一些图片,json数据等
|  |—— favicon.ico             图标文件
|  |—— index.html              入口页面
|—— vue.config.js              是一个可选的配置文件,包含了大部分的vue项目配置
|—— .babel.config.js           ES6语法编译配置
|—— .gitignore                 git上传须要忽略的文件格式
|—— README.md                  项目说明
|—— package.json               项目基本信息,包依赖信息等
复制代码

2、如何在编辑器使用该项目

2.一、后端Express框架的使用

(1)安装插件,在安装插件以前要进入express-demo目录,运行命令行:github

npm install
复制代码

(2)开发环境启动,进入express-demo 目录,运行如下命令:

npm start
复制代码

(3)生产或测试环境,可在config文件夹配置路径,运行如下命令:

npm test   //测试环境
npm build  //生产环境
复制代码

2.二、前端Vue框架的使用

(1)安装插件,在安装插件以前要进入vue-demo目录,运行命令行:

npm install
复制代码

(2)开发环境启动,进入vue-demo 目录,运行如下命令:

npm run serve
复制代码

3、项目提供的功能点梳理

3.一、后端Express框架的功能点

3.1.一、开发环境解决跨域配置

在common文件夹的corsRequest.js对跨域进行了配置,而且将它在app.js 文件进行引用,这样客户端例如:单页面应用开发、移动应用等, 就能够跨域访问服务端对应的接口。

corsRequest.js模块的代码以下:

function corsRequest(app){
	app.all("*",(req,res,next)=>{
	    //设置容许跨域的域名,*表明容许任意域名跨域
	    res.header("Access-Control-Allow-Origin",'http://localhost:8081');
	    //容许的header类型
	    // res.header("Access-Control-Allow-Headers","X-Requested-With,Content-Type,content-type");
			res.header('Access-Control-Allow-Headers:Origin,X-Requested-With,Authorization,Content-Type,Accept,Z-Key')
	    //跨域容许的请求方式 
	    res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
			res.header("X-Powered-By","3.2.1");
			res.header("Content-Type","application/json;charset=utf-8");
			res.header("Access-Control-Allow-Credentials",true);
			res.header("Cache-Control","no-store");
	    if (req.method.toLowerCase() == 'options'){
				res.send(200);  //让options尝试请求快速结束
			}else{
				next();
			}
	})
}
module.exports=corsRequest;
复制代码

以后再app.js引用该模块,配置以下:

let corsRequest=require('./common/corsRequest.js');
//跨域配置
corsRequest(app);
复制代码

3.1.二、保存用户信息——种session存cookie

客户端经过登陆请求成功以后,服务端保存用户信息,而且将信息经过cookie返给客户端这整个过程我把它认为是种session存cookie;利用的是cookie-parser中间件和express-session中间件,如何使用它可详细看cookie-parser和express-session中间件使用,那么在该项目中的配置以下:

//种session存cookie配置
app.use(cookieParser('123456'));
app.use(session({
    secret: '123456',
    resave: false,
    saveUninitialized: true
}));
复制代码

3.1.三、结合数据库mysql

安装配置mysql

这里不具体介绍 mysql 的安装配置,具体操做能够参照配置文档,建议安装一个 mysql 数据库管理工具 navicat for mysql ,平时用来查看数据库数据增删改查的状况;

数据库配置

在 sql/db.js 文件中配置 mysql 的基本信息,相关配置项以下,能够对应更改配置项,改为你本身的配置便可:

//在sql目录下的db.js文件
let host=option.host||'127.0.0.1';     //数据库所在的服务器的ip地址
let user=option.user||'root';          //用户名
let password=option.password||'1qaz@WSX';     //密码
let database=option.database ||null           //你的数据库名
let db=mysql.createConnection({       
    host:host,       
    user:user,       
    password:password,       
    database:database 
});     //建立链接
复制代码

3.1.四、静态文件目录配置

app.use(express.static(path.join(__dirname, 'public')));
复制代码

3.1.五、登陆拦截器

客户端在每次请求时,服务端处理每一个请求以前须要对用户身份进行校验,这样使得每一个接口更具备安全性,在common文件夹下的loginFilter.js的相关代码逻辑以下:

function loginFilter(app){
    	app.use(function (req,res,next){
    		if(!(req.session.auth_username && req.session.auth_password)){
    			if(req.signedCookies.username && req.signedCookies.password){
    				let {username,password}=req.signedCookies;
    				req.session.auth_username=username;
    				req.session.auth_password=password;			//将cookie的值存在session里
    				next();
    			}else{
    				let arr=req.url.split('/');
    				let index=arr && arr.findIndex((item)=>{
    					return (item.indexOf('login')!=-1 || item.indexOf('relogin')!=-1);
    				});
    				if(index!==-1){
    					next();
    				}else{
    					return res.status(401).json({
    							msg: '没有登入,请先登入'
    					 })
    				}
    			}
    		}else{
    			next();
    		}
    })
}
module.exports=loginFilter;
复制代码

以后在app.js进行引用该模块,配置以下:

//登陆拦截器
loginFilter(app);
复制代码

3.1.六、请求路由配置处理

在项目中按模块划分请求处理,咱们在 routes 目录下分别新建每一个模块路由配置,例如用户模块,则为 user.js 文件,咱们在 app.js 主入口文件中引入每一个模块的路由,以用户模块进行举例,相关代码逻辑以下:

let user=require('./routes/user');
//路由配置
app.use('/api/user',user);
复制代码

3.1.七、日志处理

(1)经过morgan记录接口请求日志 morgan 是 express 默认的日志中间件,也可脱离 express,做为 node.js 的日志组件单独使用。详细学习可看GitHub库上的morgan模块。

项目在 app.js 文件中进行了如下配置:

// 输出日志到目录
let accessLogStream = fs.createWriteStream(path.join(__dirname,'./log/request.log'), {flags:'a',encoding: 'utf8' }); 
app.use(logger('combined', { stream: accessLogStream }))
复制代码

(2)经过winston记录错误日志 morgan 只能记录 http 请求的日志,因此还须要 winston来记录其它想记录的日志,例如:访问数据库出错等。winston 中的每个 logger 实例在不一样的日志级别能够存在多个传输配置。详细学习可看GitHub库上的winston模块。

在项目中,在 common/errorLog.js 文件中进行了如下配置:

const { createLogger, format, transports } = require('winston');
const { combine, timestamp, printf } = format;
const path = require('path');
const myFormat = printf(({ level, message, label, timestamp }) => {
  return `${timestamp}-${level}- ${message}`;
});
const option={
    file:{
        level:'error',
        filename: path.join(__dirname, '../log/error.log'),
        handleExceptions:true,
        json:true,
        maxsize:5242880,
        maxFiles:5,
        colorize:true,
    },
    console:{
        level:'debug',
        handleExceptions:true,
        json:false,
        colorize:true,
    }
}
const logger=createLogger({
    format: combine(
        timestamp(),
        myFormat
    ),
    transports:[
        new transports.File(option.file),
        new transports.Console(option.console),
    ],
    exitOnError:false,
});
logger.stream={
    write:function(message,encoding){
        logger.error(message)
    }
}
module.exports=logger;
复制代码

以后在app.js使用logger模块,代码配置以下:

let winston=require('./common/errorLog.js');
// error handler
app.use(function(err, req, res, next) {  
// set locals, only providing error in development 
res.locals.message = err.message;  
res.locals.error = req.app.get('env')=== 'development' ? err : {};  
winston.error(`${err.status || 500} - ${err.message} - ${req.originalUrl} -${req.method} - ${req.ip}`);  
// render the error page 
res.status(err.status || 500);  res.render('error');});
复制代码

3.1.八、文件上传处理

在项目中,使用multiparty模块实现文件的上传功能。包括如何将数据返回给前端,前端可经过url将图片显示。multiparty模块具体用法可详细阅读github,这里就不详细介绍。在文件夹routes的upload.js是实现文件上传的功能。

3.1.九、文件操做处理

node提供的内置文件操做模块fs,fs的功能强大。包括读写文件/文件夹,以及如何读流写流。在使用过程当中可具体参考node官网,便于快速上手。

3.1.十、环境变量统一配置

环境变量的统一管理配置能够更方便的维护项目,在package.json文件经过命令行给全局变量process.env添加新的属性,从而可配置项目环境变量:

let obj=Object.create(null);
if (process.env.NODE_ENV='development'){
	obj.baseUrl="http://localhost:3000";
} else if(process.env.NODE_ENV = 'test') {
	console.log('当前是测试环境')
}else if(process.env.NODE_ENV='production'){
	console.log('当前是生产环境');
}
module.exports=obj;
复制代码

3.1.十一、自动更新

当启动项目时,更改项目中的任何一个文件代码,运用nodemon模块它将会自动帮你从新编译代码,节省了咱们开发的时间。这个模块只要在package.json这个文件作一下配置便可。

3.二、前端Vue框架的功能点

3.2.一、配置页面路由

vue项目中,经过vue-router实现页面之间的跳转,也能够经过路由进行参数的收发,vue-router的相关代码配置以下:

import Router from 'vue-router';
import Vue from 'vue';
Vue.use(Router);
export default new Router({
	mode:'hash',
	routes:[
		{
			path:'/',
			redirect:'/login',
			component:()=>import('@/pages/login/index.vue')
		},
	]
})
复制代码

3.2.二、配置公共仓库

vue项目中,经过vuex实现状态的统一管理,也可运用于多层嵌套组件的通讯,以及如何持久化保存state状态值。vuex的相关代码配置以下:

import Vuex from 'vuex';
import Vue from 'vue';
import * as mutaionsType from './mutations-TYPE';
Vue.use(Vuex);
let persits=(store)=>{
	let state;
	if(state=sessionStorage.getItem('vuex-state')) store.replaceState(JSON.parse(state));
	store.subscribe((mutations,state)=>{
		sessionStorage.setItem('vuex-state',JSON.stringify(state));
	})
}
export default new Vuex.Store({
    plugins:[persits],
	state:{
		cancelArray:[],		//存放axios取消函数容器
	},
	mutations:{
		[mutaionsType.clear_cancel]:(state,payload)=>{
			state.cancelArray.forEach(fn=>{
				fn.call(fn);
			})
			state.cancelArray=[];
		},
		[mutaionsType.filter_cancel]:(state,payload)=>{
			let arr=state.cancelArray.filter(item=>!(item.url.includes(payload)));
			state.cancelArray=[...arr];
		},
		[mutaionsType.push_cancel]:(state,payload)=>{
			state.cancelArray.push(payload);
		},
	},
	actions:{},
	modules:{}
})
复制代码

3.2.三、二次封装axios

经过运用axios来请求接口,在项目当中对axios进行了二次封装,主要的功能包括:实现aixos请求拦截器和响应拦截,请求先后loading的开启和关闭,利用发布订阅实现取消请求函数的配置。相关代码以下:

import { baseURL } from './config.js'
import axios from 'axios'
import store from '@/store'
import { Loading, Message } from 'element-ui'
//每请求一次建立一个惟一的axios
class AjaxFetch {
  constructor() {
    this.config = {
      withCredentials: true,	//跨域凭证
      responseType: 'json',
      baseURL: baseURL,
      timeout: 3000,
    }
    this.queue = {}
  }
  request(option) {
    //建立一个axios实例
    let config = {
      ...this.config,
      ...option,
    }
    let instance = axios.create()
    this.interceptors(instance,config.url)
    return instance(config)
  }
  interceptors(instance,url) {
    instance.interceptors.request.use(
      (config) => {
        let CancelToken = axios.CancelToken
        //设置取消函数
        config.cancelToken = new CancelToken((c) => {
          //c是一个函数
          store.commit('push_cancel', { fn: c, url:url }) //存放取消的函数实例
        })
        if (Object.keys(this.queue).length == 0) {
          this._loading = Loading.service({
            lock: true,
            text: 'Loading',
            spinner: 'el-icon-loading',
            background: 'rgba(0, 0, 0, 0.7)',
          })
        }
        this.queue[url] = url;
        return config;
      },
      (err) => {
        return Promise.reject(err)
      }
    )
    instance.interceptors.response.use(
      (response) => {
        let {data} = response;
        store.commit('filter_cancel',url) //存放取消的函数实例
        delete this.queue[url]
        if (Object.keys(this.queue).length == 0) {
          this._loading.close()
        }
        switch (data.code) {
          case 500:
            Message({
              type: 'error',
              message: data.msg,
            })
            break;
          case 401:
            Message({
              type: 'warning',
              message: data.msg,
            })
            break;
        }
        return data;
      },
      (err) => {
        delete this.queue[url];
        if (Object.keys(this.queue).length == 0) {
          this._loading.close();
        }
        return Promise.reject(err)
      }
    )
  }
}
export default new AjaxFetch()
复制代码

3.2.四、配置第三方库

在vue项目中,咱们想直接经过this使用第三方库,可将第三方库直接挂载到Vue类的原型(prototype)上。若是是vue生态圈的模块,则直接经过选项注册在根组件上。

3.2.五、使用路由守卫

在vue项目中,使用路由的前置守卫实现的功能主要包括:登陆受权,切换页面将发布axios取消函数。固然你还能够编写更多的前置守卫业务代码。以下是hook.js的代码:

import store from '@/store'
export default {
  permitterRouter: function(to, from, next) {
    let { username } = store.state;
    let flag=Object.keys(username).length;      //判断是否登陆过的标识
    if(!flag){
        if(to.path.includes('/login')){
            next();
        }else{
            next('/login');
        }
    }else{
        if(to.path.includes('/login')){
            next('/home');
        }else{
            next();
        }
    }
  },
  cancelAjax: (to, from, next) => {
    store.commit('clear_cancel')
    next()
  },
}
复制代码

那么写好hook.js在文件夹route下的index.js进行配置以下:

//路由前置守卫
Object.values(hookRouter).forEach(hook=>{
    //使用bind可在hook函数获取this=>router
	router.beforeEach(hook.bind(router))
})
复制代码

4、总结

本文介绍了开源的先后端分离项目(开箱即用),完善了先后端的各类功能。但愿能经过这篇文档对开源代码进行更直接的介绍,帮助使用者减轻工做量,更高效完成工做,有更多时间提高本身的能力。 辛苦整理了很久,还望手动点赞鼓励小琳同窗~~

博客地址为:Miss_hhl的博客,汇总了我全部的博客,欢迎你们关注~~

本项目 github 地址为:project项目,还望你们点个star鼓励~~(项目会持续更新迭代哦~但愿你们持续关注哈)

相关文章
相关标签/搜索