当前位置:   article > 正文

vue项目开发目录架构_vue项目一般目录结构

vue项目一般目录结构

一.整体的目录介绍

1.目录架构

  1. - mock mock可以不需要后台,自动拦截ajax返回测试数据
  2. - public 公共目录
  3. - src   
  4. api                   用于存放网络请求文件的目录
  5. index.ts
  6. xxx目录
  7. assets              存放静态文件的目录
  8. components      存放自定义组件的目录
  9. filter                过滤器的使用(例如时间data格式化)
  10. index.ts
  11. icons                图标库引入
  12. svg  
  13. index.ts
  14. lang                  语言包引入(用于项目中多语言切换)
  15. en.js
  16. zh.js
  17. layout                每个页面的框架
  18. router                路由设置 
  19. index.ts
  20. modules        可以减少router下index整体的大小
  21. store                 vuex的目录
  22. modules      相应的文件存放
  23. getters          获取vuex变量get方式
  24. index.ts 
  25. utils                 工具目录
  26. views                页面目录
  27. App.vue 
  28. main.ts  
  29. permisson.ts      权限文件
  30. - .env.development 本地的环境变量
  31. - .env.production 线上的环境变量
  32. - vue.config.js          配置整体的vue项目
  33. - package.json        所有下载的依赖统一管理文件
  34. - README.md        项目介绍文档(一般上传源码管理器使用)

二.具体的代码

1. Vuex具体代码

1)  vuex具体架构

  1. const state = {
  2. isRefresh:false //初始化刷新 为false
  3. }
  4. const mutations = {
  5. SET_IS_REFRESH:(state: any, isRefresh: Boolean)=>{
  6. state.isRefresh=isRefresh
  7. }
  8. }
  9. const actions = {
  10. setRefresh: ({ commit }: any, isRefresh: Boolean) => {
  11. commit("SET_IS_REFRESH", isRefresh)
  12. }
  13. }
  14. export default {
  15. namespaced: true,
  16. state,
  17. mutations,
  18. actions
  19. }

2)vuex的getters

  1. const getters = {
  2. isRefresh:(state:any)=>state.refresh.isRefresh
  3. }
  4. export default getters

3)vuex注册

index.ts文件  在main.ts记住引入store

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. import getters from './getters'
  4. Vue.use(Vuex)
  5. // https://webpack.js.org/guides/dependency-management/#requirecontext
  6. const modulesFiles = require.context('./modules', true, /\.ts$/)
  7. const modules = modulesFiles.keys().reduce((modules: any, modulePath) => {
  8. // set './app.js' => 'app'
  9. const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  10. const value = modulesFiles(modulePath)
  11. modules[moduleName] = value.default
  12. return modules
  13. }, {})
  14. const store = new Vuex.Store({
  15. modules,
  16. getters,
  17. })
  18. export default store

2.permisson路由问题

permisson.ts   页面会反复调用 beforeEach,直到调用next()

router.beforeEach(to,from,next)   to 代表将要访问的路径   from 当前路径  next用于跳转页面

