Vue+webpack項目配置便于維護的目錄結(jié)構(gòu)教程詳解
新建項目的時候創(chuàng)建合理的目錄結(jié)構(gòu)便于后期的維護是很重要
環(huán)境:vue、webpack
目錄結(jié)構(gòu):
項目子目錄結(jié)構(gòu)

子目錄結(jié)構(gòu)都差不多,主要目錄是在src下面操作
src目錄結(jié)構(gòu)

src/common 目錄
主要用來存放公共的文件

src/components
主要用來存放公共的組件
src/config
用來存放配置文件,文件目錄如下

src/config/index.js 配置目錄入口文件
import api from './website'
// 當前平臺
export const HOST_PLATFORM = 'WEB'
// 當前環(huán)境
export const NODE_ENV = process.env.NODE_ENV || 'prod'
// 是否開啟監(jiān)控
export const MONITOR_ENABLE = true
// 路由默認配置
export const ROUTER_DEFAULT_CONFIG = {
// mode: 'history',
waitForData: true,
transitionOnLoad: true
}
// axios 默認配置
export const AXIOS_DEFAULT_CONFIG = {
timeout: 20000,
maxContentLength: 2000,
headers: {}
}
// vuex 默認配置
export const VUEX_DEFAULT_CONFIG = {
strict: process.env.NODE_ENV !== 'production'
}
// API 默認配置
export const API_DEFAULT_CONFIG = {
baseURL: api,
// 圖標地址
imgUrl: `${api}/api/system/icon.do?name=`,
// 菜單圖標地址
menuImgUrl: `${api}/`,
dicomUrl: `${api}/testDICOM/`,
// 請求參數(shù)格式 json/form-data
isJSON: true,
// 請求加載效果, 支持element-ui所有參數(shù)配置
loading: { text: '加載中' },
// 是否開啟mock
mock: false,
// 是否開啟debug
debug: false,
// 定義全局變量
ippid: 'test'
}
export const CONSOLE_REQUEST_ENABLE = true // 開啟請求參數(shù)打印
export const CONSOLE_RESPONSE_ENABLE = false // 開啟響應參數(shù)打印
export const CONSOLE_ROUTER_ENABLE = false // 打印路由信息
export const CONSOLE_MONITOR_ENABLE = true // 監(jiān)控記錄打印
src/config/website.js 動態(tài)配置ip文件
/**
* 動態(tài)匹配api接口地址
*/
const website = [
{
web: 'localhost:9000',
api: '//192.168.0.170:8080/xhhms',
env: 'dev'
},
{
web: '127.0.0.1:8000',
api: '//192.168.0.149:8080/xhhms',
env: 'dev'
}
]
let matchApi = website.filter(item => new RegExp(item.web).test(location.href))
if (matchApi.length > 1) {
console.error(`${location.href}: 該站點映射了多個api地址${matchApi.map(item => item.api).join(',')},默認選取第一個匹配項`)
}
export default matchApi[0].api
src/config/interceptors目錄
攔截器配置
src/config/interceptors/axios.js
import router from 'Plugins/router'
import { CONSOLE_REQUEST_ENABLE, CONSOLE_RESPONSE_ENABLE } from '../index.js'
import { Toast, Indicator } from 'mint-ui'
import store from 'Store'
import Qs from 'qs'
/**
* 請求攔截器(成功)
* @param {object} request 請求對象
* @return {object} request 處理后的請求對象
*/
export function requestSuccessFunc(request) {
CONSOLE_REQUEST_ENABLE &&
console.info('requestInterceptorFunc', `url: ${request.url}`, request)
// 自定義請求攔截邏輯,可以處理權(quán)限,請求發(fā)送監(jiān)控等
// console.log(request.url)
// if (localStorage.getItem('token') === null && request.url.indexOf('login') === -1) {
// console.log('[*] 當前用戶沒有登錄!!')
// router.push('/login')
// return false
// }
// 登錄token攜帶
request.headers['X-AUTH-TOKEN'] = localStorage.getItem('token')
// 兼容性寫法,如果request里邊沒得site_code 就用全局site_code
let publicParams = {
orgCode: sessionStorage.getItem('orgCode'),
menuId: sessionStorage.getItem('currentMenuId')
}
/**
* @author wucheshi
* @time 2018-08-13
* @description 需求變動,網(wǎng)站code從本地siteCodeList 這個字段來
*/
let siteCodeList = sessionStorage.getItem('siteCodeList')
// !request.data.site_code && (publicParams = Object.assign({ site_code: store.state.currentSite.code }, publicParams))
!request.data.site_code && !request.noSiteCode && (publicParams = Object.assign({ site_code: siteCodeList }, publicParams))
/**
* @author wucheshi
* @time 2018-08-13
* @description 單表操作接口不需要傳遞sitecode
*/
// 兼容單表操作傳遞site_code
// if (request.data.condition && !request.noSiteCode) {
// console.log(siteCodeList, 11111)
// if (request.data.condition.findIndex(item => item.name === 'site_code') === -1) {
// request.data.condition.push({ name: 'site_code', value: siteCodeList })
// } else {
// request.data.condition.find(item => item.name === 'site_code').value = siteCodeList
// }
// }
let newData
// 判斷是否是formdata類型
if (Object.prototype.toString.call(request.data) === '[object FormData]') {
// 合并formdata格式公共參數(shù)
Object.keys(publicParams).forEach(key => {
request.data.append(key, publicParams[key])
})
newData = request.data
} else {
// 合并公共參數(shù)
newData = Object.assign(request.data, publicParams)
// 判斷是否采用json格式提交參數(shù)
!request.isJSON && (newData = Qs.stringify(newData))
}
// 不同提交參數(shù)方式給不同的字段賦值
if (request.method.toUpperCase() === 'POST') {
request.data = newData
} else if (request.method.toUpperCase() === 'GET') {
request.params = newData
}
// 加載效果
request.loading && Indicator.open(request.loading)
// 輸出請求數(shù)據(jù)
CONSOLE_REQUEST_ENABLE &&
console.info(`%c
請求接口地址:${request.url}
請求接口名稱:${request.desc}
請求參數(shù)JSON:
${JSON.stringify(request.data, '', 2)}
`, 'color: #f60')
return request
}
/**
* 請求攔截器(失敗)
* @param {object} requestError 請求報錯對象
* @return {object} 返回promise對象
*/
export function requestFailFunc(requestError) {
// 自定義發(fā)送請求失敗邏輯,斷網(wǎng),請求發(fā)送監(jiān)控等
return Promise.reject(requestError)
}
// 你就是個sx
/**
* 響應攔截器(成功)
* @param {object} responseObj 響應對象
*/
export function responseSuccessFunc(responseObj) {
// 自定義響應成功邏輯,全局攔截接口,根據(jù)不同業(yè)務做不同處理,響應成功監(jiān)控等
// console.log(typeof (responseObj.data))
// // 判斷string是否包含 java字段 說明error
// if (typeof (responseObj.data) === 'string' || responseObj.data.indexOf('java') !== -1) {
// console.log('[*] token錯誤')
// this.$router.push('/login')
// }
// 加載效果
Indicator.close()
// 響應對象
let resData =
typeof responseObj.data === 'object'
? responseObj.data
: JSON.parse(responseObj.data)
let { status, message } = resData
// 輸出響應體
CONSOLE_RESPONSE_ENABLE && console.info(responseObj)
// 輸出返回JSON數(shù)據(jù)
CONSOLE_RESPONSE_ENABLE &&
console.info(`%c
響應接口地址: ${responseObj.config.url}
響應接口描述: ${responseObj.config.desc}
響應數(shù)據(jù)JSON:
${JSON.stringify(resData, '', 2)}
`, 'color: blue')
// 自定義處理業(yè)務邏輯
if (responseObj.config.customErrorHandle) {
return resData
}
// 統(tǒng)一邏輯處理
switch (+status) {
case 0: // 常規(guī)錯誤
Toast(message)
break
case 1: // 如果業(yè)務成功,直接進成功回調(diào)
return resData
case 401: // 登錄失效
store.commit('DELETE_USER_INFO')
router.push({ path: '/login', redirect: router.app._route.fullPath })
Toast(message)
break
default:
// 業(yè)務中還會有一些特殊 code 邏輯,我們可以在這里做統(tǒng)一處理,也可以下方它們到業(yè)務層
// !responseObj.config.noShowDefaultError && GLOBAL.vbus.$emit('global.$dialog.show', resData.msg);
return Promise.reject(resData)
}
}
/**
* 響應攔截器(失敗)
* @param {object} responseError 響應報錯對象
* @return {object} 返回promise對象
*/
export function responseFailFunc(responseError) {
// 響應失敗,可根據(jù) responseError.message 和 responseError.response.status 來做監(jiān)控處理
// ...
// 加載效果
Indicator.close()
// 錯誤碼處理
// console.log(responseError.response)
if (typeof (responseError.response) === 'undefined') {
return false
}
switch (responseError.response.status) {
case 401:
console.error('401錯誤')
store.commit('DELETE_USER_INFO')
router.push({ path: '/login', redirect: router.app._route.fullPath })
store.state.user.username && Toast('登錄超時')
break
case 403:
console.error('403錯誤')
router.push({ path: '/403' })
break
case 500:
console.error('500錯誤')
router.push({ path: '/500' })
break
}
return Promise.reject(responseError)
}
src/config/interceptors/index.js
import {requestSuccessFunc, requestFailFunc, responseSuccessFunc, responseFailFunc} from './axios'
import {routerBeforeEachFunc} from './router'
export default {
requestSuccessFunc,
requestFailFunc,
responseSuccessFunc,
responseFailFunc,
routerBeforeEachFunc
}
src/config/interceptors/router.js
/**
* 路由beforeach攔截器
*/
import {CONSOLE_ROUTER_ENABLE} from '../index'
export function routerBeforeEachFunc (to, from, next) {
// 打印路由數(shù)據(jù)
CONSOLE_ROUTER_ENABLE && console.info(`%c
路由to:
fullPath: ${to.fullPath},
query: ${JSON.stringify(to.query, '', 2)},
meta: ${JSON.stringify(to.meta, '', 2)}
路由from:
fullPath: ${from.fullPath}
`, 'color: green;font-weight: bold;')
// 登錄狀態(tài)驗證
if (to.meta.requireLogin) {
(localStorage.getItem('token')) ? next() : next({path: '/login', query: { redirect: to.fullPath }})
return
}
// 路由重定向
// if (to.query.route) {
// let newQuery = Object.assign({}, to.query)
// delete newQuery.route
// next({
// path: `${to.query.route.indexOf('/') === 0 ? '' : '/'}${to.query.route}`,
// query: newQuery
// })
// return
// }
// console.log(to, from)
// 防止死循環(huán)
if (to.fullPath === from.fullPath) return
// 404錯誤
if (!to.name) {
next('/404')
return
}
next()
}
src/locale目錄
國際化配置,這個百度一下就行
src/mixin目錄
引入配置文件,定義部分全局變量,名字自己定義

