<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 6155字,需瀏覽 13分鐘

           ·

          2022-01-21 11:42

          點(diǎn)擊上方關(guān)注?前端技術(shù)江湖一起學(xué)習(xí),天天進(jìn)步


          前言

          因業(yè)務(wù)平臺(tái)擴(kuò)大,組件越來(lái)越豐富,想通過(guò)抽離組件(包含功能組件,業(yè)務(wù)組件,基礎(chǔ)組件)的方式分享給公司更多的小伙伴使用,所以有了這樣的一次分享。????

          接下來(lái),我將對(duì)自己的這次所學(xué)所得做一次總結(jié)。

          耐心看完,你將收獲到:

          • 通過(guò)什么方式進(jìn)行項(xiàng)目管理
          • 如何搭建組件庫(kù)的demo環(huán)境
          • 如何搭建組件庫(kù)的開(kāi)發(fā)環(huán)境
          • 如何搭建組件庫(kù)編譯打包生成資源的存放位置
          • 自動(dòng)化將組件注冊(cè)為全局組件
          • 如何實(shí)現(xiàn)按需引入
          • 約束規(guī)范
          • 如何將一個(gè)組件庫(kù)發(fā)布到npm上

          通過(guò)什么方式進(jìn)行項(xiàng)目管理

          背景

          本著調(diào)研的目的去思考如何在開(kāi)發(fā)一個(gè)組件庫(kù)時(shí),是將這些package放在一個(gè)倉(cāng)庫(kù)里維護(hù)還是放在多個(gè)倉(cāng)庫(kù)里單獨(dú)維護(hù)?或者說(shuō)一個(gè)倉(cāng)庫(kù)想基于多個(gè)不同框架開(kāi)發(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í)例來(lái)看看兩者的區(qū)別:

          image.png

          解決

          為了降低多package項(xiàng)目的維護(hù)成本,我使用lerna作為流程管理工具。

          • 初始化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的管理配置文件,在搭建過(guò)程中我是采用的是lerna的固定模式。那什么是固定模式呢?

          固定模式的Lerna項(xiàng)目是在一條單一的版本線上操作的,這個(gè)版本是在項(xiàng)目根目錄的?lerna.json文件中的?version字段中控制的。當(dāng)執(zhí)行?lerna publish時(shí),如果一個(gè)模塊自上次發(fā)布以來(lái)有更新,它將會(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)目

          地基是打好了,但是作為打工人還要考慮項(xiàng)目如何構(gòu)建。在構(gòu)建項(xiàng)目這個(gè)過(guò)程中,我通過(guò)使用webpack實(shí)現(xiàn)項(xiàng)目的構(gòu)建。

          組件庫(kù)demo環(huán)境,組件庫(kù)開(kāi)發(fā)環(huán)境以及組件庫(kù)編譯打包生成資源的存放位置,都是基于webpack的配置來(lái)實(shí)現(xiàn)。具體的配置目錄是通過(guò)config文件。

          項(xiàng)目中的example文件就是demo環(huán)境。它是給用戶查看組件具體用法的例子。

          創(chuàng)建dev.config.js- 新增開(kāi)發(fā)環(huán)境的配置:

          image.png

          創(chuàng)建pro.config.js- 新增生產(chǎn)環(huán)境的配置:

          image.png

          這2個(gè)文件分別都是對(duì)應(yīng)開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境的配置,組件可以在package文件下開(kāi)發(fā),example文件進(jìn)行使用。

          創(chuàng)建lib.config.js- 新增組件庫(kù)編譯打包生成資源的存放位置配置:

          image.png

          生成的lib文件主要是作為組件庫(kù)發(fā)布到npm的一個(gè)入口文件。所以針對(duì)的入口是packages下的組件。

          基于上面的配置之后,我們來(lái)實(shí)現(xiàn)package.json命令配置:

          image.png

          將文件區(qū)分開(kāi)來(lái),知道每個(gè)文件是用來(lái)干嘛的,分工明確是第一步其實(shí)是重要的。

          我還創(chuàng)建了一個(gè)base.config.js是公共的配置文件。這個(gè)文件實(shí)現(xiàn)了一些基礎(chǔ)配置:

          • js模塊將es6轉(zhuǎn)成es5
          • 將css文件和js文件分離打包
          • css的轉(zhuǎn)換
          • 配置別名
          • 圖片處理之類

          自動(dòng)化將組件注冊(cè)為全局組件

          為了避免使用組件的時(shí)候頻繁的import...導(dǎo)入操作,我們可以將其注冊(cè)為全局組件。需要用到Vue.component()Vue.use()兩個(gè)方法。

          先來(lái)看看一個(gè)demo組件的目錄結(jié)構(gòu):

          demo
          ├——?index.vue??#?組件實(shí)現(xiàn)
          │?
          └──?index.js?#?每個(gè)組件install方法,供全局引入
          index.js?提供install方法,供Vue.use()使用?

          復(fù)制代碼

          將組件注冊(cè)為全局組件,具體的代碼實(shí)現(xiàn)如下:

          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è)成全局組件。腳本中通過(guò)模塊json-templater可以在js和json對(duì)象上進(jìn)行胡子樣式模板替換。執(zhí)行命令:npm run build:cp,生成的demo文件下的index.js代碼:

          image.png

          將組件注冊(cè)為全局組件之后,需要在index.js引入。也是通過(guò)腳本自動(dòng)化引入,具體的實(shí)現(xiàn)可參考如上代碼。通過(guò)執(zhí)行命令:npm run build:pck,生成的代碼:

          image.png

          如何實(shí)現(xiàn)按需引入

          開(kāi)發(fā)完組件庫(kù)之后,在其他所需該組件的項(xiàng)目中,我們班可以通過(guò)import XXX from 'ddmc-ui'。這樣的方式叫做全局引入,將組件庫(kù)都引入到所需的項(xiàng)目中。

          那如何實(shí)現(xiàn)按需引入呢?例如import {XXX} from 'ddmc-ui/lib/demo'

          注意:這里的按需引入是指你所開(kāi)發(fā)的項(xiàng)目中需要按需引入這個(gè)組件庫(kù)

          要想實(shí)現(xiàn)如上的方式引入組件,我們需要把組件一個(gè)一個(gè)單獨(dú)打包的,所以要獲取每個(gè)組件的路徑。通過(guò)webpack配置,將輸出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ù)制代碼

          好了,到這里組件庫(kù)的按需引入是完成了。但是有個(gè)缺點(diǎn),就是每次使用按需引入就要寫(xiě)一大串import {XXX} from 'ddmc-ui/lib/demo'這樣的引入方式。

          這個(gè)時(shí)候就需要用到babel-plugin-import這個(gè)插件了。

          注意:babel-plugin-import是指你所開(kāi)發(fā)的項(xiàng)目中用到并修改其配置

          具體的配置是:

          //?.babelrc
          {
          ????...
          ????"plugins":[
          ????????"import",?{
          ????????????"libraryName":?"ddmc-ui",
          ????????}
          ????]
          }

          復(fù)制代碼

          約束規(guī)范

          組件庫(kù)的管理和構(gòu)建功能都有了,但是在多人共建的一個(gè)項(xiàng)目中,必須得有一個(gè)規(guī)范去進(jìn)行約束,而規(guī)范除了書(shū)面上的闡述外,還需要一些自動(dòng)化的、強(qiáng)制性的約束。所以需要在代碼提交前對(duì)每個(gè)打工仔的代碼進(jìn)行一遍驗(yàn)證,防止亂提交。

          husky?給GIT提交添加鉤子執(zhí)行一些我們需要做的驗(yàn)證,并執(zhí)行一些腳本去驗(yàn)證代碼是否有問(wè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é)作和快速定位問(wèn)題。

          一般GIT比較常見(jiàn)的提交格式規(guī)范是::

          實(shí)現(xiàn)約束規(guī)范的過(guò)程:

          • 安裝
          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ù)制代碼

          注意:這個(gè)組件庫(kù)名稱要在 npm 上是獨(dú)一無(wú)二,不能重復(fù),否則發(fā)布時(shí)會(huì)提示你無(wú)權(quán)限修改此庫(kù)

          • 將組件庫(kù)設(shè)置為公開(kāi)
          {
          ???"private":?true
          }
          復(fù)制代碼
          • 配置關(guān)鍵詞、描述、作者
          {
          ??"description":?"XXX?組件庫(kù)",
          ??"keywords":?[
          ????"element",
          ????"vue",
          ????"ddmc-ui"
          ??],
          ??"author":?"hhl"
          }

          復(fù)制代碼
          • 配置主入口文件地址
          {
          ??"main":?"lib/index/index.js"
          }
          復(fù)制代碼
          • 設(shè)置忽略文件,減少依賴包大小

          一個(gè)組件庫(kù)只有編譯后的?lib?文件夾、package.json?文件、README.md?文件才是需要被發(fā)布的。其他都不需要的,需要在根目錄下創(chuàng)建一個(gè)??.npmignore?文件中把沒(méi)必要發(fā)布的資源忽略掉,減少依賴包大小。

          思考

          在搭建的過(guò)程,主要是工具包之間本身的版本兼容問(wèn)題,還有就是和node版本相關(guān)的問(wèn)題。

          過(guò)程中使用vue-loader的高版本出現(xiàn)了Error: Cannot find module 'webpack/lib/RuleSet'。查閱vue-loader源碼最后解決方案是降低版本。

          雖然整體上的倉(cāng)庫(kù)以及組件庫(kù)已完成,但是隨著倉(cāng)庫(kù)的擴(kuò)大還是需要迭代優(yōu)化的。也可以從webpack配置進(jìn)行優(yōu)化。

          • 刪除無(wú)用的css樣式
          • 將打包后的圖片進(jìn)行壓縮
          • CDN加載文件
          • 過(guò)濾第三方包。比如vue,vuex之類的。使用webpack的externals選項(xiàng)。externals來(lái)防止這些依賴包被打包
          • tree-shaking :沒(méi)有用的代碼剔除掉等。后續(xù)會(huì)慢慢迭代項(xiàng)目?jī)?yōu)化這塊
          • happypack 多線程打包,可以將不同的邏輯交給不同的線程來(lái)處理
          • DllReferencePlugin 和 DllPlugin提前打包,大幅度提升構(gòu)建速度
          • 動(dòng)態(tài)加載 通過(guò)import引入文件:添加webpackChunkName字段,webpack配置output選項(xiàng)

          因時(shí)間的關(guān)系比較趕,后續(xù)將會(huì)慢慢迭代上去。

          總結(jié)

          以上是我從零開(kāi)發(fā)一款組件庫(kù)遇到的問(wèn)題踩過(guò)的坑總結(jié)下來(lái)并實(shí)際使用的工具和方法,希望能給大家?guī)?lái)幫助。

          除了上面提到的這些比較常用的工具和方法外,對(duì)于前端工程化/自動(dòng)化還有很多探索和學(xué)習(xí)。

          覺(jué)得不錯(cuò)給文章點(diǎn)個(gè)贊,是對(duì)我最大的支持和鼓勵(lì)????

          關(guān)于本文

          來(lái)源:樹(shù)竹

          https://juejin.cn/post/7020032793774129189



          The End

          歡迎自薦投稿到《前端技術(shù)江湖》,如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),記得點(diǎn)個(gè)?「在看」


          點(diǎn)個(gè)『在看』支持下?

          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费看一区二区三区四区 | 一区二区三区另类一级 | 啪啪啪免费视频 | 操色婷婷 | 天天操天天干天天日 |