next({"path":"/"})指向路径后代表重定向  会再次访问beforeEach

  1. import router from './router'
  2. import { getToken, removeToken, setToken } from '@/utils/auth';
  3. import { Dialog } from 'vant';
  4. import store from './store';
  5. const whiteList = ['/login']
  6. const env = process.env.NODE_ENV
  7. if(env === "production") {
  8. // 应将layout中navList的to添加到此
  9. const navList = ['/socialRela', '/childrenStatus', '/work', '/education', '/info']
  10. router.beforeEach(async (to, from, next) => {
  11. const userId = getToken()
  12. if (userId) {
  13. } else {
  14. if (whiteList.indexOf(to.path) != -1) {
  15. next()
  16. } else {
  17. next({ path: '/login' })
  18. }
  19. }
  20. })
  21. }

 在路由权限这块,有一个笔记就是对于菜单权限的分配,判断角色是否存在,不存在的话获取角色后通过角色获取动态菜单,代码如下(代码仅供参考)

  1. 、、、路由判断代码
  2. const hasRoles = store.getters.roles && store.getters.roles.length > 0
  3. if (hasRoles) {
  4. next()
  5. } else {
  6. try {
  7. // get user info
  8. // note: roles must be a object array! such as: ['ROLE_ADMIN'] or ,['ROLE_DEVELOPER','ROLE_EDITOR']
  9. const { roles } = await store.dispatch('user/getInfo')
  10. // generate accessible routes map based on roles
  11. const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
  12. // dynamically add accessible routes
  13. router.addRoutes(accessRoutes)
  14. // hack method to ensure that addRoutes is complete
  15. // set the replace: true, so the navigation will not leave a history record
  16. next({ ...to, replace: true })
  17. } catch (error) {
  18. // remove token and go to login page to re-login
  19. await store.dispatch('user/resetToken')
  20. Message.error(error || 'Has Error')
  21. next(`/login?redirect=${to.path}`)
  22. NProgress.done()
  23. }
  24. }
  1. 、、、路由添加参考
  2. import { asyncRoutes, constantRoutes } from '@/router'
  3. /**
  4. * Use meta.role to determine if the current user has permission
  5. * @param roles
  6. * @param route
  7. */
  8. function hasPermission(roles, route) {
  9. if (route.meta && route.meta.roles) {
  10. return roles.some(role => route.meta.roles.includes(role))
  11. } else {
  12. return true
  13. }
  14. }
  15. /**
  16. * Filter asynchronous routing tables by recursion
  17. * @param routes asyncRoutes
  18. * @param roles
  19. */
  20. export function filterAsyncRoutes(routes, roles) {
  21. const res = []
  22. routes.forEach(route => {
  23. const tmp = { ...route }
  24. if (hasPermission(roles, tmp)) {
  25. if (tmp.children) {
  26. tmp.children = filterAsyncRoutes(tmp.children, roles)
  27. }
  28. res.push(tmp)
  29. }
  30. })
  31. return res
  32. }
  33. const state = {
  34. routes: [],
  35. addRoutes: []
  36. }
  37. const mutations = {
  38. SET_ROUTES: (state, routes) => {
  39. state.addRoutes = routes
  40. state.routes = constantRoutes.concat(routes)
  41. }
  42. }
  43. const actions = {
  44. generateRoutes({ commit }, roles) {
  45. return new Promise(resolve => {
  46. let accessedRoutes
  47. if (roles.includes('ADMIN')) {
  48. accessedRoutes = asyncRoutes || []
  49. } else {
  50. accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
  51. }
  52. commit('SET_ROUTES', accessedRoutes)
  53. resolve(accessedRoutes)
  54. })
  55. }
  56. }
  57. export default {
  58. namespaced: true,
  59. state,
  60. mutations,
  61. actions
  62. }

3.首先是svg引入 

在src目录创建icon目录    

icon目录分为svg(目录下是svg图片)、svgo.yml 、index.js

svgo.yml

  1. # replace default config
  2. # multipass: true
  3. # full: true
  4. plugins:
  5. # - name
  6. #
  7. # or:
  8. # - name: false
  9. # - name: true
  10. #
  11. # or:
  12. # - name:
  13. # param1: 1
  14. # param2: 2
  15. - removeAttrs:
  16. attrs:
  17. - 'fill'
  18. - 'fill-rule'

index.js  加载svg目录下文件 ,不需要一次次加载,对于代码解释,收藏里require.context

  1. import Vue from 'vue'
  2. import SvgIcon from '@/components/SvgIcon'// svg component
  3. // register globally
  4. Vue.component('svg-icon', SvgIcon)
  5. const req = require.context('./svg', false, /\.svg$/)
  6. const requireAll = requireContext => requireContext.keys().map(requireContext)
  7. requireAll(req)

在vue.config中

  1. chainWebpack(config) {
  2. config.plugins.delete('preload') // TODO: need test
  3. config.plugins.delete('prefetch') // TODO: need test
  4. // set svg-sprite-loader
  5. config.module
  6. .rule('svg')
  7. .exclude.add(resolve('src/assets/icons'))
  8. .end()
  9. config.module
  10. .rule('icons')
  11. .test(/\.svg$/)
  12. .include.add(resolve('src/assets/icons'))
  13. .end()
  14. .use('svg-sprite-loader')
  15. .loader('svg-sprite-loader')
  16. .options({
  17. symbolId: 'icon-[name]'
  18. })
  19. .end()
  20. }

然后全局注册SvgIcon组件

