自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

多項(xiàng)目集成下的工程腳手架配置方案

開(kāi)發(fā)
寫(xiě)本文的目的主要是給大家提供一種思路,以后在遇到工程需要定制化的時(shí)候就可以通過(guò)更改腳手架的配置來(lái)實(shí)現(xiàn)。

一、背景

隨著項(xiàng)目的復(fù)雜和功能的增加,一個(gè)工程下可能存在多個(gè)項(xiàng)目,這個(gè)時(shí)候我們單獨(dú)開(kāi)項(xiàng)目去開(kāi)發(fā)的話項(xiàng)目代碼會(huì)冗余,項(xiàng)目后期的維護(hù)成本也很高,而代碼的冗余會(huì)造成靜態(tài)資源包加載時(shí)間變長(zhǎng)、執(zhí)行時(shí)間也會(huì)變長(zhǎng),進(jìn)而很直接的影響性能和體驗(yàn)。為了解決此問(wèn)題我們需要實(shí)現(xiàn)多項(xiàng)目的分模塊打包,且項(xiàng)目之間共享組件和依賴,運(yùn)行、打包時(shí)互不干擾。

二、應(yīng)用場(chǎng)景

以一個(gè)后臺(tái)管理系統(tǒng)為例,我們同時(shí)有運(yùn)營(yíng)管理系統(tǒng)、商家管理系統(tǒng)、設(shè)備管理系統(tǒng),還有一些內(nèi)部的管理系統(tǒng),這幾個(gè)系統(tǒng)的菜單管理、權(quán)限管理、用戶管理等相同業(yè)務(wù)模塊較多,業(yè)務(wù)組件以及UI組件都要遵循公司的規(guī)范,這種情況下就可以用一個(gè) ??repo?? 來(lái)管理這些系統(tǒng), 所有的設(shè)計(jì)文檔、源代碼、文件都放在一個(gè) ??repo?? 里面。

三、技術(shù)方案

本文基于vue-cli3,核心是 ??vue.config.js?? 文件。vue-cli2實(shí)現(xiàn)方法類似,核心是 ??webpack.config.js?? 文件,這里不做過(guò)多闡述。

1. 功能

  • 項(xiàng)目區(qū)分命令化
  • 項(xiàng)目配置化
  • 路由模塊管理
  • 項(xiàng)目生成腳本化

2. 技術(shù)點(diǎn)

  • process.argv [1] :獲取命令行參數(shù)
  • cross-env [2] :設(shè)置環(huán)境
  • fs-extra [3] :命令行生成項(xiàng)目
  • chalk [4] :命令行美化
  • inquirer [5] :命令行交互
  • node-progress [6] :加載進(jìn)度條

3. 思路

我們知道在 ??package.json?? 中有項(xiàng)目啟動(dòng)、打包的命令,我們可以從這里入手。我們的思路大概是這樣的:

  1. 通過(guò)命令行輸入的項(xiàng)目名稱打包指定項(xiàng)目   處理命令行參數(shù);
  2. 配置公共文件和項(xiàng)目配置文件;
  3. 設(shè)置當(dāng)前運(yùn)行/打包項(xiàng)目( project.js );
  4. 打包項(xiàng)目所需的模塊和資源;
npm run dev projectA           # 運(yùn)行開(kāi)發(fā)環(huán)境下的projectA項(xiàng)目
npm run build:dev projectA # 打包開(kāi)發(fā)環(huán)境下的projectA項(xiàng)目
npm run build projectA # 打包projectA項(xiàng)目

4. 目錄結(jié)構(gòu)

.
├── README.md
├── babel.config.js
├── config # 配置項(xiàng)
│ ├── build.js # 打包配置文件
│ ├── copy.js # 項(xiàng)目生成文件
│ ├── dev.js # 開(kāi)發(fā)配置文件
│ ├── project.js # 獲取項(xiàng)目項(xiàng)目信息
│ └── projectConfig.js # 項(xiàng)目配置文件(和普通的腳手架配置項(xiàng)一樣)
├── package.json # 項(xiàng)目依賴
├── postcss.config.js # postcss配置文件
├── project # 工程信息配置
│ ├── index.js
│ ├── module # 公共路由模塊
│ └── projects # 公共項(xiàng)目信息
├── public
│ └── index.html
├── src
│ ├── assets # 公共資源文件
│ │ └── logo.png
│ ├── components # 公共組件
│ │ ├── 404.vue
│ │ └── main.vue
│ └── projects # 項(xiàng)目目錄(獨(dú)立的路由 狀態(tài)管理 接口請(qǐng)求)
│ ├── projectA
│ ├── projectB
│ └── projectC
├── temp # 項(xiàng)目模板文件(可根據(jù)項(xiàng)目需求定制)
│ ├── App.vue
│ ├── components
│ ├── main.js
│ ├── page
│ │ └── Home.vue
│ ├── router.js
│ └── store.js
├── vue.config.js # 核心配置文件
└── yarn.lock

