欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-02-08 16:59:34
新建项目的时候创建合理的目录结构便于后期的维护是很重要 环境:vue、webpack 目录结构: 项目子目录结构 子目录结构都差不多,主要目录是在src下面操作...

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

环境: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项目配置便于维护的目录结构的相关知识,希望对大家有所帮助