Vue项目架构优化

11,373 阅读5分钟

写在前面

这篇博客我将为你介绍vue的架构思想,当然这只是我根据遇到的项目总结的vue架构,这是我发现的一个小三轮,如果你有好的架构也欢迎指教哦。

好了,我们开始聊吧!

重构介绍

  1. 第一次重构:集中ajax请求
  2. 第二次重构:使用类继承架构
  3. 第三次重构:使用配置表架构
  4. 第四次重构:使用类继承+单例架构

2019.9.16 最近一次重构git地址

最初的版本

目录结构

├── src                // 生产目录
│   ├── api            // axios操作
│   ├── components     // 组件 
│   │		├── common  // 公共组件
│   │		├── admin   // 用户组件
│   │		└── seller  // 商家组件  		
│   ├── router         // 路由
│   ├── store          // vuex状态管理器
│	├── App.vue        // 首页
│   └── main.js        // Webpack 预编译入口 

代码逻辑

很简单先访问App.vue,根据路由映射不同组件渲染页面,每个页面都有ajax请求

ajax请求长这样

getUserInfo: function() {
    this.axios.get('user/infor')
    .then(res => {
        if (res.data.status) {
            this.user = res.data.data;
        }
    })
    .catch(error => {
        console.log(error);
    });
},

前端第一次重构

2018.4.21 Github地址:elm1.0

目录结构

├── src                // 生产目录
│   ├── api            // axios操作
│   ├── components     // 组件 
│   ├── router         // 路由
│   ├── store          // vuex状态管理器
│	├── App.vue        // 首页
│   └── main.js        // Webpack 预编译入口

没错只是将ajax请求都集中到了api目录下 api目录下的index.js文件

import axios from 'axios';
import store from '../store';

let httpURL = "http://www.xuguobin.club/api/elm/" //这是我服务器的api接口
let localURL = 'http://localhost/api/elm/';     //这是本地koa2的api接口
axios.defaults.baseURL = localURL;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

export default {
    //获取用户信息
    getUser() {
        return axios.get('user/infor');
    },
    //获取订单
    getOrders(orderType) {
        return axios.get('user/order?type=' + orderType);
    },
    //提交订单
    submitOrder(order) {
        return axios.get('user/submit?order=' + order);
    },
    //确认收货
    confirmOrder(orderId) {
        return axios.get('user/confirm?orderId=' + orderId);
    },
    //提交评价
    submitRating(rating) {
        return axios.get('user/rating?rating=' + rating);
    },
    //用户登录
    userLogin(user) {
        return axios.post('user/login',`username=${user.username}&password=${user.password}`);
    },
};

这样子做,很好的将axios请求与vue页面解耦和了! 现在ajax请求长这样

getUserInfo: function() {
    this.api.getUser()
    .then(res => {
        if (res.data.status) {
            this.user = res.data.data;
        }
    })
    .catch(error => {
        console.log(error);
    });
},

前端第二次重构

2018.7.8 Github地址:elm2.0

目录结构

讲道理这次重构的有点过分

├── src                // 生产目录
│   └── axios           // axios操作
|         ├──base       // axios模板
|         |    ├──base.js     //axios基类
|         |    └──setting.js  //状态码
|         └── user
|               ├──cache.js     //请求函数
|               └──config.js    //配置信息
|
|   ├── base           //vue模板
│   ├── components     // 组件
|   |     ├──common    //公共组件
|   |     └──admin
|   |          ├── ui.vue             // 输出组件
|   |          ├── component.html     // template
|   |          ├── component.js       // script
|   |          └── component.less     // style
|   |  
│   ├── router         // 路由
│   ├── store          // vuex状态管理器
│	├── App.vue        // 首页
│   └── main.js        // Webpack 预编译入口

第一次的重构虽然已经将axios请求和页面分离开来了,但是每次请求后都要验证状态码,处理错误信息。

其实这完全没有必要每个页面都来一下,这些公共操作可以一起放在axios的基类

import axios from 'axios'
import setting from './setting'

let httpURL = "http://www.xuguobin.club/api/elm/" //这是我服务器的api接口
let localURL = 'http://localhost/api/elm/';     //这是本地koa2的api接口

axios.defaults.baseURL = httpURL;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

export default class AxiosCache {
	constructor() {
		this.__config = {}
		this.__setting = setting;
		this.init();
	}

	init() {
		this.doFlushSetting(CACHE_KEY, )
	}

	doFlushSetting(key, conf) {
		if (!key && typeof key !== 'string') {
			return
		}
		this.__config[key] = conf
	}
	
	/*判断状态码*/
	resultJudge(code) {
		return code
	}
	
	/*发送请求数据*/
	sendRequest(key, options) {
		let send = this.__config[this.settingKey][key];
		let self = this;
		let baseURL = send.url;
		send.method == 'get'
			? options.data && (send.url += options.data)
			: send.data = options.data
		axios(send)
			.then(function (response) {
				send.url = baseURL;
				if (self.resultJudge(response.data.status)) {
					options.success(response.data.data)
				} else {
					options.fail
						? options.fail(response.data.data)
						: self.handleErrorCase(response.data.status)
				}
			}).catch(function (error) {
				self.handleErrorCase(error)
			})
	}
	
