記一次搭建業(yè)務(wù)平臺(tái)組件庫(kù)的過程

來源:樹竹
前言
因業(yè)務(wù)平臺(tái)擴(kuò)大,組件越來越豐富,想通過抽離組件(包含功能組件,業(yè)務(wù)組件,基礎(chǔ)組件)的方式分享給公司更多的小伙伴使用,所以有了這樣的一次分享。????
接下來,我將對(duì)自己的這次所學(xué)所得做一次總結(jié)。
耐心看完,你將收獲到:
通過什么方式進(jìn)行項(xiàng)目管理
如何搭建組件庫(kù)的demo環(huán)境
如何搭建組件庫(kù)的開發(fā)環(huán)境
如何搭建組件庫(kù)編譯打包生成資源的存放位置
自動(dòng)化將組件注冊(cè)為全局組件
如何實(shí)現(xiàn)按需引入
約束規(guī)范
如何將一個(gè)組件庫(kù)發(fā)布到npm上
通過什么方式進(jìn)行項(xiàng)目管理
背景
本著調(diào)研的目的去思考如何在開發(fā)一個(gè)組件庫(kù)時(shí),是將這些package放在一個(gè)倉(cāng)庫(kù)里維護(hù)還是放在多個(gè)倉(cāng)庫(kù)里單獨(dú)維護(hù)?或者說一個(gè)倉(cāng)庫(kù)想基于多個(gè)不同框架開發(fā)的組件庫(kù),那么如何去管理呢?
那是將每個(gè)組件當(dāng)成一個(gè)package進(jìn)行管理(即多項(xiàng)目多倉(cāng)庫(kù)的形式,也稱為Multirepo),還是用一個(gè)倉(cāng)庫(kù)去管理多個(gè)package(即Monorepo)
可能上述的表達(dá)有點(diǎn)抽象,用實(shí)例來看看兩者的區(qū)別:

