uniapp vuecli项目融合[小记]:将多个项目融合,打包成一个小程序/App,拆分多个H5应用

前言:

        目前两个uniapp vuecli开发的项目【A、B】,新规划的项目C:需要融合项目B 80%的功能模块,同时也需要涵盖项目A的所有功能模块。

应用需求:

        1、新项目C【小程序】可支持切换到应用A/C界面【内部通过初始化、路由跳转实现切换】【因此新项目C考虑基于项目A的工程上开发, git引入项目B

        2、工程A在H5中需要打包成两个应用:A应用、C应用;

实现思路:

        1、A项目工程上开发新应用C,引入B工程的模块/代码:通过git地址,安装依赖的方式引入B项目;

        2、A工程:小程序打包为一个应用[A+C]、H5拆分应用[A/C]:通过pages.json动态改写来实现,通过不同的打包命令来区分;

A工程改造:基于A开发C应用、融合B项目,步骤梳理:
1. 通过git地址,安装依赖的方式引入B项目:package.json
"mobileB": "git+https://git.xxxx/mobileB.git#xxxxxxxxxx"

注:git+<项目B仓库地址>#<git提交commit id>

2. 解决问题:项目B使用了TS,项目A未使用TS,引入后需要转换支持:vue.config.js
module.exports = {
  /**
   * 某个依赖项是TypeScript编写的,但是目标环境中并不支持TypeScript,
   * 那么我们需要在打包前先将其转换为JavaScript代码
   */
  transpileDependencies: ['mobileB'],
  // ...其他配置
}
3. webpack编译构建过程中,处理mobileB模块导入路径

        引入mobileB后,因为mobileB中的别名等,无法正确被解析,所以需要在编译阶段,进行修改。

        定义一个名为CustomAliasResolverPlugin.js的文件,实现webpack插件类,在webpack编译过程中处理mobileB模块导入路径,确保mobileB中的别名路径都能正确的指向。

class CustomAliasResolverPlugin {
  constructor() {
    this.path_mobileB = 'mobileB/src';
  }

  apply(compiler) {
    // 监听了normalModuleFactory事件,在每个普通模块被创建时执行回调函数
    compiler.hooks.normalModuleFactory.tap(
      'CustomAliasResolverPlugin',
      (factory) => {
        // 回调函数接收一个模块工厂实例factory作为参数,并在其上进一步监听 beforeResolve 事件。beforeResolve 是在模块解析之前触发的钩子,此时可以修改模块请求信息
        factory.hooks.beforeResolve.tapAsync(
          'CustomAliasResolverPlugin',
          (data, callback) => {
            // 在 beforeResolve 的回调函数内,从数据对象data中获取当前模块的请求路径request和解析上下文context。
            const { request, context } = data;
            // 解析上下文包含指定的基础路径,确保是mobileB下的模块
            if (context.includes(this.path_mobileB)) {
              if (request.startsWith('@src')) {
                data.request = request.replace('@src', `${this.path_mobileB}/src`);
              } else if (request.startsWith('@service')) {
                data.request = request.replace('@service', `${this.path_mobileB}/service`);
              }
            }
            callback(null, data);
          }
        );
      }
    );
  }
}

module.exports = CustomAliasResolverPlugin;

        CustomAliasResolverPlugin 是一个自定义的webpack插件,要求它在项目构建时会参与到webpack的模块解析阶段,因此我们需要在添加到plugins中,以及解决可能存在的路径问题;

        vue.config.js:

const CustomAliasResolverPlugin = require('./config/CustomAliasResolverPlugin');

module.exports = {
  configureWebpack: {
    plugins: [new CustomAliasResolverPlugin()]
  }
  // ...其他配置
}
4. pages.json动态改写:实现H5应用拆分、小程序整合

        uniapp页面文件的打包是基于pages.json,所以我们需要根据需求,使用nodeJs动态生成对应的pages.json文件。

示例:

common.js:编写需要打包成A、C两个项目的公共页面路由及其他配置

// 小程序的分包
const subPackages = []
const pages = [
  {
    path: 'pages/home/home',
    aliasPath: '/'
  }
]

// 其他配置相关
const otherConfig = {
  globalStyle: {
    navigationStyle: 'custom',
    allowsBounceVertical: 'NO',
    renderingMode: 'seperated',
    pageOrientation: 'portrait',
    rpxCalcMaxDeviceWidth: 540,
    rpxCalcBaseDeviceWidth: 375
  },
  // 需要自动注册的自定义组件
  easycom: {
    autoscan: true,
    custom: {
      '^ant-tree-(.*)':
        '@/components/businessComponent/ant-tree/ant-tree-$1.vue',
      '^ant-customize-(.*)':
        '@/components/businessComponent/ant-customize/ant-customize-$1.vue',
      '^u-(.*)': 'uview-ui/components/u-$1/u-$1.vue',
      '^anmc-(.*)': 'antm-ui/components/common/anmc-$1/anmc-$1.vue',
      '^anmb-(.*)': 'antm-ui/components/business/anmb-$1/anmb-$1.vue'
    }
  }
};