src/mixin/index.js
import Vue from 'vue'
import { API_DEFAULT_CONFIG } from 'Config'
Vue.mixin({
computed: {
// 圖片根地址
imgUrl () {
return API_DEFAULT_CONFIG.imgUrl
},
baseUrl () {
return API_DEFAULT_CONFIG.baseURL
},
ippid () {
return API_DEFAULT_CONFIG.ippid
},
dicomUrl () {
return API_DEFAULT_CONFIG.dicomUrl
}
}
})
src/pages目錄
主要的頁面文件,目錄結(jié)構(gòu)主要按照層次結(jié)構(gòu)來分。
ex:該頁面主要跟醫(yī)生相關(guān),主要包含云搜索(cloud)、個人中心(myCenter)、工作中心(workCenter)、搜索(serach)、同理子層級也同樣區(qū)分、目錄結(jié)構(gòu)如下

至于公共頁面可以放在common文件目錄下,也可以擺在文件夾外面。
src/plugins目錄
也是配置文件目錄

src/plugins/api.js
import axios from './axios'
import _pick from 'lodash/pick'
import _assign from 'lodash/assign'
import _isEmpty from 'lodash/isEmpty'
import { assert } from 'Utils/tools'
import { API_DEFAULT_CONFIG } from 'Config'
import API_CONFIG from 'Service/api'
class MakeApi {
constructor (options) {
this.api = {}
this.options = Object.assign({}, options)
this.apiBuilder(options)
}
apiBuilder ({
config = {}
}) {
Object.keys(config).map(namespace => {
this._apiSingleBuilder({
namespace,
config: config[namespace]
})
})
}
_apiSingleBuilder ({
namespace,
config = {}
}) {
config.forEach(api => {
const { methodsName, desc, params, method, path, mockPath } = api
let { mock, mockBaseURL, baseURL, debug, isJSON, loading } = this.options
let url = mock ? (mockBaseURL + mockPath) : (baseURL + path)
debug && assert(methodsName, `${url} :接口methodsName屬性不能為空`)
debug && assert(url.indexOf('/') === 0, `${url} :接口路徑path,首字符應為/`)
Object.defineProperty(this.api, methodsName, {
value (outerParams, outerOptions) {
let allowtParam = (outerOptions && outerOptions.allowParams) || {}
let _data = (outerOptions && outerOptions.isFormData) ? outerParams : _isEmpty(outerParams) ? params : _pick(_assign({}, params, outerParams), Object.keys(Object.assign(params, allowtParam)))
return axios(_assign({
url,
desc,
method,
isJSON,
loading
}, outerOptions, { data: _data }))
}
})
})
}
}
export default new MakeApi({
config: API_CONFIG,
...API_DEFAULT_CONFIG
})['api']
src/plugins/axios.js
import axios from 'axios'
import {AXIOS_DEFAULT_CONFIG} from 'Config/index'
import {requestSuccessFunc, requestFailFunc, responseSuccessFunc, responseFailFunc} from 'Config/interceptors/axios'
let axiosInstance = {}
axiosInstance = axios.create(AXIOS_DEFAULT_CONFIG)
// 注入請求攔截
axiosInstance
.interceptors.request.use(requestSuccessFunc, requestFailFunc)
// 注入失敗攔截
axiosInstance
.interceptors.response.use(responseSuccessFunc, responseFailFunc)
export default axiosInstance
src/plugins/inject.js
import axios from './axios'
import api from './api'
// GLOBAL.ajax = axios
export default {
install: (Vue, options) => {
Vue.prototype.$api = api
Vue.prototype.$ajax = axios
// 需要掛載的都放在這里
}
}
src/plugins/router.js
import Vue from 'vue'
import Router from 'vue-router'
import ROUTES from 'Routes'
import {ROUTER_DEFAULT_CONFIG} from 'Config/index'
import {routerBeforeEachFunc} from 'Config/interceptors/router'
Vue.use(Router)
// 注入默認配置和路由表
let routerInstance = new Router({
...ROUTER_DEFAULT_CONFIG,
routes: ROUTES
})
// 注入攔截器
routerInstance.beforeEach(routerBeforeEachFunc)
export default routerInstance
src/router目錄
路由配置文件目錄,同理按照頁面的層次結(jié)構(gòu)來,結(jié)構(gòu)如下

