vue项目架构-多子系统分包运行打包

Posted wangjie962311

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue项目架构-多子系统分包运行打包相关的知识,希望对你有一定的参考价值。

分析

要实现多子系统互相不干扰,并主系统能集成任意子系统,那么我们考虑就需要对每个子系统有一个独立的入口文件,同时主系统也有自己的入口文件。从而实现可拔插式结构。


实现方式

1、使用脚手架构建项目,在src下新建文件夹projects,用于存放主系统及各子系统文件,其中任何一个项目文件都相当于一个小vue,可以进行单独运行。具体如下:

2、任何小vue项目中包含入口文件main.js,以及项目需要的views页面,这里我们将路由进行拆分为index.js,和path.js,目的是便于我们主系统需要子系统路由而进行路由合并。
index.js——主要用于创建路由对象,以及合并需要的路由。
path.js——用于放置项目中需要的具体路由。
以主系统为例:
index.js 文件内容:

import Vue from "vue";
import VueRouter from "vue-router";

//引入主系统路由path.js
import mainRouter from "@/projects/mainSystem/router/path.js"//导入主系统路由文件


//引入其他子系统path.js
 import projectARouter from "@/projects/projectA/router/path.js"//导入子系统路由文件
//引入其他子系统path.js
 import khfwzhRouter from "@/projects/khfwzh/router/path.js"//导入子系统路由文件

Vue.use(VueRouter);
//合并路由(将需要的路由进行合并)
let routes = new Set([...mainRouter, ...projectARouter, ...khfwzhRouter ]);
const router = new VueRouter(
    mode: 'hash',
    base: process.env.BASE_URL,
    routes
)
//解决路由导航冗余报错(路由重复)
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) 
    return originalPush.call(this, location).catch(err => err)

export default router

path.js文件内容(主系统自身的路由):

/**
 * 主系统路由地址
 * @returns Promise<*>|*
 */
let firstPageIndex=()=>import(/* webpackChunkName: "mainSystem" */ "@/projects/mainSystem/views/index/index.vue")
let mainHome=()=>import(/* webpackChunkName: "mainSystem" */ "@/projects/mainSystem/views/Home")
let mainLayout=()=>import(/* webpackChunkName: "mainSystem" */ "@/components/base/publicLayout")


export default [
    
        path: "/",
        name: "home",
        component: mainHome
    , 
        path: '/aa',
        name: 'aaa',
        component: mainLayout,
        children: [
            
                path: "/aa",
                name: "/aa",
                component: firstPageIndex
            
        ]
    
]

3、在根目录下创建config文件夹及projectsConfig.js,用于存放各系统入口配置。

const config = 
    //主系统
    mainSystem: 
        pages: 
            index: 
                entry: "src/projects/mainSystem/main.js",
                template: "public/index.html",
                filename: "index.html"
            
        ,
        devServer: 
            port: 8081, // 端口地址
            open: false, // 是否自动打开浏览器页面
            host: "0.0.0.0", // 指定使用一个 host,默认是 localhost
            https: false, // 使用https提供服务
            disableHostCheck: true,
        
    ,
    //子系统A
    projectA: 
        pages: 
            index: 
                entry: "src/projects/projectA/main.js",
                template: "public/index.html",
                filename: "index.html"
            
        ,
        devServer: 
            port: 8080, // 端口地址
            open: false, // 是否自动打开浏览器页面
            host: "0.0.0.0", // 指定使用一个 host,默认是 localhost
            https: false, // 使用https提供服务
            disableHostCheck: true,
        
    ,
;
module.exports = config;

4、将配置文件projectsConfig.js在vue.config.js引入,通过控制入口文件的路径和输出的路径实现,分模块打包。

const path = require('path')
const resolve = dir => 
  return path.join(__dirname, dir)