13 directories, 26 files

好了,我們的視圖目錄結(jié)構(gòu)大概就是上面的樣子,我們期望的是打包 ??src?? 目錄下這個(gè) ??A項(xiàng)目?? 就像打包一個(gè)完整的項(xiàng)目一樣。那么如何實(shí)現(xiàn)這部分呢?

5. 流程圖

6 項(xiàng)目配置

1) 修改package.json配置

這里就不得不提到 ??cross-env?? 這個(gè)模塊,我們之前在生產(chǎn)、沙箱、測(cè)試、開(kāi)發(fā)環(huán)境時(shí)也會(huì)用到這個(gè)命令。

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) 編寫(xiě)項(xiàng)目代碼

此版本為 ??簡(jiǎn)易demo?? ,配置完運(yùn)行命令和打包命令我們就可以編寫(xiě)項(xiàng)目中的業(yè)務(wù)代碼了。

路徑: ??src/projects/projectA/App.vue??

<template>
<div id="app">
<img
alt="項(xiàng)目A"
src="https://dummyimage.com/300x300/FF0097/fff&text=PROJECT-A"
/>
<router-view />
</div>
</template>

<style lang="scss">
......
</style>

路徑: ??src/projects/projectB/App.vue??

<template>
<div id="app">
<img
alt="項(xiàng)目B"
src="https://dummyimage.com/300x300/ff55ee/fff&text=PROJECT-B"
/>
<router-view />
</div>
</template>

<style lang="scss">
......
</style>


3) 配置項(xiàng)目信息

在項(xiàng)目根目錄建立 ??config?? 文件夾,在其中新建 ??projectsConfig.js?? 寫(xiě)入:

const projectName = require("./project");
const config = {
// $ 項(xiàng)目A
projectA: {
pages: {
index: {
entry: "src/projects/projectA/main.js",
template: "public/index.html",
filename: "index.html",
title: "projectA"
},
},
devServer: {
port: 7777, // 端口地址
}
},
// $ 項(xiàng)目B
projectB: {
pages: {
index: {
entry: "src/projects/projectB/main.js",
template: "public/index.html",
filename: "index.html",
title: "projectB"
},
},
devServer: {
port: 8888, // 端口地址
}
},
// $ 項(xiàng)目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];
// $ 這里導(dǎo)出的是當(dāng)前運(yùn)行項(xiàng)目的配置
module.exports = configObj;


4) 運(yùn)行時(shí)配置

開(kāi)始前先講下 ??process.argv?? 它返回的是一個(gè)數(shù)組,其中包含啟動(dòng) Node.js 進(jìn)程時(shí)傳入的命令行參數(shù)。第一個(gè)元素將是 ??process.execPath?? , 第二個(gè)元素將是正在執(zhí)行的 JavaScript文件的路徑,其余元素將是任何其他命令行參數(shù)。

const fse = require("fs-extra");
const chalk = require('chalk');
let projectName = process.argv[2]; // $ 獲取命令行項(xiàng)目名稱
if(!projectName) throw(chalk`{red.bold.bgWhite ------項(xiàng)目不存在,請(qǐng)檢查配置------}`);
console.log(chalk.red.bold(`正在運(yùn)行---${projectName}項(xiàng)目`), `${process.env.NODE_ENV} 環(huán)境`, )
fse.writeFileSync('./config/project.js', `exports.name = '${projectName}'`)

let exec = require('child_process').execSync;
exec('npm run serve', {stdio: 'inherit'});

Tips:命令行參數(shù)是固定格式 ??npm run dev projectA?? ,少了項(xiàng)目名稱會(huì)提示項(xiàng)目不存在。

5) 打包時(shí)配置

這里就比較簡(jiǎn)單了,根據(jù)當(dāng)前項(xiàng)目名稱進(jìn)行打包即可

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

  • 通過(guò) process.argv 獲取當(dāng)前命令行的項(xiàng)目名稱,判斷命令行的項(xiàng)目名稱是否在項(xiàng)目列表里,如果沒(méi)有給出異常提示;
  • 設(shè)置當(dāng)前運(yùn)行項(xiàng)目的腳手架信息;
  • 終端命令提示;