解決
初始化Lerna項(xiàng)目:
//?進(jìn)入項(xiàng)目文件夾
cd?DDMC-UI-?
//?初始化?
lerna?init
復(fù)制代碼
初始化之后 ,生成的項(xiàng)目結(jié)構(gòu)如下:
DDMC-UI?/
??packages?/
??package.json
??lerna.json
復(fù)制代碼
lerna.json是為L(zhǎng)erna的管理配置文件,在搭建過程中我是采用的是lerna的固定模式。那什么是固定模式呢?固定模式的Lerna項(xiàng)目是在一條單一的版本線上操作的,這個(gè)版本是在項(xiàng)目根目錄的?
lerna.json文件中的?version字段中控制的。當(dāng)執(zhí)行?lerna publish時(shí),如果一個(gè)模塊自上次發(fā)布以來有更新,它將會(huì)更新為你要發(fā)布的新的版本,這意味著你只需在需要時(shí)發(fā)布新版本的package。
補(bǔ)全項(xiàng)目的目錄,最終生成項(xiàng)目倉(cāng)庫(kù)如下:
DDMC-UI?/
??config?/??
??example?/?
??????app.js
??????index.html
??packages?/
??????demo1?/
??????????index.vue
??????????index.js
??????demo2?/
??????????index.vue
??????????index.js
??????index.js
??src?/
??????utils?/
??????common?/
??package.json
??lerna.json
復(fù)制代碼
webpack 構(gòu)建項(xiàng)目
webpack實(shí)現(xiàn)項(xiàng)目的構(gòu)建。config文件。example文件就是demo環(huán)境。它是給用戶查看組件具體用法的例子。dev.config.js- 新增開發(fā)環(huán)境的配置:
pro.config.js- 新增生產(chǎn)環(huán)境的配置:
package文件下開發(fā),example文件進(jìn)行使用。lib.config.js- 新增組件庫(kù)編譯打包生成資源的存放位置配置:
lib文件主要是作為組件庫(kù)發(fā)布到npm的一個(gè)入口文件。所以針對(duì)的入口是packages下的組件。package.json命令配置:
base.config.js是公共的配置文件。這個(gè)文件實(shí)現(xiàn)了一些基礎(chǔ)配置:js模塊將es6轉(zhuǎn)成es5 將css文件和js文件分離打包 css的轉(zhuǎn)換 配置別名 圖片處理之類 微信搜索readdot,關(guān)注后回復(fù)視頻教程獲取23種精品資料
自動(dòng)化將組件注冊(cè)為全局組件
Vue.component()和Vue.use()兩個(gè)方法。demo
├——?index.vue??#?組件實(shí)現(xiàn)
│?
└──?index.js?#?每個(gè)組件install方法,供全局引入
index.js?提供install方法,供Vue.use()使用?
復(fù)制代碼
const?fs=require('fs');
const?path=require('path');
var?endOfLine?=?require('os').EOL;
const?render?=?require('json-templater/string');
const?IMPORT_TEMPLATE='import?{{name}}?from?\'./index.vue\';';
const?USE_TEMPLATE='Vue.component({{name}},?{{component}});';
const?MAIN_TEMPLATE=`
{{include}}
{{component}}.install?=?function?(Vue)?{
????{{list}}
};
export?default?{{component}};
`
const?files=fs.readdirSync('./packages');
const?folder?=files.slice(0,-1)
console.log(folder)
const?includeComponentTemplate?=?[];
const?listComponentTemplate?=[];
const?writeFile=function(file,include,list){
????const?p=path.resolve(__dirname,`../packages/${file}/index.js`);
????fs.writeFileSync(p,render(MAIN_TEMPLATE,{
????????include:include,
????????list:?list,
????????component:file
????}));
}
folder.forEach((item)=>{
????const?include=render(IMPORT_TEMPLATE,{
????????name:item,
????????component:item,
????});
????const?list=render(USE_TEMPLATE,{
????????name:item,
????????component:item,
????});
????writeFile(item,include,list);
});
復(fù)制代碼
install方法,并使用Vue.component()注冊(cè)成全局組件。腳本中通過模塊json-templater可以在js和json對(duì)象上進(jìn)行胡子樣式模板替換。執(zhí)行命令:npm run build:cp,生成的demo文件下的index.js代碼:
index.js引入。也是通過腳本自動(dòng)化引入,具體的實(shí)現(xiàn)可參考如上代碼。通過執(zhí)行命令:npm run build:pck,生成的代碼:
如何實(shí)現(xiàn)按需引入
import XXX from 'ddmc-ui'。這樣的方式叫做全局引入,將組件庫(kù)都引入到所需的項(xiàng)目中。import {XXX} from 'ddmc-ui/lib/demo'lib目錄要和packages目錄結(jié)構(gòu)一致。在lib.config.js配置需要更改。packages文件的目錄是關(guān)鍵點(diǎn),如下代碼可實(shí)現(xiàn)獲取入口對(duì)象:function?getEntries(path)?{
??let?files?=??fs.readdirSync(resolve(path));
??const?entries?=?files.reduce(?(ret,?item)?=>?{
????????const?itemPath?=`${path}/${item}`;
????????const?isDir?=??fs.statSync(resolve(itemPath)).isDirectory();
????????console.log(isDir,itemPath)
????????if?(isDir)?{
????????????ret[item]?=?resolve(join(itemPath,?'index.js'));
????????}?else?{
????????????const?[name]?=?item.split('.');
????????????ret[name]?=?resolve(itemPath);
????????}
????????return?ret;
??},?{})
??return?entries;
}
復(fù)制代碼
import {XXX} from 'ddmc-ui/lib/demo'這樣的引入方式。babel-plugin-import這個(gè)插件了。babel-plugin-import是指你所開發(fā)的項(xiàng)目中用到并修改其配置//?.babelrc
{
????...
????"plugins":[
????????"import",?{
????????????"libraryName":?"ddmc-ui",
????????}
????]
}
復(fù)制代碼
約束規(guī)范
husky?給GIT提交添加鉤子執(zhí)行一些我們需要做的驗(yàn)證,并執(zhí)行一些腳本去驗(yàn)證代碼是否有問題是否規(guī)范。lint-staged?是一個(gè)在git暫存文件上運(yùn)行l(wèi)inters的工具。可以搭配husky進(jìn)行git commit前的代碼校驗(yàn)。commitlint?提交信息校驗(yàn),基于以上的兩個(gè)工具包,我們有能力在Git的鉤子里做一些事情。首先不得不提的是代碼的提交規(guī)范和規(guī)范的校驗(yàn),優(yōu)雅的提交方便團(tuán)隊(duì)協(xié)作和快速定位問題。微信搜索readdot,關(guān)注后回復(fù)視頻教程獲取23種精品資料: 。安裝
npm?install?-D?husky?lint-staged
復(fù)制代碼
初始化
npx?husky?install
npx?husky?add?.husky/pre-commit?"npm?run?test"
npx?husky?add?.husky/commit-msg?'npx?--no-install?commitlint?--edit?"$1"'
復(fù)制代碼
package.json配置
"lint-staged":?{?"src/*?*/*?.{js,json,vue,ts,tsx}":?[?"npm?run?lint"?]?}
復(fù)制代碼
新建 commitlint.js
module.exports?=?{?extends:?['@commitlint/config-conventional']?}
復(fù)制代碼
如何將一個(gè)組件庫(kù)發(fā)布到npm上
package.json配置:配置組件庫(kù)名稱
{
??"name":?"foxit-ui"
}
復(fù)制代碼
將組件庫(kù)設(shè)置為公開
{
???"private":?true
}
復(fù)制代碼
配置關(guān)鍵詞、描述、作者
{
??"description":?"XXX?組件庫(kù)",
??"keywords":?[
????"element",
????"vue",
????"ddmc-ui"
??],
??"author":?"hhl"
}
復(fù)制代碼
配置主入口文件地址
{
??"main":?"lib/index/index.js"
}
復(fù)制代碼
設(shè)置忽略文件,減少依賴包大小
lib?文件夾、package.json?文件、README.md?文件才是需要被發(fā)布的。其他都不需要的,需要在根目錄下創(chuàng)建一個(gè)??.npmignore?文件中把沒必要發(fā)布的資源忽略掉,減少依賴包大小。思考
node版本相關(guān)的問題。vue-loader的高版本出現(xiàn)了Error: Cannot find module 'webpack/lib/RuleSet'。查閱vue-loader源碼最后解決方案是降低版本。刪除無(wú)用的css樣式 將打包后的圖片進(jìn)行壓縮 CDN加載文件 過濾第三方包。比如vue,vuex之類的。使用webpack的externals選項(xiàng)。externals來防止這些依賴包被打包 tree-shaking :沒有用的代碼剔除掉等。后續(xù)會(huì)慢慢迭代項(xiàng)目?jī)?yōu)化這塊 happypack 多線程打包,可以將不同的邏輯交給不同的線程來處理 DllReferencePlugin 和 DllPlugin提前打包,大幅度提升構(gòu)建速度 動(dòng)態(tài)加載 通過import引入文件:添加webpackChunkName字段,webpack配置output選項(xiàng)
總結(jié)
以上是我從零開發(fā)一款組件庫(kù)遇到的問題踩過的坑總結(jié)下來并實(shí)際使用的工具和方法,希望能給大家?guī)韼椭?/span>
除了上面提到的這些比較常用的工具和方法外,對(duì)于前端工程化/自動(dòng)化還有很多探索和學(xué)習(xí)。
覺得不錯(cuò)給文章點(diǎn)個(gè)贊,是對(duì)我最大的支持和鼓勵(lì)????
六千字講透 Vue3 響應(yīng)式是如何實(shí)現(xiàn)的
真實(shí)業(yè)務(wù)訂單 拆單 架構(gòu)與實(shí)戰(zhàn)
如何搭建一臺(tái)永久運(yùn)行的個(gè)人服務(wù)器?
一鍵部署!搭建一個(gè)文檔網(wǎng)站 超簡(jiǎn)單!