module.exports = {
  subPackages,
  pages,
  otherConfig
};

ant4Pages.js/erpPages.js: A/C应用的页面路由

module.exports = {
  pages: [],
  subPackages: []
}

main.js: 根据条件重写pages.json


const common = require('./common')
const subPackages = common.subPackages || []
const otherConfig = common.otherConfig || {}
const commonPages = common.pages || []

const fs = require('fs');
const ant4pages = require('./ant4Pages');
const erppages = require('./erpPages');
const pagesPath = process.env.UNI_INPUT_DIR + '/pages.json';
let pagesJson = {};
// 判断是否H5
if (process.env.UNI_PLATFORM === 'h5') {
  let subPackagesProxy = subPackages
  // 判断是否是ERP项目:对应文中说到的C应用【H5】
  // VUE_APP_NODE_APP_TYPE是命令上自行添加的字段,用来区分
  if (process.env.VUE_APP_NODE_APP_TYPE === 'erp') {
    const erpSubPackages = erppages.subPackages
    erpSubPackages.forEach(item => {
      const root = subPackagesProxy.find(sub => sub.root === item.root)
      if (root) {
        item.pages = [
          ...root.pages,
          ...item.pages
        ]
        subPackagesProxy = subPackagesProxy.filter(sub => sub.root !== item.root)
      }
    })
    pagesJson = {
      ...erppages,
      pages: [...commonPages, ...erppages.pages],
      subPackages: [...subPackagesProxy, ...erpSubPackages],
      ...otherConfig
    }
  } else {
    const antSubPackages = ant4pages.subPackages
    antSubPackages.forEach(item => {
      const root = subPackagesProxy.find(sub => sub.root === item.root)
      if (root) {
        item.pages = [
          ...root.pages,
          ...item.pages
        ]
        subPackagesProxy = subPackagesProxy.filter(sub => sub.root !== item.root)
      }
    })
    pagesJson = {
      ...ant4pages,
      pages: [...commonPages, ...ant4pages.pages],
      subPackages: [...subPackagesProxy, ...antSubPackages],
      ...otherConfig
    }
  }
} else {
  // 小程序打包,整合所有的路由配置
  const subPackagesProxy = subPackages
  let antSubPackages = ant4pages.subPackages
  let erpSubPackages = erppages.subPackages
  subPackagesProxy.forEach(item => {
    const antRoot = antSubPackages.find(sub => sub.root === item.root)
    const erpRoot = erpSubPackages.find(sub => sub.root === item.root)
    if (antRoot) {
      item.pages = [
        ...antRoot.pages,
        ...item.pages
      ]
      antSubPackages = antSubPackages.filter(sub => sub.root !== item.root)
    }
    if (erpRoot) {
      item.pages = [
        ...erpRoot.pages,
        ...item.pages
      ]
      erpSubPackages = erpSubPackages.filter(sub => sub.root !== item.root)
    }
  })
  pagesJson = {
    pages: [...commonPages, ...ant4pages.pages, ...erppages.pages],
    subPackages: [...subPackagesProxy, ...antSubPackages, ...erpSubPackages],
    ...otherConfig
  };
}

// 将最终的结果写入pages.json,在项目运行或构建时
fs.writeFileSync(pagesPath, JSON.stringify(pagesJson), {
  flag: 'w'
});

将src/config/appPages/main.js文件引入vue.config.js中;

        注:直接在vue.congfig.js顶部导入对于pages.json的重写,vue.config.js 文件是 Vue CLI 在构建项目时默认查找并加载的第一个用户自定义配置文件,目前测试没有出现什么异常,如果存在异常,可考虑调整至预处理脚本中,具体根据实际情况而定:

{
  "scripts": {
    "prebuild": "node ./src/config/main.js",
    "build": "vue-cli-service build"
  },
}
5. router拦截处理【参考】

6. vuex整合: mobileB中vuex中的modules,添加到当前主工程的modules中:
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';
import getters from './getters';
import user from './modules/user.js';
// 项目B的store集合,全部抛出来,添加到modules中
import Bstore from 'mobileB/src/store/main