const config = require("./config/projectsConfig.js");
let projectName = process.env.PROJECT_NAME;
module.exports = 
  publicPath: "/" + projectName,
  outputDir: "dist/" + projectName + "/",
  configureWebpack: config => 
    config.resolve = 
      extensions: ['.js', '.vue', '.json'],
      alias: 
        'vue$': 'vue/dist/vue.esm.js',
        '@': resolve('src')
      
    
  ,
  ...config[projectName],
  chainWebpack: config =>  // 是一个函数,会接收一个基于 webpack-chain 的 ChainableConfig 实例。允许对内部的 webpack 配置进行更细粒度的修改。
    config.resolve.alias.set('@', resolve('src'))
    // config.entry('index').add('babel-polyfill')
  ,
  productionSourceMap: false,
  lintOnSave: false
;

5、安装cross-env,在我们执行打包命令的时候,通过cross-env找到我们的入口文件。

npm install --save-dev cross-env

6、修改package.json中脚本


"scripts": 
    "dev:mainSystem": "cross-env PROJECT_NAME=mainSystem vue-cli-service serve",
    "dev:projectA": "cross-env PROJECT_NAME=projectA vue-cli-service serve",
    "build:mainSystem": "cross-env PROJECT_NAME=mainSystem vue-cli-service build",
    "build:projectA": "cross-env PROJECT_NAME=projectA  vue-cli-service build",
    ...其他
  ,

7、现在可以使用命令分别进行运行 和 打包各系统
运行及打包主系统
npm run dev:mainSystem
npm run build:mainSystem
运行及打包子系统
npm run dev:projectA
npm run build:projectA


vite3+vue3 项目打包优化实战之-视图分析(rollup-plugin-visualizer)CDN引入依赖分包gzip压缩history404问题

文章目录

写在前面

vue项目在线下环境开发完成后,我们就需要项目的打包上线了,除了要知道打包命令npm run build 之外,我们还要知道项目整体文件依赖情况,web访问加载速度等概念,包括首屏优化方案。我通过一次实战把最基本可以优化的步骤走一下。
将分为以下几个步骤:

  1. build 视图分析依赖文件
  2. 第三方库CDN引入
  3. 依赖文件分包
  4. gzip压缩文件
  5. 部署前配置history路由模式的404问题

build 视图分析依赖文件

分析项目中的文件大小及引用情况,是优化前的重要一步,从而去采取文件分包,cdn引入等相关技术概念,那么在vite下我们可以利用什么工具来做项目的依赖分析呢?

答案是:
Rollup Plugin Visualizer,这是一个依赖分析插件,它提供了多种模式的依赖分析,包括直观的视图分析,sunburst(循环层次图,像光谱)、treemap(矩形层次图,看起来比较直观,也是默认参数)、network(网格图,查看包含关系)、raw-data(原数据模式,json格式), list(列表模式),你可以选择任意一种你喜欢的观察模式,这里我们就以默认的为例;

安装方式如下:
npm install --save-dev rollup-plugin-visualizer
yarn add --dev rollup-plugin-visualizer
❗选择一种安装即可;

安装完成后,即可在vite下的插件属性中进行配置:

import  visualizer  from 'rollup-plugin-visualizer';
export default defineConfig(
  plugins: [vue(), visualizer(
    emitFile: false,
    file: "stats.html", //分析图生成的文件名
    open:true //如果存在本地服务端口,将在打包后自动展示
  )],
)

配置的参数有很多是默认的,如果你没有特殊需求,完全可以不添加参数;下面我添加一个表格对已有参数进行诠释:

参数类型解释
filename/filestring生成分析的文件名
titlestringhtml标签页标题
openboolean以默认服务器代理打开文件
templatestring可选择的图表类型
gzipSizeboolean搜集gzip压缩包的大小到图表
BrotliSizeboolearn搜集brotli压缩包的大小到图表
emitFileboolean使用emitFile生成文件,简单说,这个属性为true,打包后的分析文件会出现在打包好的文件包下,否则就会在项目目录下
sourcemapboolean使用sourcemap计算大小
projectRootstring, RegExp文件的根目录,默认在打包好的目录下

看完这些参数,也有了大概的了解,根据需求配置就好
接下来,你只需要npm run build;就可以查看这个图表了

是不是很好看,五颜六色的,这里颜色也是有说法的:
🟦蓝色表示自己写下的js文件项;
🟩绿色是表示依赖的文件项;
其他颜色我们可以根据文件名来判断

