uniapp 多渠道打包实现方案

news2024/11/14 14:08:38

首先一个基础分包方案:
项目整体结构
build编译前相关问题
src资源文件和vue布局文件分包结构
包不用区分渠道,只是通过文件名进行区分,公共代码逻辑可以通过mixins进行混入。

这样分包后就需要在打包时只针对编译的渠道包文件进行替换打包,其他渠道包的文件不打包进去,通过工具类实现替换。

项目主目录下的manifest.json 和 pages.json 可以是空内容,都是通过工具类将prebuild目录下不同渠道不同平台的对应文件内容复制然后写到项目主目录下去的。

  1. 先看主要配置文件:
//package.json
{
	"name": "demo",
	"config": {
		"dev": "cross-env NODE_ENV=development uniapp-cli custom",
		"pro": "cross-env NODE_ENV=production uniapp-cli custom"
	},
	"version": "0.1.0",
	"private": true,
	"scripts": {
		"dev:ks-brandA": "cross-env-shell $npm_package_config_dev bd-brandA --minimize",
		"dev:tt-brandA": "cross-env-shell $npm_package_config_dev tt-brandA --minimize",
		"dev:bd-brandA": "cross-env-shell $npm_package_config_dev ks-brandA --minimize",
		"build:bd-brandA": "cross-env-shell $npm_package_config_pro bd-brandA --minimize",
		"build:tt-brandA": "cross-env-shell $npm_package_config_pro tt-brandA --minimize",
		"build:ks-brandA": "cross-env-shell $npm_package_config_pro ks-brandA --minimize",
		
		"dev:ks-brandB": "cross-env-shell $npm_package_config_dev bd-brandB --minimize",
		"dev:tt-brandB": "cross-env-shell $npm_package_config_dev tt-brandB --minimize",
		"dev:bd-brandB": "cross-env-shell $npm_package_config_dev ks-brandB --minimize",
		"build:bd-brandB": "cross-env-shell $npm_package_config_pro bd-brandB --minimize",
		"build:tt-brandB": "cross-env-shell $npm_package_config_pro tt-brandB --minimize",
		"build:ks-brandB": "cross-env-shell $npm_package_config_pro ks-brandB --minimize",

	},
	"dependencies": {
		"@dcloudio/uni-app": "^2.0.2-3081220230817001",
		"@dcloudio/uni-app-plus": "^2.0.2-3081220230817001",
		"@dcloudio/uni-h5": "^2.0.2-3081220230817001",
		"@dcloudio/uni-i18n": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-360": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-alipay": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-baidu": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-jd": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-kuaishou": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-lark": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-qq": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-toutiao": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-vue": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-weixin": "^2.0.2-3081220230817001",
		"@dcloudio/uni-mp-xhs": "^2.0.2-3081220230817001",
		"@dcloudio/uni-quickapp-native": "^2.0.2-3081220230817001",
		"@dcloudio/uni-quickapp-webview": "^2.0.2-3081220230817001",
		"@dcloudio/uni-stacktracey": "^2.0.2-3081220230817001",
		"@dcloudio/uni-stat": "^2.0.2-3081220230817001",
		"@dcloudio/uni-ui": "^1.4.28",
		"@vue/shared": "^3.0.0",
		"core-js": "^3.8.3",
		"crypto-js": "^3.1.9-1",
		"flyio": "^0.6.2",
		"vue": ">= 2.6.14 < 2.7",
		"vuex": "^3.2.0"
	},
	"devDependencies": {
		"@dcloudio/types": "^3.3.2",
		"@dcloudio/uni-automator": "^2.0.2-3081220230817001",
		"@dcloudio/uni-cli-i18n": "^2.0.2-3081220230817001",
		"@dcloudio/uni-cli-shared": "^2.0.2-3081220230817001",
		"@dcloudio/uni-helper-json": "*",
		"@dcloudio/uni-migration": "^2.0.2-3081220230817001",
		"@dcloudio/uni-template-compiler": "^2.0.2-3081220230817001",
		"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.2-3081220230817001",
		"@dcloudio/vue-cli-plugin-uni": "^2.0.2-3081220230817001",
		"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.2-3081220230817001",
		"@dcloudio/webpack-uni-mp-loader": "^2.0.2-3081220230817001",
		"@dcloudio/webpack-uni-pages-loader": "^2.0.2-3081220230817001",
		"@vue/cli-plugin-babel": "~5.0.0",
		"@vue/cli-service": "~5.0.0",
		"babel-plugin-import": "^1.11.0",
		"clean-webpack-plugin": "^4.0.0",
		"copy-webpack-plugin": "^11.0.0",
		"cross-env": "^7.0.2",
		"jest": "^25.4.0",
		"less": "^4.1.3",
		"less-loader": "^7.3.0",
		"mini-types": "*",
		"miniprogram-api-typings": "*",
		"postcss-comment": "^2.0.0",
		"sass": "^1.49.8",
		"sass-loader": "^8.0.2",
		"vue-template-compiler": ">= 2.6.14 < 2.7",
		"webpack-cli": "^5.1.4",
		"vconsole": "^3.15.0"
	},
	"browserslist": [
		"Android >= 4.4",
		"ios >= 9"
	],
	"uni-app": {
		"scripts": {
			"tt-brandA": {
				"env": {
					"UNI_PLATFORM": "h5"
				},
				"define": {
					"MP-BRANDA": true
				}
			},
			"ks-brandA": {
				"env": {
					"UNI_PLATFORM": "mp-kuaishou"
				},
				"define": {
					"MP-BRANDA": true
				}
			},

			"tt-brandB": {
				"env": {
					"UNI_PLATFORM": "h5"
				},
				"define": {
					"MP-BRANDB": true
				}
			},
			"ks-brandB": {
				"env": {
					"UNI_PLATFORM": "mp-kuaishou"
				},
				"define": {
					"MP-BRANDB": true
				}
			},
			"bd-brandB": {
				"env": {
					"UNI_PLATFORM": "mp-baidu"
				},
				"define": {
					"MP-BRANDB": true
				}
			},
		}
	}
}