Vue.use(Vuex);
export default new Vuex.Store({
  plugins: [
    createPersistedState({
      storage: {
        // ...
      },
      reducer(value) { // 选择性配置持久化
        return {
        }
      }
    })
  ],
  modules: {
    user,
    ...Bstore
  },
  getters
});
7. 初始化B项目依赖的一些数据缓存等;

        可以在B项目中提供一个应用初始化的方法,在引用项目中合适的时机调用;以下示例仅供参考:

import { libraryId } from "@service/user";
import { setLocal } from "src/utils/utils";
import { YB_MOBILE_LIBRARY_INFO } from "src/configs/constants";

/**
 * 对外git引用,初始化方法
 */
async function appInit(appType: string) {
  // 存储用户信息、token等
  // 初始化一些业务数据等等
  const res: any = await libraryId({});
  getApp().globalData.appType = appType || 'anterp'
  setLocal(YB_MOBILE_LIBRARY_INFO, res.res.data.libraryId);
}

export default appInit;
8. 项目A/C工程中,使用项目B的组件或页面;

        在路由中对应配置项目B中的路由,对应创建页面文件,将项目B中的文件当作组件引入:【注:目前项目统一使用uni-simple-router路由管理插件,项目B中的路由跳转,只要在项目A/C工程中同样存在该路由,及可跳转】

<script>
import templateDetail from 'mobileB/src/pages/common/templateDetailInfo/index.vue';
export default {
  components: {
    templateDetail
  }
};
</script>
备注

        在实际的项目开发场景中,对需要融合项目架构的技术、实现方案统一性、代码规范等等有较高的要求,同时会遇到很多问题需要处理,如主题样式、不同平台的语法支持等等,本文仅对实现方案进行一个大致梳理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/349711.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

便捷接口调测:API 开发工具大比拼 | 开源专题 No.62

hoppscotch/hoppscotch Stars: 56.1k License: MIT Hoppscotch 是一个开源的 API 开发生态系统&#xff0c;主要功能包括发送请求和获取实时响应。该项目具有以下核心优势&#xff1a; 轻量级&#xff1a;采用简约的 UI 设计。快速&#xff1a;实时发送请求并获得响应。支持多…

直播项目开发

uni-aapp&#xff0c;egg.js&#xff0c;直播服务器自己搭建&#xff0c;Node.js&#xff0c;socket.io实时送礼物&#xff0c;充值&#xff0c;兼容Android&#xff0c;iOS,小程序&#xff0c;充值时用到微信支付&#xff0c;直播分为主播端和用户端&#xff0c;主播端有摄像头…

湿法蚀刻酸洗槽—— 应用半导体新能源光伏光电行业

PFA清洗槽又被称为防腐蚀槽、酸洗槽、溢流槽、纯水槽、浸泡槽、水箱、滴流槽&#xff0c;是四氟清洗桶后的升级款&#xff0c;是为半导体光伏光电等行业设计&#xff0c;一体成型&#xff0c;无需担心漏液。主要用于浸泡、清洗带芯片硅片电池片的花篮。 由于PFA的特点它能耐受…

分钟级实时数据分析的背后——实时湖仓产品解决方案

随着信息技术的深入应用&#xff0c;企业对市场的响应速度也在不断提升&#xff0c;而且这种响应速度正在变得越来越快&#xff0c;没有最快只有更快。对数据实时性要求的提高&#xff0c;是眼下很多企业遇到的一个新的挑战。 从生产侧的视角来看&#xff0c;系统实时监控与实…

大白话带你认识 JVM

大白话带你认识 JVM 文章目录 大白话带你认识 JVM前言一、JVM 的基本介绍1.1 Java 文件是如何被运行的① 类加载器② 方法区③ 堆④ 栈⑤ 程序计数器小总结1.2 简单的代码例子二、类加载器的介绍2.1 类加载器的流程2.1.1 加载2.1.2 链接2.1.3 初始化2.1.4 卸载2.2 类加载器的加…

c++入门学习(十八)赋值运算符

简单赋值运算符&#xff08;&#xff09;&#xff1a; 最基本的赋值运算符是“”。它表示将右侧的值赋给左侧的变量。例如&#xff0c;x 5意味着将值5赋给变量x。 增量赋值运算符&#xff1a; 这是一组在赋值的同时对变量进行递增操作的运算符。常见的有、-、*、/等。例如&…

选择海外云手机需要考虑什么?

随着跨境电商行业的蓬勃发展&#xff0c;企业们纷纷寻找提升平台流量和广告投放效果的方法&#xff0c;这已成为业界的当务之急。传统的宣传模式在国内受到直播和链接带货等新兴方式的冲击&#xff0c;而在国外&#xff0c;类似的趋势也在悄然兴起&#xff0c;呈现出广阔的发展…