那么组件代码(组件定义完整后,最后记得在main.ts引入icon目录  但不需要vue对象里声明,lang需要)

  1. <template>
  2. <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  3. <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
  4. <use :xlink:href="iconName" />
  5. </svg>
  6. </template>
  7. <script>
  8. // doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
  9. import { isExternal } from '@/utils/validate'
  10. export default {
  11. name: 'SvgIcon',
  12. props: {
  13. iconClass: {
  14. type: String,
  15. required: true
  16. },
  17. className: {
  18. type: String,
  19. default: ''
  20. }
  21. },
  22. computed: {
  23. isExternal() {
  24. return isExternal(this.iconClass)
  25. },
  26. iconName() {
  27. return `#icon-${this.iconClass}`
  28. },
  29. svgClass() {
  30. if (this.className) {
  31. return 'svg-icon ' + this.className
  32. } else {
  33. return 'svg-icon'
  34. }
  35. },
  36. styleExternalIcon() {
  37. return {
  38. mask: `url(${this.iconClass}) no-repeat 50% 50%`,
  39. '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
  40. }
  41. }
  42. }
  43. }
  44. </script>
  45. <style scoped>
  46. .svg-icon {
  47. width: 1em;
  48. height: 1em;
  49. vertical-align: -0.15em;
  50. fill: currentColor;
  51. overflow: hidden;
  52. }
  53. .svg-external-icon {
  54. background-color: currentColor;
  55. mask-size: cover!important;
  56. display: inline-block;
  57. }
  58. </style>

4.lang 语言包

在src创建lang文件目录

en.js tw.js zh.js  

其中一个文件的代码

  1. export default {
  2. route: {
  3. "language":"汉语"
  4. }
  5. }

index.js需要下载vue-i18n

  1. import Vue from 'vue'
  2. import VueI18n from 'vue-i18n'
  3. //import Cookies from 'js-cookie'
  4. import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
  5. import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
  6. import elementTwLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang
  7. import enLocale from './en'
  8. import zhLocale from './zh'
  9. import twLocale from './tw'
  10. Vue.use(VueI18n)
  11. const messages = {
  12. en: {
  13. ...enLocale,
  14. ...elementEnLocale
  15. },
  16. zh: {
  17. ...zhLocale,
  18. ...elementZhLocale
  19. },
  20. tw: {
  21. ...twLocale,
  22. ...elementTwLocale
  23. }
  24. }
  25. export function getLanguage() {
  26. //此处代码如果有选择可以加
  27. //const chooseLanguage = Cookies.get('language')
  28. //if (chooseLanguage) return chooseLanguage
  29. // if has not choose language
  30. const language = (navigator.language || navigator.browserLanguage).toLowerCase()
  31. const locales = Object.keys(messages)
  32. for (const locale of locales) {
  33. if (language.indexOf(locale) > -1) {
  34. return locale
  35. }
  36. }
  37. return 'en'
  38. }
  39. const i18n = new VueI18n({
  40. // set locale
  41. // options: en | zh | es
  42. locale: getLanguage(),
  43. // set locale messages
  44. messages
  45. })
  46. export default i18n

在main.ts(由于i18n使用到了elementui)

  1. import i81n from './lang'
  2. Vue.use(Element, {
  3. size: Cookies.get('size') || 'medium', // set element-ui default size
  4. i18n: (key, value) => i18n.t(key, value)
  5. })
  6. new Vue({
  7. i18n,
  8. })

在使用页面中  $t('login.title')    判断页面是否有字段this.$te('route.' + title)

5.filter的使用

<span>{{ new Date(row.createTime) | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>

  1. /**
  2. * @param {number} time
  3. * @param {string} option
  4. * @returns {string}
  5. */
  6. export function formatTime(time, option) {
  7. if (('' + time).length === 10) {
  8. time = parseInt(time) * 1000
  9. } else {
  10. time = +time
  11. }
  12. const d = new Date(time)
  13. const now = Date.now()
  14. const diff = (now - d) / 1000
  15. if (diff < 30) {
  16. return '刚刚'
  17. } else if (diff < 3600) {
  18. // less 1 hour
  19. return Math.ceil(diff / 60) + '分钟前'
  20. } else if (diff < 3600 * 24) {
  21. return Math.ceil(diff / 3600) + '小时前'
  22. } else if (diff < 3600 * 24 * 2) {
  23. return '1天前'
  24. }
  25. if (option) {
  26. return parseTime(time, option)
  27. } else {
  28. return (
  29. d.getMonth() +
  30. 1 +
  31. '月' +
  32. d.getDate() +
  33. '日' +
  34. d.getHours() +
  35. '时' +
  36. d.getMinutes() +
  37. '分'
  38. )
  39. }
  40. }

全局注册在main.ts

  1. import * as filters from './filters'
  2. Object.keys(filters).forEach(key => {
  3.   Vue.filter(key, filters[key])
  4. })

6.api网络请求

1)封装axios

首先安装axios框架,

api->index.ts文件 (封装axios)

  1. import axios from 'axios'
  2. import { Dialog } from 'vant';
  3. import store from '@/store';
  4. // create an axios instance
  5. const service = axios.create({
  6. baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  7. timeout: 8000 // request timeout
  8. })
  9. // request interceptor
  10. service.interceptors.request.use(
  11. config => {
  12. //do something before request is sent
  13. if (store.getters.token) {
  14. config.headers['Authorization'] = 'Bearer ' + store.getters.token
  15. }
  16. return config
  17. },
  18. error => {
  19. // do something with request error
  20. console.log(error) // for debug
  21. return Promise.reject(error)
  22. }
  23. )
  24. // response interceptor
  25. service.interceptors.response.use(
  26. response => {
  27. const res = response.data
  28. // if the custom code is not 200, it is judged as an error.
  29. if (res.status && res.status !== 200) {
  30. Dialog.alert({
  31. message: res.message ,
  32. title: "提示"
  33. })
  34. return Promise.reject(new Error(res.message || 'Error'))
  35. } else {
  36. return res
  37. }
  38. },
  39. error => {
  40. Dialog.alert({
  41. message: error,
  42. title: "服务器提示"
  43. })
  44. return Promise.reject(error)
  45. }
  46. )
  47. export default service

2)具体的方法网络请求实现