//src/pages.js:: 直接复制 prebuild下对应brand和平台platform下的pages.js文件
const {getApp,getHostCode} = require("../prebuild/env");
const path = require('path')
module.exports = (pagesJson, loader) => {
	let brandName = getApp();
	let code = getHostCode()
	const srcPath = path.join(__dirname, `../prebuild/${brandName}/pages-${code}.js`)
	let pagesJsonNew = require(srcPath)
	loader.addDependency(require.resolve(srcPath))
	return pagesJsonNew
}

//vue.config.js
const fs = require("fs");
const CopyWebpackPlugin = require('copy-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const {getApp,getOS} = require("./prebuild/env");

/*
 * 本函数重定向输出路径到下面路径 *
 * */
function rebuildOutputPath(brand) {
	let separator = getOS() == 'win' ? '\\' : '/'
	const lastIndex = process.env.UNI_OUTPUT_DIR.lastIndexOf(separator)
	let pre = process.env.UNI_OUTPUT_DIR.substring(0, lastIndex)
	console.log("vue.config--buildOutputPath::",process.env.UNI_OUTPUT_DIR.substring(lastIndex))
	process.env.UNI_OUTPUT_DIR = process.env.UNI_OUTPUT_DIR.substring(lastIndex) + separator + brand
}

const brandName = getApp()
rebuildOutputPath(brandName)

let clearDir = ['static/img-*']
module.exports = {
	chainWebpack: (config) => {
		//本插件将图片资源目录例如img-brandA, 拷贝到输出目录的static/img下面
		config.plugin('copy').use(CopyWebpackPlugin, [{
			patterns: [{
				from: `src/static/img-${brandName}`,
				to: 'static/imgs/'
			}]
		}])
		//本插件将输出目录下,重复无效的/static/img-*目录删除掉
		config.plugin('clean').use(CleanWebpackPlugin, [{
			cleanAfterEveryBuildPatterns: clearDir
		}])
		//本插件进行预编译相关的拷贝工作,拷贝指定brand的manifest和生成build.less
		config.plugin('CopyPlugin').use(require('./prebuild/CopyPlugin', [{
			brand: brandName
		}]))
		//对目标brand相关的非公用源文件进行监控更新编译的loader
		config.module.rule('module_replace').test(/\.vue$/).pre().use('./prebuild/module_replace')
			.loader('./prebuild/module_replace').end()
	}
}

// 本代码模块配合module_replace的loader完成【对目标brand相关的非公用源文件进行监控更新编译】
const chokidar = require('chokidar');

//监测改动时触发编译
const ignoreFiles = [];
const brandSign = `-${brandName}.`
const watcher = chokidar.watch('src/').on('ready', async () => {
	await watcher.close()
	chokidar.watch('src/', {ignored: ignoreFiles})
		.on('change', (path, stats) => {
			console.log('changed file', path);
			let brandSignIndex = path.lastIndexOf(brandSign)
			let endFix = path.substring(brandSignIndex + brandSign.length)
			let preFix = path.substring(0, brandSignIndex)
			let destPath = preFix + '.' + endFix
			const destContent = fs.readFileSync(destPath, {encoding: "utf-8"})
			fs.writeFileSync(destPath, destContent + '\n')
			fs.writeFileSync(destPath, destContent)
		})
}).on('add', (path) => {
	if (path.indexOf(brandSign) <= 0) ignoreFiles.push(path)
})

vue.config.js 是项目打包入口,在里面使用了三个工具类,env.js CopyPlugin.js module_replace.js;通过不同的brand读取指定brand文件,重写到指定文件,实现分渠道打包

  1. 三个不可缺少工具类:
//env.js文件
module.exports = {
	getApp() {
		if (process.env.UNI_SCRIPT) {
			const script = process.env.UNI_SCRIPT
			let subs = script.split('-')
			if (subs.length == 2) return subs[1]
		}
		console.error('错误!!未检测到有效的brand名称!!')
		return 'default'
	},
	makeDateTimeString(date) {
		let year = date.getFullYear();
		let month = date.getMonth() + 1;
		let day = date.getDate();
		if (month < 10) month = '0' + month
		if (day < 10) day = '0' + day
		let hour = date.getHours();
		let min = date.getMinutes();
		let sec = date.getSeconds()
		if (min < 10) min = '0' + min
		if (sec < 10) sec = '0' + sec
		return `${year}-${month}-${day} ${hour}:${min}:${sec}`
	},
	getOS() {
		let host = 'ios'
		let arch = process.env.MSYSTEM_CARCH
		if (arch == 'x86_64' || arch == 'x86_32') host = 'win'
		return host
	},
	getHostCode() {
		if (process.env.UNI_SCRIPT) {
			const script = process.env.UNI_SCRIPT
			let subs = script.split('-')
			if (subs.length == 2) return subs[0]
		}
		console.error('错误!!未检测到有效的brand名称!!')
		return ''
	}
}

//CopyPlugin.js文件
const fs = require("fs");
const path = require('path');
const {
	getApp, getHostCode
} = require("./env");

class CopyPlugin {
	log(...param) {
		console.log("CopyPlugin", ...param)
	}
	constructor() {
		this.log('constructed')
	}
	apply(compiler) {
		compiler.hooks.environment.tap('Manifest', params => {
			let brandName = getApp()
			const platform = process.env.UNI_PLATFORM
			console.log('brandName=' + brandName, 'platform=' + platform)

			// 构造build.less
			let buildStr = '@brand: ' + brandName + ';'
			fs.writeFileSync('src/build.less', buildStr)
			
			//复制manifest.json
			let manifestPath = `prebuild/${brandName}/manifest.json`
			console.log('manifestPath=',manifestPath)
			let destManifestPath = 'src/manifest.json'
			const manifestContent = fs.readFileSync(manifestPath, {
				encoding: "utf-8"
			});
			fs.writeFileSync(destManifestPath, manifestContent)
		})
	}
}

module.exports = CopyPlugin

//module_replace.js 文件
const {getApp} = require("./env");
const fs = require("fs");
const tag = 'loader:module-replace'

const brandName = getApp()
const brandSign = `-${brandName}.`

module.exports = function(content) {
	let resourcePath = this.resourcePath
	let dotIndex = resourcePath.lastIndexOf('.')
	let endFix = resourcePath.substring(dotIndex)
	let srcPath = resourcePath.substring(0, dotIndex) + '-' + brandName + endFix
	if (srcPath.indexOf(brandSign) <= 0) return content
	if (!fs.existsSync(srcPath)) return content
	console.log(tag, 'replace from', srcPath)
	const newContent = fs.readFileSync(srcPath, {
		encoding: "utf-8"
	});
	return newContent
}

运行打包时只需要控制台中输入package.json中指定的脚本key值即可:
npm run dev:tt-barndA
npm run build:ks-barndB
按照上述方法分包后即使实现一个项目多平台多渠道打包,如有问题请多指教
资源包在下一篇文章

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

商业策划案怎么写?附商场230个策划案例

商业策划案的撰写是一个系统性工程&#xff0c;旨在详细阐述项目的背景、目标、实施策略、财务预测及风险评估等内容&#xff0c;以吸引投资者或合作伙伴的关注。 以下是一个详细的撰写步骤和要点&#xff0c;码字不易&#xff0c;如果回答对你有所帮助&#xff0c;请不吝给一…

GraphRAG如何使用ollama提供的llm model 和Embedding model服务构建本地知识库

使用GraphRAG踩坑无数 在GraphRAG的使用过程中将需要踩的坑都踩了一遍&#xff08;不得不吐槽下&#xff0c;官方代码有很多遗留问题&#xff0c;他们自己也承认工作重心在算法的优化而不是各种模型和框架的兼容性适配性上&#xff09;&#xff0c;经过了大量的查阅各种资料以…

【目标和】python刷题记录

R3-dp篇. 目录 思路&#xff1a; 增加记忆化搜索&#xff1a; 优化空间复杂度&#xff1a; 思路&#xff1a; class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:#设正数之和为p,总元素之和为s&#xff0c;带符号总元素之和为t&…

AWS开发人工智能:如何基于云进行开发人工智能AI

随着人工智能技术的飞速发展&#xff0c;企业对高效、易用的AI服务需求日益增长。Amazon Bedrock是AWS推出的一项创新服务&#xff0c;旨在为企业提供一个简单、安全的平台&#xff0c;以访问和集成先进的基础模型。本文中九河云将详细介绍Amazon Bedrock的功能特点以及其收费方…

安卓常用控件(上)

文章目录 TextViewButtonEditText TextView textview主要用于在界面上显示一段文本信息。 属性名描述id给当前控件定义一个唯一的标识符。layout_width给控件指定一个宽度。match_parent&#xff1a;控件大小与父布局一样&#xff1b;wrap_content&#xff1a;控件大小刚好够包…

WinUI vs WPF vs WinForms: 三大Windows UI框架对比

1.前言 在Windows平台上开发桌面应用程序时&#xff0c;WinUI、WPF和WinForms是三种主要的用户界面框架。每种框架都有其独特的特点和适用场景。本文将通过示例代码&#xff0c;详细介绍这些框架的优缺点及其适用场景&#xff0c;帮助dotnet桌面开发者更好地选择适合自己项目的…

【Spring】SSM框架整合Spring和SpringMVC

目录 1.项目结构 2.项目的pom.xml文件 3.spring.xml和springMVC配置文件 4.database.properties和mybatis.xml配置文件 5. 代码编写 6.测试整合结果 1.项目结构 首先创建一个名为ssm_pro的Mavew项目&#xff0c;然后再在主目录和资源目录下&#xff0c;创建如下所示的结…

5.2-软件工程基础知识-软件过程模型

软件过程模型 瀑布模型瀑布模型变种-V模型演化模型-原型模型增量模型演化模型-螺旋模型喷泉模型基于构件的开发模型形式化方法模型统一过程模型敏捷方法极限编程其他方法 软件过程模型概述练习题 瀑布模型 瀑布模型(SDLC):瀑布模型是一个经典的生命周期模型&#xff0c;一般将软…

SpringBoot中如何正确使用Redis(详细介绍,原理讲解,企业版)

1.引入Redis依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency> 2.配置Redis的连接信息(application.yml) 实际开发中有两个一个是开发环境applicati…

VBA字典与数组第十七讲:工作表数组大小的扩展及意义

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

JAVA(IO流)7.31

ok了家人们今天还是学习IO流&#xff0c; 一.打印流【了解】 1.1 打印流的概述 我们平时使用的System语句就是调用了print()方法和println()方法。 这两个方法都来自于 java.io.PrintStream 类。 作用&#xff1a; 该类能够方便地打印各种数据类型的值&#xff0c;写入数据后…

谷粒商城实战笔记-115-全文检索-ElasticSearch-进阶-bool复合查询

文章目录 1&#xff0c;must2&#xff0c;must not3&#xff0c;should 1&#xff0c;must {"query": {"bool": {"must": [{"match": {"gender": "M"}},{"match": {"address": "mill&q…

java代码审计-SQL的注入

0x01 前言 Java里面常见的数据库连接方式有三种&#xff0c;分别是JDBC&#xff0c;Mybatis&#xff0c;和Hibernate。 0x02 JDBC注入场景 很早之前的Javaweb都是用JDBC的方式连接数据库然后去实现dao接口再调service业务层去实现功能代码JDBC连接代码 WebServlet("/d…

科技云报道:大模型引领技术浪潮,AI安全治理面临“大考”

科技云报道原创。 从文生文到文生图&#xff0c;再到文生视频&#xff0c;近年来&#xff0c;以ChatGPT、Sora等为代表的大模型引领了全球人工智能技术与产业的新一轮浪潮。2024年更是被业内称为大模型应用爆发元年。 年初&#xff0c;Sora横空出世验证了Scalling Law在视频生…

计算机的错误计算(五十)

摘要 扩展了计算机的错误计算&#xff08;四十九&#xff09;中的代码。同时发现&#xff0c;误差也“扩展”了。 下面是代码&#xff1a; import torch# 设置随机种子 torch.manual_seed(0)# 创建张量并移动到GPU W1 torch.randn(5, 3) * 10 W1 W1.to(cuda) X1 torch.ran…

高级宏定义

平时常说的 C 语言三大预处理功能是什么&#xff1f;&#xff08;吹牛谈资&#xff0c;不能不知&#xff09; 答&#xff1a;宏定义&#xff1b;文件包含&#xff1b;条件编译。 说到底&#xff0c;宏定义的实质是什么&#xff1f; 答&#xff1a;替换。 关于宏定义有一点…

CSS技巧专栏:一日一例 18 -纯CSS实现背景浮光掠影的按钮特效

CSS技巧专栏:一日一例 18 -纯CSS实现背景浮光掠影的按钮特效 先发图,再说话: 案例图片 案例分析 按钮是好几种颜色的背景色组成的,使用css的话,应该会有几个不同颜色的层,在按钮后面移动。每个层互相叠加,大概还会用到图片混合模式产生了更多的叠加的颜色,然后边缘过…

云计算实训20——mysql数据库安装及应用(增、删、改、查)

一、mysql安装基本步骤 1.下载安装包 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 2.解压 tar -xf mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 3.卸载mariadb yum -y remove mariadb 查看解压后的包 [rootmysq…

二叉树遍历算法的应用

1、二叉树的创建 2、二叉树的复制 3、二叉树的深度 4、计算结点总个数