const path = require('path')
const conf = require('./config/projectConfig'); // $ 當(dāng)前項(xiàng)目
const chalk = require('chalk'); // $ 終端顏色設(shè)置插件
const ProgressBarPlugin = require('progress-bar-webpack-plugin'); // $ 進(jìn)度條插件

const PROJECTNAME = require('./config/project.js').name;
if(!conf) throw(chalk`{black.bold.bgWhite ------項(xiàng)目不存在,請(qǐng)檢查配置 777------}`);
const assetsDir = ''

function getAssetPath (assetsDir, filePath) {
return assetsDir
? path.posix.join(assetsDir, filePath)
: filePath
}

module.exports = {
pages: conf.pages, // $ 當(dāng)前項(xiàng)目頁(yè)面
outputDir: "dist/" + projectName + "/", // $ 設(shè)置輸出目錄名
assetsDir: 'static', // $ 增加static文件夾
lintOnSave: process.env.NODE_ENV !== 'production', // $ 是否在開(kāi)發(fā)環(huán)境下通過(guò) eslint-loader 在每次保存時(shí) lint 代碼
productionSourceMap: false, // $ 是否需要生產(chǎn)環(huán)境的 source map
devServer: conf.devServer, // $ 看項(xiàng)目需求 可配可不配
configureWebpack: {
plugins: [
new ProgressBarPlugin({
width: 50, // 默認(rèn)20,進(jìn)度格子數(shù)量即每個(gè)代表進(jìn)度數(shù),如果是20,那么一格就是5。
// format: 'build [:bar] :percent (:elapsed seconds)',
format: chalk.blue.bold("build") + chalk.yellow('[:bar] ') + chalk.green.bold(':percent') + ' (:elapsed秒)',
// stream: process.stderr, // 默認(rèn)stderr,輸出流
// complete: "~", // 默認(rèn)“=”,完成字符
clear: false, // 默認(rèn)true,完成時(shí)清除欄的選項(xiàng)
// renderThrottle: "", // 默認(rèn)16,更新之間的最短時(shí)間(以毫秒為單位)
callback() { // 進(jìn)度條完成時(shí)調(diào)用的可選函數(shù)
console.log(chalk.red.bold("---->>>>編譯完成<<<<----"))
}
}),
]
},
// $ 對(duì)內(nèi)部的 webpack 配置進(jìn)行更細(xì)粒度的修改
chainWebpack: config => {
// $ 修復(fù)HMR
config.resolve.symlinks(true);
// $ 制定環(huán)境打包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 提取至一個(gè)獨(dú)立的 CSS 文件中 (而不是動(dòng)態(tài)注入到 JavaScript 中的 inline 代碼)
loaderOptions: {
sass: {
implementation: require('sass'),
fiber: require('fibers')
}
}
}
}

配置終端插件的效果圖:

7) 運(yùn)行效果

寫(xiě)到這里我們就建立一個(gè)完整的小vue項(xiàng)目了,我們運(yùn)行看看效果:

npm run dev projectA

如圖:

8) 打包效果

npm run build:projectA

cd dist/projectA

live-server --port=9999

??live-server?? 是一個(gè)具有實(shí)時(shí)加載功能的小型服務(wù)器,在項(xiàng)目中用live-server作為一個(gè)實(shí)時(shí)服務(wù)器查看開(kāi)發(fā)的網(wǎng)頁(yè)或項(xiàng)目效果

7. 自動(dòng)化生成模板項(xiàng)目

1) 流程圖

2) 思路整理

  • 本文涉及到腳手架里邊與命令行交互的知識(shí)點(diǎn),感興趣的可以拷貝文末 demo 去練習(xí)下;
  • 這里主要是針對(duì)新建的模板做拷貝處理,流程節(jié)點(diǎn)中執(zhí)行拷貝命令后輸入的項(xiàng)目名稱提示在本地已存在是否需要?jiǎng)h除或者覆蓋,根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景做處理,這里不做過(guò)多探討;
  • 示例代碼涉及到的模板代碼存放在工程根目錄,也可以放在 src 目錄下,不做強(qiáng)制要求;

3) 執(zhí)行命令

npm run copy

4) 示例代碼

  • fs-extra :添加了未包含在 原生fs模塊 中的文件系統(tǒng)方法,并向fs方法添加了promise支持;
  • fse.pathExists :判斷當(dāng)前要拷貝的項(xiàng)目是否存在;
  • 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: "請(qǐng)輸入要生成的項(xiàng)目名稱",
},
])
.then((answers) => {
createProject(answers.projectName);
});

