Vue+webpack项目配置便于维护的目录结构教程详解


Posted in Javascript onOctober 14, 2018

新建项目的时候创建合理的目录结构便于后期的维护是很重要

环境:vue、webpack

目录结构:

项目子目录结构

Vue+webpack项目配置便于维护的目录结构教程详解

子目录结构都差不多,主要目录是在src下面操作

src目录结构

Vue+webpack项目配置便于维护的目录结构教程详解

src/common 目录

主要用来存放公共的文件

Vue+webpack项目配置便于维护的目录结构教程详解

src/components

主要用来存放公共的组件

src/config

用来存放配置文件,文件目录如下

Vue+webpack项目配置便于维护的目录结构教程详解

src/config/index.js 配置目录入口文件

import api from './website'

// 当前平台
export const HOST_PLATFORM = 'WEB'
// 当前环境
export const NODE_ENV = process.env.NODE_ENV || 'prod'

// 是否开启监控
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/`,
 // 请求参数格式 json/form-data
 isJSON: true,
 // 请求加载效果, 支持element-ui所有参数配置
 loading: { text: '加载中' },
 // 是否开启mock
 mock: false,
 // 是否开启debug
 debug: false,
 // 定义全局变量
 ippid: 'test'
}

export const CONSOLE_REQUEST_ENABLE = true // 开启请求参数打印
export const CONSOLE_RESPONSE_ENABLE = false // 开启响应参数打印
export const CONSOLE_ROUTER_ENABLE = false // 打印路由信息
export const CONSOLE_MONITOR_ENABLE = true // 监控记录打印

src/config/website.js 动态配置ip文件

/**
 * 动态匹配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)
 // 自定义请求拦截逻辑,可以处理权限,请求发送监控等
 // 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 需求变动,网站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格式公共参数
  Object.keys(publicParams).forEach(key => {
   request.data.append(key, publicParams[key])
  })
  newData = request.data
 } else {
  // 合并公共参数
  newData = Object.assign(request.data, publicParams)

  // 判断是否采用json格式提交参数
  !request.isJSON && (newData = Qs.stringify(newData))
 }

 // 不同提交参数方式给不同的字段赋值
 if (request.method.toUpperCase() === 'POST') {
  request.data = newData
 } else if (request.method.toUpperCase() === 'GET') {
  request.params = newData
 }

 // 加载效果
 request.loading && Indicator.open(request.loading)

 // 输出请求数据
 CONSOLE_REQUEST_ENABLE &&
 console.info(`%c
请求接口地址:${request.url}

请求接口名称:${request.desc}

请求参数JSON: 

${JSON.stringify(request.data, '', 2)}

`, 'color: #f60')

 return request
}

/**
 * 请求拦截器(失败)
 * @param {object} requestError 请求报错对象
 * @return {object} 返回promise对象
 */
export function requestFailFunc(requestError) {
 // 自定义发送请求失败逻辑,断网,请求发送监控等
 return Promise.reject(requestError)
}
// 你就是个sx
/**
 * 响应拦截器(成功)
 * @param {object} responseObj 响应对象
 */
export function responseSuccessFunc(responseObj) {
 // 自定义响应成功逻辑,全局拦截接口,根据不同业务做不同处理,响应成功监控等
 // 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数据
 CONSOLE_RESPONSE_ENABLE &&
  console.info(`%c
响应接口地址: ${responseObj.config.url}

响应接口描述: ${responseObj.config.desc}

响应数据JSON:

