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

Multiple解决形式

  • 每一个流动创立一个分支,在不同的分支上各自保护
  • 如果须要保护复用代码时,任选某一分支进行批改,通过git cherry-pick <commit id>进行平行迁徙。

Monorepo解决形式

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

目录构造

应用vue-cli初始化我的项目后,须要进行目录的调整,将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/下的我的项目定义;同级其余目录是各个我的项目通用的内容。

以上目录构造需在我的项目工作目录的顶层构造下,不容许包裹,为后续和lerna联合做筹备。

这里须要调整vue.config.js中的入口文件pages: entries的门路地址。

提取公共页面 & 路由

公共页面示例:

// views/Company/index.vue<template> ...</template><script>...</script><style scoped>...</style>

公共页面路由

// views/Company/index.jsexport default [  {    path: '/company',    name: 'company',    component: () => import(/* webpackChunkName: "company" */ './index.vue'),    meta: {      title: '公司简介'    }  }]

公共页面对立入口

// views/index.jsexport { default as companyRoute } from './Company/index.js'export { default as ruleRoute } from './Rule/index.js'

定制我的项目中的公共页面

// config/proA.jsimport {  companyRoute,  ruleRoute} from '../views/index.js'...export const logoUrl = '' // 还能够定制其它的内容export const routes = [  ...companyRoute,  ...ruleRoute]

我的项目中应用公共页面

projects/proA为例:

目录构造
|- assets|- components|- mixins|- router|- store|- pages|- App.vue|- main.js
我的项目主路由
// projects/proA/router/index.jsimport 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是定制化的。

状态治理

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

通用状态提取

// store/index.jsimport 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治理状态

定义我的项目的主状态治理

// projects/proA/store/index.jsimport 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局部代码到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'}

core/libs/index.js中追加:

export * from './helpers'

*.vue中应用

// 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>

引入lerna治理我的项目

初始化lerna环境

npm i -g lernanpm i -g yarn // 须要借助yarn的workspaces个性cd <workplace>lerna init        // ^ >lerna init        // lerna notice cli v4.0.0        // lerna info Updating package.json        // lerna info Creating lerna.json        // lerna info Creating packages directory        // lerna success Initialized Lerna files

lerna初始化环境会做以下几件事:

  • 更新package.json文件
  • 创立lerna.json文件
  • 创立packages/目录

指定工作区域

  • 批改package.json文件
  "private": true,  // private须要为true  "workspaces": [    "projects/*",    "components/*"  ],
  • 批改lerna.json文件
{  "npmClient": "yarn",  "useWorkspaces": true,  // 共用package.json文件的workspaces配置  "version": "independent"  // 每个我的项目独立治理版本号}

创立新我的项目

lerna create @demo/cli// ^ lerna success create New package @demo/cli created at .projects/cli

lerna会做以下几件事:

  • 命令行是否指定指标工作区
  • 若无指定工作区,选lerna.json配置项packages第一项工作区为指标工作区
  • 通过交互命令行界面创立我的项目目录

    • 新我的项目目录构造

      |- projects  |- cli    |- package.json    |- README.md    |- lib      |- cli.js    |- __tests__

这里讲诉一下,为什么整体的我的项目目录不容许包裹,如不容许应用src包裹我的项目目录:lerna指定工作区loc限度为顶级目录构造

lerna create @demo/cli2 'components'// ^ lerna success create New package @demo/cli2 created at .components/cli2

将已有我的项目改装为lerna的工作我的项目

在我的项目目录下追加package.json文件即可

{  "name": "proA",  "version": "0.0.0"}

查看我的项目列表

// 以yarn命令查看yarn workspaces info // 以lerna命令查看lerna list

我的项目目录下有package.json形容的能力被检索进去。

治理依赖

yarn workspace proA add packageA // 给proA装置依赖packageAlerna add packageA --scope=proA // 给proA装置依赖packageAyarn workspace proA add package-a@0.0.0 // 将packageA作为proA的依赖进行装置larna add package-a --scope=proA  // 将packageA作为proA的依赖进行装置//  ^ == yarn workspace装置本地包,第一次必须加上lerna.json中的版本号(后续肯定不要再加版本号),否则,会从 npm.org近程检索装置yarn add -W -D typescript // 在root下装置专用依赖typescript

通过以上几步,能够将我的项目的依赖独自治理

// projects/proA/package.json{  "name": "proA",  "version": "0.0.0",  "dependencies": {    "packageA": "^1.0.0"  }}// ./package.json根目录文件{  "name": "monoDemo",  "version": "0.0.0",  "private": true,  "workspaces": [    "projects/*",    "components/*",  ],  "dependencies": [    "typescript": "^0.4.9"  ]}

查看改变的我的项目

lerna changed

提交批改

  • 初始化提交:创立我的项目或切新分支第一次提交

    git add .git commit -m <message>git push --set-upstream origin <branch>
  • 后续提交

    git add .git commit -m <message>lerna version  --conventional-commits [-m <message>]// ^ 应用--conventional-commits参数lerna会依据semer版本规定主动生成版本号,否则,通过交互式命令行手动抉择版本号。

能够提前预置lerna version的提交日志

{  "command": {    "version": {      "message": "chore(release): publish %s"    }  }}

lerna version会做一下几件事:

  • 找出从上一个版本公布以来有过变更的 package
  • 提醒开发者确定要公布的版本号
  • 将所有更新过的的 package 中的package.json的version字段更新
  • 将依赖更新过的 package 的 包中的依赖版本号更新
  • 更新 lerna.json 中的 version 字段
  • 提交上述批改,并打一个 tag
  • 推送到 git 仓库

参考文档

  • 应用 MonoRepo 治理前端我的项目
  • husky对monoRepo的反对