vue3多个项目共享开发和单个项目独立打包的解决方案

Posted 左直拳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue3多个项目共享开发和单个项目独立打包的解决方案相关的知识,希望对你有一定的参考价值。

由于实际项目的特点产生了多系统共享开发和独立打包的需求,主要原理在于应用vue的多页面支持和npm的打包定制。本方案中,vue项目由一个主程序和若干个子系统构成。主程序代码放置于外层,提供共享组件和功能;而子系统各自独立,仅包含私有代码。运行时,可整个项目集成运行,也可以单个子系统运行;打包类似,可整体打包,也可单个子系统独立打包。

一、项目简介

近期参与的一个项目,业主是同一个单位,辖下多个部门,每个部门对应一个子系统,因此该项目由多个相对独立的子系统组成。公司组建了项目团队,分为若干个小组,负责开发不同的子系统。技术方案定为前后端分离,后端采取微服务框架,前端采用VUE进行构建。

前端这里,虽然各个子系统各自独立,但必须风格统一,且为了效率和成本,总有一些资源可以复用,比如一些组件。而实际部署时,既可以整体部署,也应该可以每个子系统独立部署,独立配置,以降低风险和耦合,提升性能和资源利用率,等等。

这种情况有2种方案。一种是公共资源与各子系统并行,独立开发,并发布为node module的形式,供各子系统调用;另一种方案是前端代码是一个大的工程,公共资源和所有子系统包含于其中。

目前采取第2种方式。由于所有子系统的代码位于同一个工程(项目),共享公用的组件和资源相对容易,但编译、运行、打包,要有所区分才好。否则编译耗时,更新部署也不合理:一个子系统修改,其他子系统也被迫发布和更新,可能会带来意想不到的问题。就算是每个子系统独立部署,但部署的其实都是完整的项目,可能又有同步的问题。

解决这个问题,可以应用vue的多页面支持和webpack的打包定制。介绍如下。

二、项目整体结构

总的来说,是公共部分放在外围,而各个子系统集中于一个子文件夹下。在结构上,每个子系统都几乎是一个完整的项目,特别是都提供入口(即main.js)。
在这里插入图片描述
由上图可知,子系统代码在src/apps/下集中存放;每个子系统都有 main.js、router.js、index.html等文件。而公共资源部分,又可称为框架、容器或主程序,代码存放在外层,即项目根目录下和src/根目录下。其中projects.js是我新加的,内容为多入口配置。它仅仅是数据,供vue.config.js调用。vue.config.js在vue3里,默认是没有的,也要手动添加。与vue2相比,可能vue3觉得约定重于配置,各种webpack的繁琐配置就不用设置了,于是默认不提供。但如果文件存在的话,它就会去读取,并以这里的内容为准。

图里还漏了突出package.json,里面有书写分情况运行、打包的脚本。

三、代码介绍

1、生成项目
vue的版本为vue3。网上有相关问题的文章,可惜很多都是vue2,写于2018或2019年,有点旧了,上来就说了一大堆webpack的配置文件,还有build文件夹,帮不上什么忙。当然了,我这篇文章过2年也很有可能不合时宜。或者现在说的就根本不对亦未可知。

生成项目方法如下,在命令行方式下运行

vue create 项目名称

这时系统会询问创建方式,选手动

Vue CLI v4.5.13
? Please pick a preset: 
  Default ([Vue 2] babel, eslint) 
  Default (Vue 3) ([Vue 3] babel, eslint) 
❯ Manually select features 

进入第二步,选中路由支持(注意系统的说明,使用空格键进行选中)

Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project: 
 ◉ Choose Vue version
 ◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
❯◉ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

第三步,选vue3

Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Router, Linter
? Choose a version of Vue.js that you want to start the project with 
  2.x 
❯ 3.x 

后面的按回车默认进行即可,有2个询问,我选了no,不过应该无关大雅。完成后生成一个干净而就绪的项目。

2、添加和修改文件

1)创建文件夹 /src/apps,用于集中存放子系统代码

2)依次创建 /src/apps/projectA、B、C、D,每个子系统都摆放这样一些文件:
在这里插入图片描述
这些文件可以直接从根目录中复制过来,内容无关紧要,只为一个示意。其中index.html与projectA.html文件是一样的。index.html用于仅运行和打包projectA,而projectA.html则用于多个项目集成运行和打包,看到下面的入口脚本设置就会明白为何会这样。router.js可以完全不需要修改,原原本本复制自根目录即可。

3)/projects.js
添加/projects.js,内容如下:

let path = require('path')
let glob = require('glob')
//配置pages多页面获取当前文件夹下的html和js
function getEntry(globPath) {
  let entries = {},
    basename, tmp, pathname;

  glob.sync(globPath).forEach(function(entry) {
    basename = path.basename(entry, path.extname(entry));
    tmp = entry.split('/').splice(-3);
    pathname = basename; // 正确输出js和html的路径

    entries[pathname] = {
      entry: 'src/' + tmp[0] + '/' + tmp[1] + '/main.js',
      template: 'src/' + tmp[0] + '/' + tmp[1] + '/' + tmp[2],
      title:  tmp[2],
      filename: tmp[2] //如projectA.html
    };
  });
  return entries;
}
let pages = getEntry('./src/apps/**?/*.html');
pages['index'] = {
  // page 的入口
  entry: 'src/main.js',
  // 模板来源
  template: 'public/index.html',
  // 在 dist/index.html 的输出
  filename: 'index.html',
  // 当使用 title 选项时,
  // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
  title: '公共首页',
  // 在这个页面中包含的块,默认情况下会包含
  // 提取出来的通用 chunk 和 vendor chunk。
  // chunks: ['chunk-vendors', 'chunk-common', 'index']
};