像上面这个文件,明显我们可以看出这是router相关的文件,当鼠标点击时我们可以看出这块依赖文件的大小,他的位置是是哪里;这里也看到它的大小马上到了100kb,是因为我们的主要业务也确实在这里;那我们就可以通过这样的信息采取一些优化方案,包括不限于修改代码的设计方式等,那么下面要说的CDN引入就是为了减少如element-plus及bootstrap5的打包文件大小,减少本地文件载入压力。下图很直观的可以看出element-plus的占据程度


第三方库CDN引入

🤔CDN 是构建在数据网络上的一种分布式的内容分发网。 CDN
的作用是采用流媒体服务器集群技术,克服单机系统输出带宽及并发能力不足的缺点,可极大提升系统支持的并发流数目,减少或避免单点失效带来的不良影响。

上面是百度百科的一段话,这里我要cdn引入是因为我们只有一个服务器,便称为单系统;那么引入他们各自官方的cdn链接就是在利用分布式内容分发技术,但是毕竟是人家的cdn安全上还需要考虑,如果自己公司有条件,就可以用自己公司的保证安全性;

话接上文,我们通过视图分析发现了element-plus是最大的文件依赖,包括
bootstrap5,那我们就尝试在vite+vue下配置这俩个库的cdn引入来减少请求压力;

cdn管理插件我们使用vite-plugin-cdn-import
安装方式
npm install vite-plugin-cdn-import --save-dev
yarn add vite-plugin-cdn-import -D
选择自己的包管理器下载

// vite.config.js 基本用法
import reactRefresh from '@vitejs/plugin-react-refresh'
import importToCDN from 'vite-plugin-cdn-import'

export default 
    plugins: [
        importToCDN(
            modules: [
                
                    name: 'react',
                    var: 'React',
                    path: `umd/react.production.min.js`,
                ,
                
                    name: 'react-dom',
                    var: 'ReactDOM',
                    path: `umd/react-dom.production.min.js`,
                ,
            ],
        ),
    ],

有一些model该插件还提供了自动完成,不要太爽,我们不需要配置参数了

export default 
    plugins: [
        importToCDN(
            modules: [
                autoComplete('react'),
                autoComplete('react-dom')
            ],
        ),
        reactRefresh(),
    ],

写法如上,那么有那些是支持自动完成的呢,官方也写出了

自动完成支持的 module
“react” | “react-dom” | “react-router-dom” |
“antd” | “ahooks” | “@ant-design/charts” |
“vue” | “vue2” | “@vueuse/shared” |
“@vueuse/core” | “moment” |
“eventemitter3” | “file-saver” |
“browser-md5-file” | "xlsx | “crypto-js” |
“axios” | “lodash” | “localforage”

接下来我们再看完参数配置即可完成cdn的相关配置
这里的相关属性配置你们去官方看吧,很明确vite-plugin-cdn-import
然后还需要知道是常见cdn网站,我们主要介绍俩个国外的,也是常用的,

UNPKG:https://unpkg.com
jsDelivr :https://www.jsdelivr.com

这里还要学习怎么找到对应库cdn包的路径,我们好来配置参数,以UNPKG为例实践如下:
比如我们要配置element-plus的cdn引入,

  1. 先进入https://unpkg.com
  2. 在地址后面链接你的包名
  3. 得到链接配置参数即可

下面我附图来展示:
package.json找到你的包信息

浏览器拼接你的包并且回车:

你会发现页面请求到了内容(这就说明该库在cdn上已经存在,我们把整个的链接拿下来配置好参数就可以了):

到这里你已经掌握了cdn库自动构建的基本知识,下面看一下我的配置片段:

import  defineConfig  from 'vite'
import vue from '@vitejs/plugin-vue'
import  autoComplete, Plugin as importToCDN  from 'vite-plugin-cdn-import';