我們來看src/router/index.js 和 src/common/index.js 即可
src/common/index.js
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('Pages/login'),
meta: {
require: true,
title: '登錄'
}
},
{
path: '/register',
name: 'register',
component: () => import('Pages/register'),
meta: {
require: true,
title: '注冊'
}
},
{
path: '/404',
name: '404',
component: () => import('Pages/error/404.vue'),
meta: {
require: true,
title: '404'
}
},
{
path: '/500',
name: '500',
component: () => import('Pages/error/500.vue'),
meta: {
require: true,
title: '500'
}
},
{
path: '/403',
name: '403',
component: () => import('Pages/error/403.vue'),
meta: {
require: true,
title: '403'
}
}
]
export default routes
src/router/index.js
import common from './common'
import doctor from './doctor'
import patient from './patient'
import test from './test'
const route = [
{
path: '/',
redirect: '/login'
},
{
path: '/checkrecord',
name: 'checkrecord',
component: () => import('Pages/checkrecord.vue'),
meta: {
require: true,
title: '檢查記錄'
}
},
{
path: '/report',
name: 'report',
component: () => import('Pages/report.vue'),
meta: {
require: true,
title: '心電圖報告'
}
},
{
path: '/opinion',
name: 'opinion',
component: () => import('Pages/opinion.vue'),
meta: {
require: true,
title: '意見'
}
},
{
path: '/bind',
name: 'bind',
component: () => import('Pages/bind.vue'),
meta: {
require: true,
title: '綁定'
}
},
...common,
...doctor,
...patient,
...test
]
export default route
把所有的路由文件掛載進去。
src/service 目錄
接口配置文件目錄,根據(jù)頁面來定義文件

