基于Vue-cli初始化项目的多项目管理

在Vue-cli 3.X环境下,基于同一类型的活动,可以多个页面复用,大部分组件可以公用的背景

Multiple处理方式

  • 每一个活动创建一个分支,在不同的分支上各自维护
  • 如果需要维护复用代码时,任选某一分支进行修改,通过git cherry-pick <commit id>进行平行迁移。

Monorepo处理方式

仅在同一分钟下进行多项目的维护,各个功能模块解构,通过项目配置项进行个性化配置。

目录结构

|-src  
  |- views
    |- index.js // 通用页面的统一入口
    |- Company
      |- index.vue // 通用页面Company结构、样式、逻辑
      |- index.js  // Company页面路由
    |- Rule
      |- index.vue
      |- index.js
  |- components
  |- core
    |- instance  // 和app实例挂钩的方法
    |- libs  // 和app实例无关的方法
  |- assets
    |- images
    |- fonts
  |- store
    |- index.js  // 通用状态
    |- types.js  // 事件类型
  |- config
    |- proA.js  // 项目资源配置
    |- proB.js
  |- projects  // 项目定制资源
    |- proA
    |- proB

不同项目的区别完全在于config/文件的配置和projects/下的项目定义;同级其余目录是各个项目通用的内容。

提取公共页面 & 路由

公共页面示例:
// src/views/Company/index.vue
<template>
 ...
</template>
<script>
...
</script>
<style scoped>
...
</style>
公共页面路由
// src/views/Company/index.js
export default [
  {
    path: '/company',
    name: 'company',
    component: () => import(/* webpackChunkName: "company" */ './index.vue'),
    meta: {
      title: '公司简介'
    }
  }
]
公共页面统一入口
// src/views/index.js
export { default as companyRoute } from './Company/index.js'
export { default as ruleRoute } from './Rule/index.js'
定制项目中的公共页面
// src/config/proA.js
import {
  companyRoute,
  ruleRoute
} from '../views/index.js'
...
export const logoUrl = '' // 还可以定制其它的内容

export const routes = [
  ...companyRoute,
  ...ruleRoute
]
项目中使用公共页面

src/projects/proA为例:

目录结构

|- assets
|- components
|- mixins
|- router
|- store
|- pages
|- App.vue
|- main.js

项目主路由

// src/projects/proA/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import { routes } from '../../config/proA'
import Home from '../pages/Home'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/home',
      name: 'Home',
      component: Home,
      meta: {
        title: ''
      }
    },
    ...routes
  ]
})

其中:Home/index.vue是定制化的。

状态管理

多项目是独立运行时,状态提取不会互相干扰,若一次性运行多个项目,通用状态会被修改。

通用状态提取
// src/store/index.js
import types from './types'

export const initialState = {
  userInfo: {},
  ...
}
export function getGetters (store) {
  return {
    userId: () => store.userInfo.userID,
    ...
  }
}
export function getMutations (store) {
  return {
    [types.INITIALMUTATIONTYPES.USER_INFO] (val) {
      store.userInfo = val
    },
    ...
  }
}

config/proA.js文件中追加:

...
export * from '../store/index.js'
export * from '../store/types.js'
...
项目中使用

小型项目,使用vue.observable管理状态

定义项目的主状态管理
// src/projects/proA/store/index.js

import vue from 'vue'
import { initialState, getGetters, getMutations } from '../../../config/proA'

export const store = vue.observable({
  ...initialState,
  customState: '', // 项目私有状态
  ...
})
store._getters = {
  ...getGetters(store),
  customGetter() {  // 项目私有
      return store.customState
  },
  ...
}
store._mutations = {
  ...getMutation(store),
  ...  // 项目私有
}
export const mutation = {
  ...getMutations(store),
  ...  // 项目私有
}
定义辅助方法mapGetters

拷贝vuex部分代码到src/core/libs/helpers.js文件中

export const mapGetters = (getters) => {
  const res = {}
  if (!isValidMap(getters)) {
    console.error('[vuex] mapGetters: mapper parameter must be either an Array or an Object')
  }
  normalizeMap(getters).forEach(({ key, val }) => {
    res[key] = function mappedGetter () {
      if (!(val in this.$store._getters)) {
        console.error(`[vuex] unknown getter: ${val}`)
        return
      }
      return this.$store._getters[val]()
    }
  })
  return res
}
export function normalizeMap (map) {
  if (!isValidMap(map)) {
    return []
  }
  return Array.isArray(map)
    ? map.map(key => ({ key, val: key }))
    : Object.keys(map).map(key => ({ key, val: map[key] }))
}
export function isValidMap (map) {
  return Array.isArray(map) || isObject(map)
}
export function isObject (obj) {
  return obj !== null && typeof obj === 'object'
}

/src/core/libs/index.js中追加:

export * from './helpers'
*.vue中使用
// src/projects/proA/pages/Home/index.vue
<script>
...
import { mapGetters } from '../../../core/libs/'

export default {
  data () {
    return {
      ...
    }
  },
  computed: {
    ...mapGetters([
        'userId'
    ]),
    ...
  }
...
</script>

组件管理

组件统一入口

借助webpackrequire.context方法将/components/下的组件整合

const ret = {}
const requireComponent = require.context(
  './',  // 指定递归的文件目录
  true,  // 是否递归文件子目录
  /[A-Z]\w+\.(vue)$/  // 落地文件
)
requireComponent.keys().forEach(fileName => {
  const componentConfig = requireComponent(fileName)
  const component = componentConfig.default || componentConfig
  const componentName = component.name || fileName.split('/').pop().replace(/\.\w+$/, '')
  // ret[componentName] = () => requireComponent(fileName)
  ret[componentName] = component
})
export default ret
定义布局配置
// config/proA.js追加
...
export const layouts = {
  Home: [
    {
      componentType: 'CompA',
      request: {
        fetch () {
          const res = []
          return res
        }
      },
      response: {
        filter (res) {
          return []
        },
        effect () {

        }
      }
    },
    {
      componentType: 'CompB'
    },
    {
      componentType: 'CompC'
    },
    {
      componentType: 'CompD'
    }
  ]
}
项目中使用

proA/Home/index.vue

<template>
...
  <template v-for="componentConfig of layouts">
    <component
      v-bind="dataValue"
      :is="componentConfig.componentType"
      :key="componentConfig.componentType"
    >
    </component>
  </template>
...
</template>
<script>
...
import {
  CompA,
  CompB
} from '../../components/'
import { layouts } from '../../config/proA'
...
export default {
  ...
  data () {
    return {
      ...
      layouts: layouts.Home,
      ...
    }
  },
  ...
  components: {
    CompA,
    CompB
  },
  ...
}
</script>

参考文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355