export default defineConfig(
  plugins: [vue(),
   importToCDN(
    prodUrl: 'https://unpkg.com/name@version/path',
    modules: [
      autoComplete('vue'),
      autoComplete('axios'),
      
        name: 'element-plus',
        var: 'ElementPlus', //根据main.js中定义的来
        version: '2.2.17',
        path: 'dist/index.full.js',
        css: 'dist/index.css'
      ,
      
        name: 'vue-demi',
        var: 'VueDemi', //根据main.js中定义的来
        version: '0.13.11',
        path: 'lib/index.iife.js'
      ,
      
        name: '@element-plus/icons-vue',
        var: 'ElementPlusIconsVue', //根据main.js中定义的来
        version: '2.0.9',
        path: 'dist/index.iife.min.js'
      ,
      
        name: 'bootstrap',
        var: 'bootStrap', //根据main.js中定义的来
        version: '5.2.1',
        path: 'dist/js/bootstrap.js',
        css: 'dist/css/bootstrap.min.css'
      ,
    ],
  )
],
)

上面的配置中有一个问题需要提及,我配置了vue-demi,它是pinia仓库的依赖,简单说,如果不配置它我们的cdn构建中间出现了个缺口,它是pinia-vue中间的一个依赖;vue和axios我配置了自动完成,那接下来看下我们进入实战。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="./static/ico/favicon-a91524b8.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>xxxxx</title>
    <script type="module" crossorigin src="./static/js/index-3f77d0fe.js"></script>
    <link rel="modulepreload" crossorigin href="./static/js/@vue-57a1150c.js">
    <link rel="modulepreload" crossorigin href="./static/js/js-cookie-1db5286e.js">
    <link rel="modulepreload" crossorigin href="./static/js/axios-08851a62.js">
    <link rel="modulepreload" crossorigin href="./static/js/lodash-es-9a8f223b.js">
    <link rel="modulepreload" crossorigin href="./static/js/@vueuse-d210d573.js">
    <link rel="modulepreload" crossorigin href="./static/js/@element-plus-1fa478d9.js">
    <link rel="modulepreload" crossorigin href="./static/js/@popperjs-892fd7f5.js">
    <link rel="modulepreload" crossorigin href="./static/js/@ctrl-eb0b847c.js">
    <link rel="modulepreload" crossorigin href="./static/js/dayjs-54e8cf14.js">
    <link rel="modulepreload" crossorigin href="./static/js/async-validator-efc2d198.js">
    <link rel="modulepreload" crossorigin href="./static/js/memoize-one-99e54574.js">
    <link rel="modulepreload" crossorigin href="./static/js/escape-html-4bbaf1e1.js">
    <link rel="modulepreload" crossorigin href="./static/js/normalize-wheel-es-da779ce4.js">
    <link rel="modulepreload" crossorigin href="./static/js/@floating-ui-4b8fd220.js">
    <link rel="modulepreload" crossorigin href="./static/js/element-plus-88e408ab.js">
    <link rel="modulepreload" crossorigin href="./static/js/vue-router-5a9da933.js">
    <link rel="modulepreload" crossorigin href="./static/js/vue-demi-5b9a0fa5.js">
    <link rel="modulepreload" crossorigin href="./static/js/pinia-ef1d9feb.js">
    <link rel="modulepreload" crossorigin href="./static/js/@fortawesome-7be7f9bf.js">
    <link rel="stylesheet" href="./static/css/element-plus-c08499e6.css">
    <link rel="stylesheet" href="./static/css/index-f2f66543.css">
    <link rel="stylesheet" href="./static/css/bootstrap-744009a1.css">
  </head>
  <body>
    <div id="app"></div>
    
  </body>
</html>

这样的入口文件,这么多的link是我提前用了分包的效果,下面我们会实战分包;回到上面,这么多的link引入对我们本地的压力可想而知;再来看分析图:

