開箱即用: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鼓勵~~(項目會持續更新迭代哦~但願你們持續關注哈)

相關文章
相關標籤/搜索