vue3.x 一套代码打包多环境多项目

一套代码打包多环境多项目


版本号:

vue/cli:4.5.12
node:v14.15.3
npm:6.14.9

需求
  • 代码多环境打包很常见,直接说说多项目打包:
  • 多项目打包就是一套代码,只有logo,部分样式,标题等不同。
  • 如果分好几套代码,后期改需求总不能每套代码都改一遍,所以就需要多项目打包。
    (当然,如果后期项目差异过大时,还是强烈建议把项目区分开来)
多环境打包实现
  1. package.jsonscriptspush以下代码
"dev": "vue-cli-service build --mode dev",
"pre": "vue-cli-service build --mode pre",
"prod": "vue-cli-service build --mode prod"
  1. 在项目根目录创建以下三个文件。注意:.env.之后的名称与 --mode 之后的要一致
.env.dev			--开发/测试环境
.env.pre			--预发布环境
.env.prod			--生产/线上环境

多环境打包

  1. .env.dev举例
    这里需要注意以下几点

    1. 不允许有注释
    2. NODE_ENV相当于自定义打包的环境变量
    3. 接口地址使用 '//xxx.xxx.com/' 这样的好处就是
      1. 会根据项目打开是https或者http进行自动切换
      2. 不能用逗号,句号,分号等。例如NODE_ENV = 'development';
      3. 不能写成'//xxx.xxx.com',否则拼接api时不会出现https://xxx.xxx.comtest/testA情况
      4. 不能写成'"//xxx.xxx.com/"'
NODE_ENV = 'development'
VUE_APP_TITLE = 'dev'
VUE_APP_BASE_API = '//xxx.xxx.com/'

.env.pre

NODE_ENV = 'preview'
VUE_APP_TITLE = 'pre'
VUE_APP_BASE_API = '//xxx.xxx.com/'

.env.prod

NODE_ENV = 'production'
VUE_APP_TITLE = 'prod'
VUE_APP_BASE_API = '//xxx.xxx.com/'
  1. request.js中进行设置即可
const service = axios.create({
	// axios中请求配置有baseURL选项,表示请求URL公共部分
	baseURL: process.env.VUE_APP_BASE_API,
	// 超时
	timeout: 10000
})

当然其他地方如果需要根据不同环境写逻辑的话也可以配置后使用process.env.xxx
如果只用多环境到此就可以结束了。
需要使用多项目的继续冲!!!

多环境打包实现
  1. package.jsonscriptspush以下代码
"switch": "node build/switch.js",
"current": "node build/current.js",
  1. 在项目根目录创建以下文件。
|-----build	打包配置
|		|-current.js 运行 npm run current。作用:查询当前项目。
|		|-project.js 全部项目的配置文件。
|		|-switch.js 运行 npm run switch 调用的文件。例如:npm run switch projectA 。作用:切换项目。
|		|-temp.js 	当前项目的临时文件,使用module.exports导出主要供配置文件使用。

多项目打包

  1. 先从project.js文件开始
module.exports = {
	projectA: {
		name: "projectA", // 项目名称
		title: "项目A", // 项目标题
		version:"1.0.0", // 版本号
		// 不同环境的接口地址
		dev: {
			baseUrl: "//xxx.com/"
		},
		pre: {
			baseUrl: "//xxx.com/"
		},
		prod: {
			baseUrl: "//xxx.com/"
		},
		targetUrl: "https://xxxx.com",// 本地启动时服务器的接口地址
		isShowLoginLogo: false,// 是否展示登录LOGO
	},
	projectB: {
		name: "projectB",
		title: "项目B",
		version:"1.0.0",
		dev: {
			baseUrl: "//xxx.com/"
		},
		pre: {
			baseUrl: "//xxx.com/"
		},
		prod: {
			baseUrl: "//xxx.com/"
		},
		targetUrl: "https://xxxx.com",
		isShowLoginLogo: true,
	}
}
  1. 敲黑板哒哒哒,重点来了:switch.js
var fs = require('fs');
var path = require('path');
var project = require('./project');

/**
 * fDelete 删除文件
 * @param {String} src 需要删除的文件夹地址
 * @returns {Null}
 */