element-plus的依赖是如此庞大,那我们配置完后,build再来感受下;

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="./static/ico/favicon-a91524b8.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>xxxxxxxxx</title>
  <link href="https://unpkg.com/element-plus@2.2.17/dist/index.css" rel="stylesheet">
  <link href="https://unpkg.com/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://unpkg.com/vue@3.2.36/dist/vue.global.prod.js"></script>
  <script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script>
  <script src="https://unpkg.com/element-plus@2.2.17/dist/index.full.js"></script>
  <script src="https://unpkg.com/vue-demi@0.13.11/lib/index.iife.js"></script>
  <script src="https://unpkg.com/@element-plus/icons-vue@2.0.9/dist/index.iife.min.js"></script>
  <script src="https://unpkg.com/bootstrap@5.2.1/dist/js/bootstrap.js"></script>
  <script type="module" crossorigin src="./static/js/index-8dbc54cb.js"></script>
  <link rel="modulepreload" crossorigin href="./static/js/js-cookie-1db5286e.js">
  <link rel="modulepreload" crossorigin href="./static/js/vue-router-5755d64e.js">
  <link rel="modulepreload" crossorigin href="./static/js/pinia-7992cf6b.js">
  <link rel="modulepreload" crossorigin href="./static/js/@fortawesome-0742f1fb.js">
  <link rel="stylesheet" href="./static/css/index-f2f66543.css">
  <link rel="stylesheet" href="./static/css/element-plus-c08499e6.css">
  <link rel="stylesheet" href="./static/css/bootstrap-744009a1.css">
</head>

<body>
  <div id="app"></div>

</body>

</html>

cdn标签引入正确,我们也可以本地构建serve 来验证效果,这里就不扩展了,那么再看下分析图

element-plus等第三方库将不会成为我们的本地依赖,这里的tool是我自己的js工具模块,他看起来也是很大,那我们就可以找方法去优化调整,至此你学会了vite框架下的vue项目cdn引入方式;

还有一点题外话,cdn引入之前:

  1. 你需要知道一但依赖网站出现问题,我们的项目也就不行了,所以有依赖如上网站的cdn建议留下备用方案,以便维护。
  2. cdn是对整个库的引入,在你引入前,你要对一个库足够熟悉,比如我有一个好朋友他就在cdn引入中上出现了问题;
    大致是这样:项目是vue3项目,我们知道在vue3中的reactive响应式对象函数,
//它可以这样导入:
import  reactive  from "@vue/reactivity";
//也可以这样:
import  onBeforeMount, ref, reactive  from "vue";

那么如果你用@vue/reactivity来导入,上线后发现reactive响应式没了,别问我怎么知道的,我朋友说的。我们cdn可没有引入该组件的相关依赖,只是在本地\\node_modules下才有;


依赖文件分包

🤔在我们没有配置构建工具的分包功能时,构建出来的build将无比巨大且是独立的一个js and css 文件,这样就会存在本地加载文件的压力,已经成熟的方案在rollup 和 webpack中都有概念;

在vite的官方介绍中有这么一段

build.rollupOptions¶
类型: RollupOptions
自定义底层的 Rollup 打包配置。这与从 Rollup 配置文件导出的选项相同,并将与 Vite 的内部 Rollup 选项合并。查看 Rollup 选项文档 获取更多细节。

vite底层已经集成了rollup的一部分功能,也就是说我们直接配置好即可;详细关于rollup的配置可以去看官方文档rollupjs;这里我直接给出我的配置情况,也是借鉴部分大佬的文章得出;

export default defineConfig(
  plugins: [vue(), viteCompression(
    verbose: true,
    disable: false,
    threshold: 10240,
    algorithm: 'gzip',
    ext: '.gz',
  ), 
   importToCDN(
    prodUrl: 'https://unpkg.com/name@version/path',
    modules: [
      autoComplete('vue'),
      autoComplete('axios'),
      
        name: 'element-plus',
        var: 'ElementPlus', //根据main.js中定义的来
        version: '2.2.17',
        path: 'dist/index.full.js',
        css: 'dist/index.css'
      ,
      以上是关于vue项目架构-多子系统分包运行打包的主要内容,如果未能解决你的问题,请参考以下文章

通过cordova将vue项目打包为app

Vue项目部署,打包发布上线

vue.js 打包时出现空白页和路径错误

idea的Maven项目,vue.js应该放哪

如何运行一个Vue项目

如何把vue.js项目部署到服务器上