const config = {
  all: {
    pages: pages
  },
  projectA: {
    pages: {
      index: {
        entry: "src/apps/projectA/main.js",
        template: "src/apps/projectA/index.html",
        filename: "index.html"
      }
    },
    outputDir: "dist/projectA/"
  },
  projectB: {},//偷懒不写了
  projectC: {},
  projectD: {}
};

module.exports = config;

从上面的脚本可知,为什么子系统里需要提供一个index.html和一个projectA.html这样的文件。

4)/vue.config.js
主要是读取/projects.js的数据

const config = require("./projects.js");

/*
	npm run serve 就是all,否则是后接子系统名称
*/
let projectName = (!process.env.PROJECT_NAME || process.env.PROJECT_NAME.length === 0)
 ? 'all' : process.env.PROJECT_NAME;

module.exports = {
  ...config[projectName],
}

5)/package.json
节选手动加入的代码:

{
  "name": "v",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "serve:projectA": "cross-env PROJECT_NAME=projectA vue-cli-service serve --open",
    "build:projectA": "cross-env PROJECT_NAME=projectA vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  。。。

需要安装 cross-env。全局安装吧,以后用得上。

npm install -g cross-env
sudo ln -s /home/chenqu/apps/node-v14.17.0-linux-x64/bin/cross-env /usr/local/bin/cross-env

安装好后再加上链接。

到此系统架构其实已经完成。但为了演示效果更明显,突出单独打包和资源共享的思想,我加了一个导航条组件:

6)/src/components/Navi.vue

<template>
  <div id="nav">
  <!-- 注意指向子系统的是 <a> 链接,而不是vue的 router-link!!! -->
    <a href="/" :class="{active:!moduleName || moduleName === 'HOME'}">公共首页</a> | 
    <a href="/projectA" :class="{active:moduleName === 'A'}">projectA</a> | 
    <a href="/projectB" :class="{active:moduleName === 'B'}">projectB</a> | 
    <a href="/projectC" :class="{active:moduleName === 'C'}">projectC</a> | 
    <a href="/projectD" :class="{active:moduleName === 'D'}">projectD</a> | 
  </div>
</template>

<script>
    export default {
      props:{
        moduleName: String
      },
      data(){
        return {
        }
      },
      mounted() {
        
      },
    }
</script>

<style lang="less">
#nav {
  padding: 30px;
  a {
    font-weight: bold;
    color: #2c3e50;
    &.router-link-exact-active {
      color: #42b983;
    }
  }
  a.active {color:#00f;text-decoration:none;}
  a:hover {color:red;text-decoration:underline;}  
}
</style>

注意指向子系统的是 a 链接,而不是vue的 router-link!!!

注意指向子系统的是 a 链接,而不是vue的 router-link!!!

注意指向子系统的是 a 链接,而不是vue的 router-link!!!

然后在主程序和各子系统的首页(Home.vue)里调用:

主程序/src/views/Home.vue

<template>
  <div>
    <Navi />
    <div>
      <router-link to="/">home</router-link> | 
      <router-link to="/about">about</router-link> |
    </div>
    <div class="home">
      <img alt="Vue logo" src="../assets/logo.png">
      <HelloWorld msg="Welcome to Your Vue.js App"/>
    </div>
  </div>
</template>

<script>
import HelloWorld from '@/components/HelloWorld.vue'
import Navi from '@/components/Navi.vue'

export default {
  name: 'Home',
  components: {
    HelloWorld,
    Navi
  }
}
</script>

子系统首页/src/apps/projectA/views/Home.vue

<template>
  <div class="home">
    <Navi moduleName="A" />
    
    <div>
      <router-link to="/">home</router-link> | 
      <router-link to="/about">about</router-link> |
    </div>

    <div><h1>项目A首页</h1></div>
  </div>
</template>

<script>
import Navi from '@/components/Navi.vue'

export default {
  name: 'Home',
  components: {
    Navi
  }
}
</script>

四、运行与打包

1、集成运行

npm run serve

在这里插入图片描述
在这里插入图片描述
2、子系统单独运行

npm run serve:projectA

在这里插入图片描述
打包与运行类似。

参考文章
vue cli3超详细创建多页面配置

以上是关于vue3多个项目共享开发和单个项目独立打包的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

Git:由多个子项目组成的单个项目的最佳方法?

NuGet多个项目依赖的公共组件如何打包

tauri+vite+vue3开发环境下创建启动运行和打包发布

您如何在单个 SVN 服务器中安排多个项目?

如何在多个 Angular 2 项目之间共享一个 Angular 2 组件?

Vue-cli 2.X 如何配置多项目集成环境