var fDelete = function(src) {
	//同步读取当前目录
	let paths = fs.readdirSync(src);
	// 是否删除成功
	var isDeleteSuc = false;
	var pathsLength = paths.length;
	paths.forEach(function(path, index) {
		var _src = src + '/' + path;
		// unlinkSync同步删除 fs.unlinkSync(path);
		// unlink 异步删除 fs.unlink (path,callback);
		fs.unlink(_src, error => {
			if (error) return console.log("删除文件失败,原因是" + error.message);
			isDeleteSuc = true;
			if (index === pathsLength - 1) {
				console.log("img delete success!");
				fCheckDirectory("./src/assets/images/" + projectName, "./src/assets/images/project",
					fCopyFolder, true);
				fs.readdir("./src/assets/images/project", (err, files) => {
					if (error) return console.log("读取文件./src/assets/images/project失败,原因是" +
						error.message);
					console.log("images updata success!");
				});
				fCheckDirectory("./src/assets/images/" + projectName + "/favicon.ico",
					"./public/favicon.ico", fCopy, false);
			};
		})
	});
};

/**
 * fCopy 复制文件
 * @param {String} src 需要被复制的文件地址
 * @param {String} dst 需要复制到的文件地址
 * @returns {Null}
 */
var fCopy = function(src, dst) {
	let readable = fs.createReadStream(src); //创建读取流
	let writable = fs.createWriteStream(dst); //创建写入流
	readable.pipe(writable);
	writable.on('finish', function() {
		console.log('favicon.ico updata success!');
	});
}
/**
 * fCopyFolder 复制文件夹下所有内容
 * @param {String} src 需要被复制的文件夹地址
 * @param {String} dst 需要复制到的文件夹地址
 * @returns {Null}
 */
var fCopyFolder = function(src, dst) {
	let paths = fs.readdirSync(src); //同步读取当前目录
	paths.forEach(function(path, index) {
		var _src = src + '/' + path;
		var _dst = dst + '/' + path;
		fs.stat(_src, function(err, stats) { //stats  该对象 包含文件属性
			if (err) throw err;
			if (stats.isFile()) { //如果是个文件则拷贝 
				let readable = fs.createReadStream(_src); //创建读取流
				let writable = fs.createWriteStream(_dst); //创建写入流
				readable.pipe(writable);
			} else if (stats.isDirectory()) { //是目录则 递归 
				fCheckDirectory(_src, _dst, fCopyFolder, true);
			}
		});
	});
}
/**
 * fCheckDirectory 检查目录
 * @param {String} src 需要被复制的文件夹地址
 * @param {String} dst 需要复制到的文件夹地址
 * @param {Function} callback 回调参数
 * @param {Boolean} isMkdir 是否创建目录
 * @returns {Null}
 */
var fCheckDirectory = function(src, dst, callback, isMkdir) {
	// fs.access 判断文件的状态; fs.constants.F_OK 除了判断文件是否存在(默认模式),还可以用来判断文件的权限。
	fs.access(dst, fs.constants.F_OK, (err) => {
		if (err) {
			if (isMkdir) {
				// mkdir创建目录
				fs.mkdirSync(dst);
			}
			callback(src, dst);
		} else {
			callback(src, dst);
		}
	});
};

// 截取npm run switch projectA
// aCommand为数组
var aCommand = process.argv.splice(2);
// 默认切换项目名为projectA---console.log(aCommand.length, aCommand[0])
var projectName = aCommand[0] ? aCommand[0] : "projectA";
console.log("当前运行项目:" + projectName);
// console.log(projectName)
// console.log(project[projectName]);

// 当前项目 字符串
var oCurrentProject = project[projectName];
var sCurrentProject = null;
if (oCurrentProject) {
	sCurrentProject = JSON.stringify(oCurrentProject)
} else {
	return new TypeError("-----项目不存在,请检查-----");
}

var jsExport = 'export default ' + sCurrentProject;
var jsModuleExports = 'module.exports = ' + sCurrentProject;

fs.writeFile(path.join(__dirname, '../src/utils/config.js'), jsExport, function(err) {
	if (err) throw err;
	console.log("config.js updata success!");
});
fs.writeFile(path.join(__dirname, './temp.js'), jsModuleExports, function(err) {
	if (err) throw err;
	console.log("temp.js updata success!");
});