// $ 拷貝項(xiàng)目模板
const createProject = (projectName) => {
const currentTemp = path.join(`./src/projects/${projectName}`);
// $ 判斷當(dāng)前要拷貝的項(xiàng)目是否存在
fse.pathExists(currentTemp, (err, exists) => {
console.log(err, exists); // $ => null, false
// $ 根據(jù)用戶選擇是否替換本項(xiàng)目或者刪除本項(xiàng)目
if (exists) {
// $ 這里也可以覆蓋原項(xiàng)目或者dong
inquirer
.prompt([
{
type: "input",
name: "projectName",
message: "項(xiàng)目已存在,請(qǐng)重新輸入項(xiàng)目名稱",
},
])
.then((answers) => {
createProject(answers.projectName);
});
// throw chalk`{red.bold.bgWhite >>> ${projectName} <<< 項(xiàng)目已經(jīng)存在}`;
} else {
// $ 拷貝模板文件到指定目錄
fse.copy("./temp", path.join(`./src/projects/${projectName}`), (err) => {
// if (err) return console.error(err)
if (err)
throw chalk`{red.bold.bgWhite ------${projectName}項(xiàng)目拷貝失敗 ${err}------}`;
console.log(chalk.red.bold(`--->>>${projectName}項(xiàng)目拷貝成功`));
});
}
});
};

8 優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 方便統(tǒng)一管理項(xiàng)目;
  • 項(xiàng)目之間共享組件和依賴;
  • 運(yùn)行、打包時(shí)互不干擾;
  • 支持同時(shí)運(yùn)行多個(gè)項(xiàng)目;
  • 對(duì)于公共模塊一次提交可以解決所有子項(xiàng)目的問(wèn)題;

缺點(diǎn):

  • 執(zhí)行拷貝模板命令后生成的項(xiàng)目需要在 config/projectConfig.js 文件中手動(dòng)配置項(xiàng)目信息;
  • 隨著項(xiàng)目的增加路由文件的提交在每次代碼的時(shí)候都需要進(jìn)行 Code Review ,不然的話不熟悉項(xiàng)目的同學(xué)很可能會(huì)在解決沖突的過(guò)程中把沖突的模塊刪除;
  • 隨著程序規(guī)模的不斷增加,代碼量的增加,文檔的增加,整個(gè) repo 會(huì)變得越來(lái)越大;

四、思考

有興趣的童鞋可以考慮以下兩個(gè)問(wèn)題:

  1. 項(xiàng)目中有公共路由我們應(yīng)該如何處理?
  2. 狀態(tài)管理和接口管理在這個(gè)工程下如何處理?

五、總結(jié)

通過(guò)以上的分析,我們應(yīng)該對(duì)同一工程下多項(xiàng)目配置化打包的大概流程有基本的了解,而上邊的方案也只是其中的一種實(shí)現(xiàn)方式。寫(xiě)本文的目的主要是給大家提供一種思路,以后在遇到工程需要定制化的時(shí)候就可以通過(guò)更改腳手架的配置來(lái)實(shí)現(xiàn)。

??Demo?? :[https://github.com/licairen/multi_project_demo](

責(zé)任編輯:張燕妮 來(lái)源: 大轉(zhuǎn)轉(zhuǎn)FE
相關(guān)推薦

2017-07-21 09:56:46

Webpack3 Vue.js腳手架

2021-12-23 10:35:32

SpringCloud腳手架架構(gòu)

2021-01-07 05:34:07

腳手架JDK緩存

2020-05-19 10:13:45

Java開(kāi)發(fā)代碼

2016-09-07 15:35:06

VueReact腳手架

2021-04-13 14:47:53

認(rèn)證授權(quán)Java

2018-05-15 09:10:27

前端vue.jswebpack

2021-05-21 05:22:52

腳手架工具項(xiàng)目

2020-03-20 08:32:41

物聯(lián)網(wǎng)腳手架傳感器

2022-07-11 10:38:06

TienChin項(xiàng)目動(dòng)態(tài)

2018-08-30 16:08:37

Node.js腳手架工具

2018-06-11 14:39:57

前端腳手架工具node.js

2019-12-25 15:20:48

前端腳手架命令

2023-11-21 17:36:04

OpenFeignSentinel

2014-08-15 09:36:06

2021-08-30 06:59:06

StrviewAppStrview.js項(xiàng)目

2024-03-11 13:18:00

RustClap項(xiàng)目

2021-09-01 10:07:43

開(kāi)發(fā)零搭建Groovy

2020-06-29 11:35:02

Spring BootJava腳手架

2022-01-14 14:09:11

腳手架代碼自定義
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)