${JSON.stringify(resData, '', 2)}
  `, 'color: blue')

 // 自定义处理业务逻辑
 if (responseObj.config.customErrorHandle) {
  return resData
 }

 // 统一逻辑处理
 switch (+status) {
  case 0: // 常规错误
   Toast(message)
   break
  case 1: // 如果业务成功,直接进成功回调
   return resData
  case 401: // 登录失效
   store.commit('DELETE_USER_INFO')
   router.push({ path: '/login', redirect: router.app._route.fullPath })
   Toast(message)
   break
  default:
   // 业务中还会有一些特殊 code 逻辑,我们可以在这里做统一处理,也可以下方它们到业务层
   // !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) {
 // 响应失败,可根据 responseError.message 和 responseError.response.status 来做监控处理
 // ...
 // 加载效果
 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) {
 // 打印路由数据
 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;')

 // 登录状态验证
 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)

 // 防止死循环
 if (to.fullPath === from.fullPath) return

 // 404错误
 if (!to.name) {
  next('/404')
  return
 }

 next()
}

src/locale目录

国际化配置,这个百度一下就行

src/mixin目录

引入配置文件,定义部分全局变量,名字自己定义

Vue+webpack项目配置便于维护的目录结构教程详解

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目录

主要的页面文件,目录结构主要按照层次结构来分。

ex:该页面主要跟医生相关,主要包含云搜索(cloud)、个人中心(myCenter)、工作中心(workCenter)、搜索(serach)、同理子层级也同样区分、目录结构如下

Vue+webpack项目配置便于维护的目录结构教程详解

至于公共页面可以放在common文件目录下,也可以摆在文件夹外面。

src/plugins目录

也是配置文件目录

Vue+webpack项目配置便于维护的目录结构教程详解

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目录

路由配置文件目录,同理按照页面的层次结构来,结构如下

Vue+webpack项目配置便于维护的目录结构教程详解

我们来看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 目录

接口配置文件目录,根据页面来定义文件

Vue+webpack项目配置便于维护的目录结构教程详解

同理我们只看src/service/api/index.js 和src/service/api/login.js、src/pages/login/index.vue以及页面如何调用接口即可。

src/service/api/login.js

先定义好login接口

const login = [
 {
  methodsName: 'loginByPhone',  // 方法名
  method: 'POST',
  desc: '登录',
  path: '/rest/app/login',   // 接口路径
  mockPath: '/rest/app/login', 
  params: {   // 参数配置 这里需要注意,只有配置的这些参数才能通过接口,所以需要传递的参数都要在这里配置
   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 => {
  
})
// 这样调用登陆接口  this.$api.方法名(参数).then(res=>{}) 
// 方法名定义不能重名

其它目录

这些目录还是包含很多东西,用户的信息保存,主体,工具函数这些,就不多说了。

Vue+webpack项目配置便于维护的目录结构教程详解

对于项目的维护还是需要看重,后期维护方便也便于管理。

总结

以上所述是小编给大家带来的Vue+webpack项目配置便于维护的目录结构的相关知识,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Javascript 相关文章推荐
网页设计常用的一些技巧
Dec 22 Javascript
js实现权限树的更新权限时的全选全消功能
Feb 17 Javascript
JavaScript 面向对象的 私有成员和公开成员
May 13 Javascript
基于BootStrap Metronic开发框架经验小结【二】列表分页处理和插件JSTree的使用
May 12 Javascript
bootstrap table单元格新增行并编辑
May 19 Javascript
jQuery实现手机号正则验证输入及自动填充空格功能
Jan 02 jQuery
vue cli 全面解析
Feb 28 Javascript
vue mint-ui tabbar变组件使用
May 04 Javascript
vue中过滤器filter的讲解
Jan 21 Javascript
微信小程序蓝牙连接小票打印机实例代码详解
Jun 03 Javascript
vue-quill-editor插入图片路径太长问题解决方法
Jan 08 Vue.js
基于JavaScript实现随机点名器
Feb 25 Javascript
单页面vue引入百度统计的使用方法示例详解
Oct 13 #Javascript
详解解决Vue相同路由参数不同不会刷新的问题
Oct 12 #Javascript
详解webpack loader和plugin编写
Oct 12 #Javascript
深入理解Angularjs 脏值检测
Oct 12 #Javascript
vue中render函数的使用详解
Oct 12 #Javascript
详解Vue的常用指令v-if, v-for, v-show,v-else, v-bind, v-on
Oct 12 #Javascript
Vue插值、表达式、分隔符、指令知识小结
Oct 12 #Javascript
You might like
PHP中根据IP地址判断城市实现城市切换或跳转代码
2012/09/04 PHP
php使用Jpgraph绘制复杂X-Y坐标图的方法
2015/06/10 PHP
Laravel框架实现model层的增删改查(CURD)操作示例
2018/05/12 PHP
JavaScript 继承详解(一)
2009/07/13 Javascript
jquery实用代码片段集合
2010/08/12 Javascript
JQuery中使用Ajax赋值给全局变量异常的解决方法
2014/01/10 Javascript
Jquery实现由下向上展开效果的例子
2014/12/08 Javascript
JS获取Table中td值的方法
2015/03/19 Javascript
Javascript中arguments用法实例分析
2015/06/13 Javascript
Bootstrap图片轮播组件Carousel使用方法详解
2016/10/20 Javascript
JavaScript遍历Json串浏览器输出的结果不统一问题
2016/11/03 Javascript
概述javascript在Google IE中的调试技巧
2016/11/24 Javascript
vue interceptor 使用教程实例详解
2018/09/13 Javascript
对layui中table组件工具栏的使用详解
2019/09/19 Javascript
js实现数据导出为EXCEL(支持大量数据导出)
2020/03/31 Javascript
[01:16:50]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第一场 3月7日
2021/03/11 DOTA
Win7 64位下python3.6.5安装配置图文教程
2020/10/27 Python
python 读取文件并替换字段的实例
2018/07/12 Python
python traceback捕获并打印异常的方法
2018/08/31 Python
python读取有密码的zip压缩文件实例
2019/02/08 Python
python中报错"json.decoder.JSONDecodeError: Expecting value:"的解决
2019/04/29 Python
Python中的正则表达式与JSON数据交换格式
2019/07/03 Python
如何基于matlab相机标定导出xml文件
2020/11/02 Python
python 实现简单的计算器(gui界面)
2020/11/11 Python
Alpine安装Python3依赖出现的问题及解决方法
2020/12/25 Python
html5中地理位置定位api接口开发应用小结
2013/01/04 HTML / CSS
科颜氏美国官网:Kiehl’s美国
2017/01/31 全球购物
Vichy薇姿加拿大官网:法国药妆,全球专业敏感肌护肤领先品牌
2018/07/11 全球购物
Oakley西班牙官方商店:太阳眼镜和男女运动服
2019/04/26 全球购物
思想品德自我鉴定
2013/10/12 职场文书
自我评价是什么
2014/01/04 职场文书
幼儿园毕业典礼主持词
2014/03/21 职场文书
高中竞选班长演讲稿
2014/04/24 职场文书
2014年村官工作总结
2014/11/24 职场文书
2015年毕业生个人自荐书
2015/03/24 职场文书
python神经网络学习 使用Keras进行简单分类
2022/05/04 Python