	/*处理错误信息*/
	handleErrorCase(error) {
		if (typeof error == 'Number') {
			console.log(error)
		} else {
			alert(error)
		}
	}
}

而发送请求的时候,只需要这样

getUSer: function() {
     this.userCache.getUser({
         success: res => this.user = res
     })
},

是不是很简洁。这样做,又进一步的解耦了axios操作,你可以对比我github上的elm1和elm2两个版本结构,一定会有所收获。

前端的架构追求就是尽量 完美复用和解耦

前端第三次重构

2019.4.14 Github地址:elm3.0

可能并没有优化多少,但是提供一个思路

elm2版本中的axios处理逻辑是:封装一个AxiosCache的基类,里面有一些共用的方法,其他axios对象则继承这个基类,在各自页面中进行实例化,如果这个页面需要请求用户相关接口则实例化一个UserCache,可如果另一个页面也有用到则再实例化一个UserCache,重复的实例化让我觉得性能上的浪费。所以我想到了另外一种不使用类继承和实例化的axios的结构

elm3版本结构如下

├── axios           // axios操作
|         ├──index.js   // axios配置表
|         ├──base       // axios公共部分
|         |    ├──index.js     //axios公共方法
|         |    └──setting.js  //状态码
|         └── user
|               ├──cache.js     //请求函数
|               └──config.js    //配置信息

elm3版本中的axios处理逻辑是这样的:在axios目录下的index.js中引入各Cache的配置信息和状态码构成一个配置表

// 引入状态码表
import setting from './base/setting'
// 引入config配置表
import userConfig from './user/config';
import goodConfig from './good/config';
import sellerConfig from './seller/config';

export default {
    __setting: setting,
    __config:  {
        'user_cache_key': userConfig,
        'good_cache_key': goodConfig,
        'seller_cache_key': sellerConfig
    }
}

将这个配置表导入赋值给Vue(main.js):

import axios from '@/axios' 
Vue.$axios = axios;

base目录下的index.js中只保留公共方法 (这里我使用了Promise)

import axios from 'axios'
import Vue from 'vue'

let httpURL = "http://www.xuguobin.club/api/elm/" //这是我服务器的api接口
let localURL = 'http://localhost/api/elm/';     //这是本地koa2的api接口

axios.defaults.baseURL = httpURL;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

// 判断状态码
function resultJudge(code) {
	return code
}
// 处理错误公共方法
function handleErrorCase(error) {
	if (typeof error == 'Number') {
		console.log(error)
	} else {
		alert(error)
	}
}
// 发送请求
export default function sendRequest(settingKey, key, data) {
	let send = Vue.$axios.__config[settingKey][key];
	let baseURL = send.url;
	send.method == 'get'
		? data && (send.url += data)
		: send.data = data;
	return new Promise((resolve,reject) => {
		axios(send)
		.then((res)=>{
			send.url = baseURL;
			resultJudge(res.data.status) ? resolve(res.data.data) : reject(res.data.data);
		}).catch((err)=>{
			handleErrorCase(err)
		});
	});
}

而在其他的Cache页面的请求函数

import sendRequest from '../base';
const CACHE_KEY = 'good_cache_key'

export default {
	getGood(options) {
		return sendRequest(CACHE_KEY, 'good-getGood',options);
	},
	getGoods(options) {
		return sendRequest(CACHE_KEY, 'good-getGoods',options);
	}
}

这样做的好处是在页面第一次加载时就制定好了一张ajax请求的信息表并存储在Vue.$axios中,后期页面使用时不需求实例化,直接按表索引就好了

前端第四次重构

2019.9.16 Github地址:elm3.1

之前讨论过elm2.0版本使用继承架构带来的一个问题:重复的实例化造成性能上的浪费。所以用另外一种不使用类继承和实例化的axios的配置表结构,即elm3.0版本。其实问题完全可以用单例模式来解决嘛,是我太菜了当时没想到。

以GoodCache为例

import BaseCache from '../base/base';
import config from './config';
const CACHE_KEY = 'good_cache_key';

class GoodCache extends BaseCache {
  constructor() {
    super();
    this.settingKey = CACHE_KEY;
  }

  init() {
    this.doFlushSetting(CACHE_KEY, config);
  }

  getGood(options) {
    this.sendRequest('good-getGood', options);
  }

  getGoods(options) {
    this.sendRequest('good-getGoods', options);
  }
}

export default (function() {
  let goodCache;
  return function() {
    if (!goodCache) {
      goodCache = new GoodCache();
    }
    return goodCache;
  };
})();

在其他页面中引入使用:

import GoodCache from '@/axios/good/cache';

created: function() {
    this.goodCache = GoodCache();
    this.getGood();
}

无论在几个页面调用了GoodCache里的API,GoodCache只会实例化一次,全局只存在一个goodCache对象。

前端第五次重构?

未完待续...... 你有好的架构也欢迎你fork我的master版本! ^_^