// 修改html的title
fs.readFile(path.join(__dirname, '../public/index.html'), "utf8", function(err, data) {
	if (err) throw err;
	console.log("index.html read success!");
	var html = "";
	html = data.replace(/<title>([\S\s\t]*?)<\/title>/g, "<title>" + oCurrentProject.title + "</title>")
	fs.writeFile(path.join(__dirname, '../public/index.html'), html, function(err) {
		if (err) throw err;
		console.log("index.html updata success!");
	});
});
fDelete("./src/assets/images/project");
  1. 准备区分项目所属图片
    将有差异的图片存放到和项目名相同的文件里
    switch.js会帮我们把项目对应的文件夹里的所有内容存入到project
    所以我们在每个项目对应的文件夹中都需要存入名字相同但内容不同的图片
    比如logo,背景页面等等
    因此页面引用时,只需要引入project文件夹的内容
    这里需要注意一点:
    projectA_001.png在两个项目都有,但是为什么是projectA_001.png而不是img_003.png呢?
    因为这是projectA项目独有的文件,其他项目如果没有对应的图片。
    那么就把该图片放到其他项目中作为区分。
    多项目图片文件夹
  2. 这个时候我们可以使用命令 npm run switch projectA 进行项目切换
    你会发现生成的src/utils/config.jsbuild/temp.js内容基本一致只是导出方式不同
    这牵扯到 nodejs中module.exports和exports的区别 以后有机会讲讲

你暂时可以这么理解:

build/temp.js module.exports主要用于node加载的js文件。例如vue.config.jsbuild文件夹下的js文件
src/utils/config.js export主要用于项目中的js文件。例如main.jssrc/utils文件夹下的js文件

  1. current.js
    安装 npm install --save-dev colors
    我的版本号为:"colors": "^1.4.0"
var colors = require('colors');
colors.setTheme({
	silly: 'rainbow',
	input: 'grey',
	verbose: 'cyan',
	prompt: 'red',
	info: 'green',
	data: 'blue',
	help: 'cyan',
	warn: 'yellow',
	debug: 'magenta',
	error: 'red'
});
var temp = require('./temp');
var project = require('./project');
var current = "no init!";
if (temp && temp.name) {
	current = temp.name
}
console.log("===================================================")
console.log("当前项目:" + current)
console.log("===================================================")

for (var key in project) {
	if (key == current) {
		console.log("* " + key.info)
	} else {
		console.log("  " + key.debug)
	}
}

colors就是想给代码增添一些色彩~就像生活一样
这个时候使用 npm run current 查看当前项目
8. 最后再修改一下需要用到动态文件的地方
vue.config.js

const temp = require('./build/temp');
// 时间戳保证不会版本重复
const Timestamp = new Date().getTime();
const { version,targetUrl } = temp;
module.exports = {
	devServer: {
		https: true, // 允许https接口代理
		proxy: {
			'/': {
				target: targetUrl, //服务器的接口地址 // 重写路径
				changeOrigin: true, //是否是跨域请求
				ws: false, //开启WebSocket
				secure: true, // 如果是https接口,需要配置这个参数
				pathRewrite: {
					'^/': ''
				}
			}
		}
	},
	css: {
		extract: process.env.NODE_ENV === 'production' ? {
			ignoreOrder: true,
		} : false,
	},
	configureWebpack: {
		output: { // 输出重构  打包编译后的 文件名称  【模块名称.版本号.时间戳】
			filename: `js/[name].${version}.${Timestamp}.js`,
			chunkFilename: `js/[name].${version}.${Timestamp}.js`
		},
		performance: {
			hints: 'warning',
			//入口起点的最大体积 整数类型(以字节为单位)
			maxEntrypointSize: 50000000,
			//生成文件的最大体积 整数类型(以字节为单位 300k)
			maxAssetSize: 30000000,
			//只给出 js 文件的性能提示
			assetFilter: function(assetFilename) {
				return assetFilename.endsWith('.js');
			}
		}
	}
}

最后一步注意了昂!
request.js

import uConfig from '@/utils/config';

const service = axios.create({
	// axios中请求配置有baseURL选项,表示请求URL公共部分
	baseURL: process.env.VUE_APP_TITLE && uConfig[process.env.VUE_APP_TITLE] ? uConfig[process.env.VUE_APP_TITLE].baseUrl : uConfig.dev.baseUrl,
	// 超时
	timeout: 10000
})

这样写是因为非本地项目是根据项目对应的环境切换baseURL
而本地启动项目的时候,没有VUE_APP_TITLE,所以需要直接使用项目dev环境的baseUrl

完结撒花~~✿✿ヽ(°▽°)ノ✿

如有不正确的地方,欢迎留言讨论!

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值