您现在的位置是:亿华云 > 域名
多项目集成下的工程脚手架配置方案
亿华云2025-10-09 15:03:35【域名】9人已围观
简介一、背景随着项目的复杂和功能的增加,一个工程下可能存在多个项目,这个时候我们单独开项目去开发的话项目代码会冗余,项目后期的维护成本也很高,而代码的冗余会造成静态资源包加载时间变长、执行时间也会变长,进
一、多项的工背景
随着项目的目集复杂和功能的增加,一个工程下可能存在多个项目,成下程脚这个时候我们单独开项目去开发的手架话项目代码会冗余,项目后期的配置维护成本也很高,而代码的多项的工冗余会造成静态资源包加载时间变长、执行时间也会变长,目集进而很直接的成下程脚影响性能和体验。为了解决此问题我们需要实现多项目的手架分模块打包,且项目之间共享组件和依赖,配置运行、多项的工打包时互不干扰。目集
二、成下程脚应用场景
以一个后台管理系统为例,手架我们同时有运营管理系统、配置商家管理系统、设备管理系统,还有一些内部的管理系统,这几个系统的菜单管理、权限管理、用户管理等相同业务模块较多,业务组件以及UI组件都要遵循公司的源码库规范,这种情况下就可以用一个 repo 来管理这些系统, 所有的设计文档、源代码、文件都放在一个 repo 里面。
三、技术方案
本文基于vue-cli3,核心是 vue.config.js 文件。vue-cli2实现方法类似,核心是 webpack.config.js 文件,这里不做过多阐述。
1. 功能
项目区分命令化项目配置化路由模块管理项目生成脚本化2. 技术点
process.argv [1] :获取命令行参数cross-env [2] :设置环境fs-extra [3] :命令行生成项目chalk [4] :命令行美化inquirer [5] :命令行交互node-progress [6] :加载进度条3. 思路
我们知道在 package.json 中有项目启动、打包的命令,我们可以从这里入手。我们的思路大概是这样的:
通过命令行输入的项目名称打包指定项目 处理命令行参数;配置公共文件和项目配置文件;设置当前运行/打包项目( project.js );打包项目所需的模块和资源;npm run dev projectA # 运行开发环境下的projectA项目
npm run build:dev projectA # 打包开发环境下的projectA项目
npm run build projectA # 打包projectA项目4. 目录结构
.
├── README.md
├── babel.config.js
├── config # 配置项
│ ├── build.js # 打包配置文件
│ ├── copy.js # 项目生成文件
│ ├── dev.js # 开发配置文件
│ ├── project.js # 获取项目项目信息
│ └── projectConfig.js # 项目配置文件(和普通的脚手架配置项一样)
├── package.json # 项目依赖
├── postcss.config.js # postcss配置文件
├── project # 工程信息配置
│ ├── index.js
│ ├── module # 公共路由模块
│ └── projects # 公共项目信息
├── public
│ └── index.html
├── src
│ ├── assets # 公共资源文件
│ │ └── logo.png
│ ├── components # 公共组件
│ │ ├── 404.vue
│ │ └── main.vue
│ └── projects # 项目目录(独立的路由 状态管理 接口请求)
│ ├── projectA
│ ├── projectB
│ └── projectC
├── temp # 项目模板文件(可根据项目需求定制)
│ ├── App.vue
│ ├── components
│ ├── main.js
│ ├── page
│ │ └── Home.vue
│ ├── router.js
│ └── store.js
├── vue.config.js # 核心配置文件
└── yarn.lock
13 directories, 26 files好了,我们的视图目录结构大概就是上面的样子,我们期望的亿华云是打包 src 目录下这个 A项目 就像打包一个完整的项目一样。那么如何实现这部分呢?
5. 流程图
6 项目配置
1) 修改package.json配置这里就不得不提到 cross-env 这个模块,我们之前在生产、沙箱、测试、开发环境时也会用到这个命令。
npm i --save-dev cross-env代码:"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"dev": "cross-env NODE_ENV=development node config/dev.js",
"test": "cross-env NODE_ENV=test node config/dev.js",
"pre": "cross-env NODE_ENV=preview node config/dev.js",
"prd": "cross-env NODE_ENV=production node config/dev.js",
"build:dev": "cross-env NODE_ENV=development node config/build.js",
"build:test": "cross-env NODE_ENV=test node config/build.js",
"build:pre": "cross-env NODE_ENV=preview node config/build.js",
"build:prd": "cross-env NODE_ENV=production node config/build.js",
"copy": "node config/copy.js"
}2) 编写项目代码此版本为 简易demo ,配置完运行命令和打包命令我们就可以编写项目中的业务代码了。
路径: src/projects/projectA/App.vue
alt="项目A"
src="https://dummyimage.com/300x300/FF0097/fff&text=PROJECT-A"
/>
......
3) 配置项目信息在项目根目录建立 config 文件夹,在其中新建 projectsConfig.js 写入:
const projectName = require("./project");
const config = {
// $ 项目A
projectA: {
pages: {
index: {
entry: "src/projects/projectA/main.js",
template: "public/index.html",
filename: "index.html",
title: "projectA"
},
},
devServer: {
port: 7777, // 端口地址
}
},
// $ 项目B
projectB: {
pages: {
index: {
entry: "src/projects/projectB/main.js",
template: "public/index.html",
filename: "index.html",
title: "projectB"
},
},
devServer: {
port: 8888, // 端口地址
}
},
// $ 项目C
projectC: {
pages: {
index: {
entry: "src/projects/projectC/main.js",
template: "public/index.html",
filename: "index.html",
title: "projectC"
},
},
devServer: {
port: 9999, // 端口地址
}
},
};
const configObj = config[projectName.name];
// $ 这里导出的是当前运行项目的配置
module.exports = configObj;
4) 运行时配置开始前先讲下 process.argv 它返回的是一个数组,其中包含启动 Node.js 进程时传入的命令行参数。第一个元素将是 process.execPath , 第二个元素将是正在执行的 JavaScript文件的路径,其余元素将是任何其他命令行参数。
const fse = require("fs-extra");
const chalk = require(chalk);
let projectName = process.argv[2]; // $ 获取命令行项目名称
if(!projectName) throw(chalk`{ red.bold.bgWhite ------项目不存在,请检查配置------}`);
console.log(chalk.red.bold(`正在运行---${ projectName}项目`), `${ process.env.NODE_ENV} 环境`, )
fse.writeFileSync(./config/project.js, `exports.name = ${ projectName}`)
let exec = require(child_process).execSync;
exec(npm run serve, { stdio: inherit});Tips:命令行参数是固定格式 npm run dev projectA ,少了项目名称会提示项目不存在。
这里就比较简单了,根据当前项目名称进行打包即可
const projectName = process.argv[2]
const fse = require("fs-extra");
fse.writeFileSync(./config/project.js, `exports.name = ${ projectName}`)
const str = npm run build
const exec = require(child_process).execSync;
exec(str, { stdio: inherit});6) 配置Vue CLI通过 process.argv 获取当前命令行的项目名称,b2b供应网判断命令行的项目名称是否在项目列表里,如果没有给出异常提示;设置当前运行项目的脚手架信息;终端命令提示;const path = require(path)
const conf = require(./config/projectConfig); // $ 当前项目
const chalk = require(chalk); // $ 终端颜色设置插件
const ProgressBarPlugin = require(progress-bar-webpack-plugin); // $ 进度条插件
const PROJECTNAME = require(./config/project.js).name;
if(!conf) throw(chalk`{ black.bold.bgWhite ------项目不存在,请检查配置 777------}`);
const assetsDir =
function getAssetPath (assetsDir, filePath) {
return assetsDir
? path.posix.join(assetsDir, filePath)
: filePath
}
module.exports = {
pages: conf.pages, // $ 当前项目页面
outputDir: "dist/" + projectName + "/", // $ 设置输出目录名
assetsDir: static, // $ 增加static文件夹
lintOnSave: process.env.NODE_ENV !== production, // $ 是否在开发环境下通过 eslint-loader 在每次保存时 lint 代码
productionSourceMap: false, // $ 是否需要生产环境的 source map
devServer: conf.devServer, // $ 看项目需求 可配可不配
configureWebpack: {
plugins: [
new ProgressBarPlugin({
width: 50, // 默认20,进度格子数量即每个代表进度数,如果是20,那么一格就是5。
// format: build [:bar] :percent (:elapsed seconds),
format: chalk.blue.bold("build") + chalk.yellow([:bar] ) + chalk.green.bold(:percent) + (:elapsed秒),
// stream: process.stderr, // 默认stderr,输出流
// complete: "~", // 默认“=”,完成字符
clear: false, // 默认true,完成时清除栏的选项
// renderThrottle: "", // 默认16,更新之间的最短时间(以毫秒为单位)
callback() { // 进度条完成时调用的可选函数
console.log(chalk.red.bold("---->>>>编译完成<<<<----"))
}
}),
]
},
// $ 对内部的 webpack 配置进行更细粒度的修改
chainWebpack: config => {
// $ 修复HMR
config.resolve.symlinks(true);
// $ 制定环境打包js路径
const filename = getAssetPath(
assetsDir,
`static/js/[name].js`
)
config.mode(production).devtool(false).output.filename(filename).chunkFilename(filename)
config.performance.set(hints, false)
},
css: {
extract: false // $ 是否将组件中的 CSS 提取至一个独立的 CSS 文件中 (而不是动态注入到 JavaScript 中的 inline 代码)
loaderOptions: {
sass: {
implementation: require(sass),
fiber: require(fibers)
}
}
}
}配置终端插件的效果图:
写到这里我们就建立一个完整的小vue项目了,我们运行看看效果:
npm run dev projectA如图:
8) 打包效果npm run build:projectA
cd dist/projectA
live-server --port=9999live-server 是一个具有实时加载功能的小型服务器,在项目中用live-server作为一个实时服务器查看开发的网页或项目效果
7. 自动化生成模板项目
1) 流程图2) 思路整理本文涉及到脚手架里边与命令行交互的知识点,感兴趣的可以拷贝文末 demo 去练习下;这里主要是针对新建的模板做拷贝处理,流程节点中执行拷贝命令后输入的项目名称提示在本地已存在是否需要删除或者覆盖,根据实际业务场景做处理,这里不做过多探讨;示例代码涉及到的模板代码存放在工程根目录,也可以放在 src 目录下,不做强制要求;3) 执行命令npm run copy4) 示例代码fs-extra :添加了未包含在 原生fs模块 中的文件系统方法,并向fs方法添加了promise支持;fse.pathExists :判断当前要拷贝的项目是否存在;fse.copy :拷贝模板文件到指定目录;const fse = require("fs-extra");
const chalk = require("chalk");
const path = require("path");
const inquirer = require("inquirer");
inquirer
.prompt([
{
type: "input",
name: "projectName",
message: "请输入要生成的项目名称",
},
])
.then((answers) => {
createProject(answers.projectName);
});
// $ 拷贝项目模板
const createProject = (projectName) => {
const currentTemp = path.join(`./src/projects/${ projectName}`);
// $ 判断当前要拷贝的项目是否存在
fse.pathExists(currentTemp, (err, exists) => {
console.log(err, exists); // $ => null, false
// $ 根据用户选择是否替换本项目或者删除本项目
if (exists) {
// $ 这里也可以覆盖原项目或者dong
inquirer
.prompt([
{
type: "input",
name: "projectName",
message: "项目已存在,请重新输入项目名称",
},
])
.then((answers) => {
createProject(answers.projectName);
});
// throw chalk`{ red.bold.bgWhite >>> ${ projectName} <<< 项目已经存在}`;
} else {
// $ 拷贝模板文件到指定目录
fse.copy("./temp", path.join(`./src/projects/${ projectName}`), (err) => {
// if (err) return console.error(err)
if (err)
throw chalk`{ red.bold.bgWhite ------${ projectName}项目拷贝失败 ${ err}------}`;
console.log(chalk.red.bold(`--->>>${ projectName}项目拷贝成功`));
});
}
});
};8 优缺点
优点:方便统一管理项目;项目之间共享组件和依赖;运行、打包时互不干扰;支持同时运行多个项目;对于公共模块一次提交可以解决所有子项目的问题;缺点:执行拷贝模板命令后生成的项目需要在 config/projectConfig.js 文件中手动配置项目信息;随着项目的增加路由文件的提交在每次代码的时候都需要进行 Code Review ,不然的话不熟悉项目的同学很可能会在解决冲突的过程中把冲突的模块删除;随着程序规模的不断增加,代码量的增加,文档的增加,整个 repo 会变得越来越大;四、思考
有兴趣的童鞋可以考虑以下两个问题:
项目中有公共路由我们应该如何处理?状态管理和接口管理在这个工程下如何处理?五、总结
通过以上的分析,我们应该对同一工程下多项目配置化打包的大概流程有基本的了解,而上边的方案也只是其中的一种实现方式。写本文的目的主要是给大家提供一种思路,以后在遇到工程需要定制化的时候就可以通过更改脚手架的配置来实现。
Demo :[https://github.com/licairen/multi_project_demo](
很赞哦!(882)
相关文章
- 互联网其实拼的也是人脉,域名投资也是一个时效性很强的东西,一个不起眼的消息就会引起整个域名投资市场的动荡,因此拓宽自己的人脉圈,完善自己的信息获取渠道,让自己能够掌握更为多样化的信息,这样才更有助于自己的域名投资。
- 戴尔科技PowerEdge服务器 可充分满足人工智能工作负载和业务需求
- 戴尔科技构建智能化运维新平台,降低人力成本提升了综合效益
- 云服务器和物理服务器哪个好?
- 国内域名
- 用Nginx禁止国外IP访问我的网站...
- 龙芯中科构筑自主生态长城 护航产业数字化转型
- Ingress-Nginx工作原理和实践
- 审核通过的域名将显示在域名竞拍页面,并进入正式拍卖期,买家可以在拍卖周期内出价,加价幅度与拍卖保证金说明,点此查看。
- 数据中心和人工智能如何支持远程工作场所?