同理我們只看src/service/api/index.js 和src/service/api/login.js、src/pages/login/index.vue以及頁面如何調(diào)用接口即可。
src/service/api/login.js
先定義好login接口
const login = [
{
methodsName: 'loginByPhone', // 方法名
method: 'POST',
desc: '登錄',
path: '/rest/app/login', // 接口路徑
mockPath: '/rest/app/login',
params: { // 參數(shù)配置 這里需要注意,只有配置的這些參數(shù)才能通過接口,所以需要傳遞的參數(shù)都要在這里配置
phone: 1,
password: 2,
code: 3,
codeid: '',
clientid: ''
}
},
{
methodsName: 'login',
method: 'POST',
desc: '登錄',
path: '/rest/interfacesLoginController/login',
mockPath: '/rest/interfacesLoginController/login',
params: {
username: 1,
password: 2,
code: 3,
codeid: '',
clientid: ''
}
},
{
methodsName: 'checkcode',
method: 'POST',
desc: '驗證提取碼',
path: '/rest/app/medical/checksharecode',
mockPath: '/rest/app/medical/checksharecode',
params: {
sharecode: '',
id: ''
}
},
{
methodsName: 'getCode',
method: 'POST',
desc: '獲取驗證碼',
path: '/rest/interRandomCodeController/gererateRandomCode',
mockPath: '',
params: {
}
},
{
methodsName: 'getPublicKey',
method: 'POST',
desc: '獲取公鑰',
path: '/rest/interRandomCodeController/clientIdAndPublicKey',
mockPath: '',
params: {
}
}
]
export default login
src/service/api/index.js
掛載所有定義的接口文件
import login from './login'
import workcenter from './workcenter'
import detail from './detail'
import register from './register'
import doctorpc from './doctorpc'
import patientpc from './patientpc'
import checklist from './checklist'
export default {
login,
workcenter,
detail,
register,
doctorpc,
patientpc,
checklist
}
src/pages/login/index.vue
this.$api.login( params).then(data => {
})
// 這樣調(diào)用登陸接口 this.$api.方法名(參數(shù)).then(res=>{})
// 方法名定義不能重名
其它目錄
這些目錄還是包含很多東西,用戶的信息保存,主體,工具函數(shù)這些,就不多說了。

對于項目的維護還是需要看重,后期維護方便也便于管理。
總結(jié)
以上所述是小編給大家?guī)淼腣ue+webpack項目配置便于維護的目錄結(jié)構(gòu)的相關(guān)知識,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!
相關(guān)文章
解決VUEX刷新的時候出現(xiàn)數(shù)據(jù)消失
這篇文章主要介紹了解決VUEX刷新的時候出現(xiàn)數(shù)據(jù)消失,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07
詳解Vue如何實現(xiàn)顏色選擇與調(diào)色板功能
顏色選擇和調(diào)色板是Web開發(fā)中常用的功能,Vue作為一個流行的JavaScript框架,可以方便地實現(xiàn)顏色選擇和調(diào)色板功能,本文講講如何在Vue中進行顏色選擇和調(diào)色板吧2023-06-06