api下新建多个目录,存放网络请求的方法,如

  1. import request from '@/utils/request'
  2. import qs from 'qs'
  3. export function getUserId(query: any) {
  4. return request({
  5. url: "/consumer/user",
  6. method: 'get',
  7. params: query
  8. })
  9. }
  10. // 新增用户基本信息
  11. export function Add(data: any) {
  12. return request({
  13. url: '/consumer/baseInfo/add',
  14. method: 'post',
  15. data: qs.stringify(data)
  16. })
  17. }

以下axios提交方式Content-type:form-data
在使用axios时,注意到配置选项中包含params和data两者

因为params是添加到url的请求字符串中的,用于get请求。 (后台@RequestParam)

而data是添加到请求体(body)中的, 用于post请求;(后台@RequestBody) 

使用qs:qs的作用将json用&相连并序列化处理,那么需要字符特殊处理的可以使用qs

7.mock的使用

1)mock的注册

mock下的index.ts,将所有mock文件注册,如果确认使用就在主目录下的main.ts引入(require('../mock');)

  1. // 首先引入Mock
  2. const Mock = require('mockjs');
  3. // 设置拦截ajax请求的相应时间
  4. Mock.setup({
  5. timeout: '200-600'
  6. });
  7. let configArray = [];
  8. // 使用webpack的require.context()遍历所有mock文件
  9. const files = require.context('./modules', true, /\.ts$/);
  10. files.keys().forEach((key) => {
  11. configArray = configArray.concat(files(key).default);
  12. });
  13. // 注册所有的mock服务
  14. configArray.forEach((item) => {
  15. for (let [path, target] of Object.entries(item)) {
  16. let protocol = path.split('|');
  17. Mock.mock(new RegExp('^' + protocol[1]), protocol[0], target);
  18. }
  19. });

2)mock的查找

modules下的xx,例如(具体的mock格式Home · nuysoft/Mock Wiki · GitHub):

  1. let demoList =
  2. {
  3. status: 200,
  4. message: 'success',
  5. 'data|10': [{
  6. clientId:'@id',
  7. clientName: '@cname',
  8. contactsPhone: '@email',
  9. }]
  10. }
  11. export default {
  12. 'get|/consumer/clientele/queryByAccount': demoList
  13. }

三.组件构造对象通过js调用

  1. import Vue from 'vue';
  2. import tipVue from './Tip.vue';
  3. //将组件转换成对象
  4. const TipVueConstructor = Vue.extend(tipVue);
  5. const initInstance = () => {
  6. //实例化
  7. instance = new TipVueConstructor({
  8. el: document.createElement('div')
  9. });
  10. };
  11. const TipBox = function (options) {
  12. if (!instance) {
  13. initInstance();
  14. }
  15. instance.action = '';
  16. if (!instance.visible && options) {
  17. document.body.appendChild(instance.$el);
  18. var closeTime = options.closeTime || 1000;
  19. Vue.nextTick(() => {
  20. instance.visible = true;
  21. instance.isTip = options.isTip;//isTip为true显示错误提示小黑框
  22. instance.message = options.message
  23. setTimeout(() => {
  24. TipBox.close();
  25. if (typeof options.func == "function") {
  26. options.func();
  27. }
  28. }, closeTime);
  29. });
  30. }
  31. };
  32. export default TipBox;

main.js或全局

Vue.prototype.$tip = TipBox

调用案例:

this.$tip({

          isTip: true,

          message: "验证码发送成功",

        });

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/58962
推荐阅读
相关标签
  

闽ICP备14008679号