基于Docker、Minikube在PC端构建K8S试验环境

在桌面电脑上使用Docker和Minikube构建Kubernetes&#xff08;K8S&#xff09;试验环境&#xff0c;为学习和测试提供了一个理想的平台。Docker的容器化技术允许在隔离的环境中运行应用&#xff0c;而Minikube则简化了在单节点上部署和管理Kubernetes集群的过程。这种组合使得个…

100.乐理基础-五线谱-是否需要学习五线谱

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;99.乐理基础-简谱的多声部-CSDN博客 简谱与五线谱的区别&#xff0c;各自的优劣势、使用场景、范围等&#xff1a; 要搞懂这个问题&#xff0c;其实核心就是四个词&#xff1a;首调、固定调、单声部、多声部 首调、…

高斯分布的应用,正态分布的实践应用,什么是极大似然估计法

目录 高斯分布的应用 正态分布的实践应用 什么是极大似然估计法 高斯分布的应用

Web04--Flex布局

1、flex布局 1.1 flex认识 1.2 flex组成 1.3 flex布局 1.3.1 主轴对齐方式 <!DOCTYPE html> <html lang"CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.…

Optional lab: Linear Regression using Scikit-LearnⅠ

scikit-learn是一个开源的、可用于商业的机器学习工具包&#xff0c;此工具包包含本课程中需要使用的许多算法的实现 Goals In this lab you will utilize scikit-learn to implement linear regression using Gradient Descent Tools You will utilize functions from sci…

【Hexo博客|Fluid主题】实现链接卡片效果

文章目录 前言一、CardLink库二、配置步骤1. 添加静态js文件2. 使库文件生效3. 编写启用CardLink4. 查看效果效果与前面一致。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/06e0630f994d4d67a90e18e291c3fdc5.png#pic_center) 总结 前言 今天在阅读Github…

windows?linux?如何使用JMeter

windows?linux?如何使用JMeter 安装JMeter的步骤以GUI模式启动JMeter如何在非GUI模式下运行JMeter在linux中使用JMeter 安装JMeter的步骤 JMeter 是一个纯 Java应用程序&#xff0c;应该在任何具有兼容Java实现的系统上正确运行。 安装 JMeter 的步骤 步骤1&#xff09;安…

freeRTOS总结(六)列表及列表项

1&#xff0c;列表和列表项的简介&#xff08;熟悉&#xff09; 1、列表就是一个双向循环链表&#xff0c;列表项就是其中的节点 2、其用途就是在三大链表&#xff08;挂起、阻塞、就绪&#xff09;中将任务&#xff08;列表项&#xff09;进行排序管理。 列表是 FreeRTOS 中的…

人工智能的未来展望:自然语言处理(NLP)与计算机视觉(CV)

NLP和CV是人工智能的两个重要分支&#xff0c;它们在处理和分析信息方面有不同的侧重点和挑战。 NLP&#xff08;自然语言处理&#xff09;旨在让计算机理解和生成人类语言&#xff0c;主要处理的是文本信息。NLP的研究和应用主要集中在如何让计算机理解和生成人类语言&#x…

一、windows_Dos命令——批处理命令

一、批处理编程 winr输入cmd 1、打开记事本 notepad 回车 记事本保存文件要以.bat后缀进行保存 2、显示对应.bat的盘符位置 echo off 3、echo输出字符串内容 echo "hello world" 回车 4、pause等待任意键输入 win r 回车 输入cmd 回车 echo "hel…

深度学习道路提取代码跑自己的训练集(一)——CoANet代码

首先去下载作者发布在github上面的代码 为了防止我们之前的虚拟环境遭到破坏 我们首先重新克隆一个虚拟环境 conda create --name pytorch2 --clone pytorch接下来 1. 在mypath.py中定义自己的数据集 class Path(object):staticmethoddef db_root_dir(dataset):if dataset…

element el-date-picker type=“datetimerange“

刚写完结果需求变更了。封装的时间组件重新做。结合eacharts 。 直接上代码了 日期选择组件封装 <template><section class"warning-container"><header class"query-head"><el-form :inline"true" class"query-form…

【华为 ICT HCIA eNSP 习题汇总】——题目集8

1、在VRP平台下&#xff0c;关于各个协议的外部优先级的描述&#xff0c;正确的是&#xff08;&#xff09;。 A、OSPF路由的外部优先级是15 B、IS-IS路由的外部优先级是10 C、静态路由的外部优先级是60 D、BGP路由的外部优先级是20 考点&#xff1a;路由技术原理 解析&#xf…