從 ElementUI 源碼的構建流程來看前端 UI 庫設計

image

引言

因爲業務須要,近期團隊要搞一套本身的UI組件庫,框架方面仍是Vue。而業界已經有比較成熟的一些UI庫了,好比ElementUIAntDesignVant等。css

結合框架Vue,咱們選擇在ElementUI基礎上進行改造。但造輪子絕非易事,首先須要先去了解它整個但構建流程、目錄設計等。html

本文經過分析ElementUI完整的構建流程,最後給出搭建一個完備的組件庫須要作的一些工做,但願對於想了解ElementUI源碼或者也有搭建UI組件庫需求的你,能夠提供一些幫助!前端

咱們先來看下ElementUI的源碼的目錄結構。vue

目錄結構解析

  • github:存放了Element UI貢獻指南、issuePR模板
  • build:存放了打包相關的配置文件
  • examples:組件相關示例 demo
  • packages:組件源碼
  • src:存放入口文件和一些工具輔助函數
  • test:單元測試相關文件,這也是一個優秀的開源項目必備的
  • types:類型聲明文件

說完文件目錄,剩下還有幾個文件(常見的.babelrc.eslintc這裏就不展開說明了),在業務代碼中是不常見的:
imagenode

  • .travis.yml:持續集成(CI)的配置文件
  • CHANGELOG:更新日誌,這裏Element UI提供了四種不一樣語言的,也是很貼心了
  • components.json:標明瞭組件的文件路徑,方便 webpack 打包時獲取組件的文件路徑。
  • FAQ.md:ElementUI 開發者對常見問題的解答。
  • LICENSE:開源許可證,Element UI使用的是MIT協議
  • Makefile:Makefile 是一個適用於 C/C++ 的工具,在擁有 make 環境的目錄下, 若是存在一個 Makefile 文件。 那麼輸入 make 命令將會執行 Makefile 文件中的某個目標命令。

深刻了解構建流程前,咱們先來看下ElementUI 源碼的幾個比較主要的文件目錄,這對於後面研究ElementUI的完整流程是有幫助的。webpack

package.json

一般咱們去看一個大型項目都是從package.json文件開始看起的,這裏麪包含了項目的版本、入口、腳本、依賴等關鍵信息。git

我這裏拿出了幾個關鍵字段,一一的去分析、解釋他的含義。github

main

項目的入口文件web

import Element from 'element-ui' 時候引入的就是 main中的文件

lib/element-ui.common.jscommonjs規範,而lib/index.jsumd規範,這個我在後面的打包模塊會詳細說明。shell

files

指定npm publish發包時須要包含的文件/目錄。

typings

TypeScript入口文件。

home

項目的線上地址

unpkg

當你把一個包發佈到npm上時,它同時應該也能夠在unpkg上獲取到。也就是說,你的代碼既可能在NodeJs環境也可能在瀏覽器環境執行。爲此你須要用umd格式打包,lib/index.jsumd規範,由webpack.conf.js生成。

style

聲明樣式入口文件,這裏是lib/theme-chalk/index.css,後面也會詳細說明。

scripts

開發、測試、生產構建,打包、部署,測試用例等相關腳本。scripts算是package.json中最重要的部分了,下面我會一一對其中的重要指令進行說明。
image

bootstrap

"bootstrap": "yarn || npm i"

安裝依賴, 官方推薦優先選用yarn(吐槽一句:我剛開始沒看明白,想着bootstrap不是以前用過的那個 ui 庫嗎 🤔,後來看了下,原來bootstrap翻譯過來是引導程序的意思,這樣看看也就大概理解了 🤣)

build:file

該指令主要用來自動化生成一些文件。

"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js"

這條指令較長,咱們拆開來看:

build/bin/iconInit.js

解析icon.scss,把全部的icon的名字放在icon.json裏面 最後掛在Vue原型上的$icon上。

最後經過遍歷icon.json,獲得了官網的這種效果:
image

build/bin/build-entry.js

根據components.json文件,生成src/index.js文件,核心就是json-templater/string插件的使用。

咱們先來看下src/index.js文件,他對應的是項目的入口文件,最上面有這樣一句:

/* Automatically generated by './build/bin/build-entry.js' */

也就是src/index.js文件是由build/bin/build-entry.js腳本自動構建的。咱們來看下源碼:

// 根據components.json生成src/index.js文件

// 引入全部組件的依賴關係
var Components = require("../../components.json");
var fs = require("fs");
// https://www.npmjs.com/package/json-templater 可讓string與變量結合 輸出一些內容
var render = require("json-templater/string");
// https://github.com/SamVerschueren/uppercamelcase  轉化爲駝峯 foo-bar >> FooBar
var uppercamelcase = require("uppercamelcase");
var path = require("path");
// os.EOL屬性是一個常量,返回當前操做系統的換行符(Windows系統是\r\n,其餘系統是\n)
var endOfLine = require("os").EOL;

// 生成文件的名字和路徑
var OUTPUT_PATH = path.join(__dirname, "../../src/index.js");
var IMPORT_TEMPLATE =
  "import {{name}} from '../packages/{{package}}/index.js';";
var INSTALL_COMPONENT_TEMPLATE = "  {{name}}";
// var MAIN_TEMPLATE = `/* Automatically generated by './build/bin/build-entry.js' */

// ...

// 獲取全部組件的名字,存放在數組中
var ComponentNames = Object.keys(Components);

var includeComponentTemplate = [];
var installTemplate = [];
var listTemplate = [];

ComponentNames.forEach((name) => {
  var componentName = uppercamelcase(name);

  includeComponentTemplate.push(
    render(IMPORT_TEMPLATE, {
      name: componentName,
      package: name,
    })
  );

  if (
    [
      "Loading",
      "MessageBox",
      "Notification",
      "Message",
      "InfiniteScroll",
    ].indexOf(componentName) === -1
  ) {
    installTemplate.push(
      render(INSTALL_COMPONENT_TEMPLATE, {
        name: componentName,
        component: name,
      })
    );
  }

  if (componentName !== "Loading") listTemplate.push(`  ${componentName}`);
});

var template = render(MAIN_TEMPLATE, {
  include: includeComponentTemplate.join(endOfLine),
  install: installTemplate.join("," + endOfLine),
  version: process.env.VERSION || require("../../package.json").version,
  list: listTemplate.join("," + endOfLine),
});

// 結果輸出到src/index.js中
fs.writeFileSync(OUTPUT_PATH, template);
console.log("[build entry] DONE:", OUTPUT_PATH);

其實就是上面說的,根據components.json,生成src/index.js文件。

build/bin/i18n.js

根據 examples/i18n/page.json 和模版,生成不一樣語言的 demo,也就是官網 demo 展現國際化的處理。

ElementUI官網的國際化依據的模版是examples/pages/template,根據不一樣的語言,分別生成不一樣的文件:
image
這裏面都是.tpl文件,每一個文件對應一個模版,並且每一個tpl文件又都是符合SFC規範的Vue文件。

咱們隨便打開一個文件:

export default {
  data() {
    return {
      lang: this.$route.meta.lang,
      navsData: [
        {
          path: "/design",
          name: "<%= 1 >",
        },
        {
          path: "/nav",
          name: "<%= 2 >",
        },
      ],
    };
  },
};

裏面都有數字標示了須要國際化處理的地方。

首頁全部國際化相關的字段對應關係存儲在examples/i18n/page.json中:
image

最終官網展現出來的就是通過上面國際化處理後的頁面:
image
支持切換不一樣語言。

繞了一圈,回到主題:build/bin/i18n.js幫咱們作了什麼呢?

咱們思考一個問題:首頁的展現是如何作到根據不一樣語言,生成不一樣的vue文件呢?

這就是build/bin/i18n.js幫咱們作的事情。

來看下對應的源碼:

"use strict";

var fs = require("fs");
var path = require("path");
var langConfig = require("../../examples/i18n/page.json");

langConfig.forEach((lang) => {
  try {
    fs.statSync(path.resolve(__dirname, `../../examples/pages/${lang.lang}`));
  } catch (e) {
    fs.mkdirSync(path.resolve(__dirname, `../../examples/pages/${lang.lang}`));
  }

  Object.keys(lang.pages).forEach((page) => {
    var templatePath = path.resolve(
      __dirname,
      `../../examples/pages/template/${page}.tpl`
    );
    var outputPath = path.resolve(
      __dirname,
      `../../examples/pages/${lang.lang}/${page}.vue`
    );
    var content = fs.readFileSync(templatePath, "utf8");
    var pairs = lang.pages[page];

    Object.keys(pairs).forEach((key) => {
      content = content.replace(
        new RegExp(`<%=\\s*${key}\\s*>`, "g"),
        pairs[key]
      );
    });

    fs.writeFileSync(outputPath, content);
  });
});

處理流程也很簡單:遍歷examples/i18n/page.json,根據不一樣的數據結構把tpl文件的標誌位,經過正則匹配出來,並替換成本身預先設定好的字段。

這樣官網首頁的國際化就完成了。

build/bin/version.js

根據package.json中的version,生成examples/versions.json,對應就是完整的版本列表

build:theme

處理樣式相關。

"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",

一樣這一條也關聯了多個操做,咱們拆開來看。

build/bin/gen-cssfile

這一步是根據components.json,生成package/theme-chalk/index.scss文件,把全部組件的樣式都導入到index.scss

實際上是作了一個自動化導入操做,後面每次新增組件,就不用手動去引入新增組件的樣式了。

gulp build --gulpfile packages/theme-chalk/gulpfile.js

咱們都知道ElementUI在使用時有兩種引入方式:

  • 全局引入
import Vue from "vue";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import App from "./App.vue";

Vue.use(ElementUI);

new Vue({
  el: "#app",
  render: (h) => h(App),
});
  • 按需引入
import Vue from "vue";
import { Pagination, Dropdown } from "element-ui";

import App from "./App.vue";

Vue.use(Pagination);
Vue.use(Dropdown);

new Vue({
  el: "#app",
  render: (h) => h(App),
});

對應兩種引入方式,Element在打包時對應的也有兩種方案。

具體以下:將packages/theme-chalk下的全部scss文件編譯爲css,當你須要全局引入時,就去引入index.scss文件;當你按需引入時,引入對應的組件scss文件便可。

這其中有一點,咱們須要思考下:如何把packages/theme-chalk下的全部scss文件編譯爲css

在平時的開發中,咱們打包、壓縮之類的工做每每都會交給webpack去處理,可是,針對上面這個問題,咱們若是採用gulp基於工做流去處理會更加方便。

gulp相關的處理就在packages/theme-chalk/gulpfile.js中:

"use strict";

const { series, src, dest } = require("gulp");
const sass = require("gulp-sass"); // 編譯gulp工具
const autoprefixer = require("gulp-autoprefixer"); // 添加廠商前綴
const cssmin = require("gulp-cssmin"); // 壓縮css

function compile() {
  return src("./src/*.scss") // src下的全部scss文件
    .pipe(sass.sync()) // 把scss文件編譯成css
    .pipe(
      autoprefixer({
        // 基於目標瀏覽器版本,添加廠商前綴
        browsers: ["ie > 9", "last 2 versions"],
        cascade: false,
      })
    )
    .pipe(cssmin()) // 壓縮css
    .pipe(dest("./lib")); // 輸出到lib下
}

function copyfont() {
  return src("./src/fonts/**") // 讀取src/fonts下的全部文件
    .pipe(cssmin())
    .pipe(dest("./lib/fonts")); // 輸出到lib/fonts下
}

exports.build = series(compile, copyfont);

通過處理,最終就會打包出對應的樣式文件

cp-cli packages/theme-chalk/lib lib/theme-chalk
cp-cli 是一個跨平臺的 copy工具,和 CopyWebpackPlugin相似

這裏就是複製文件到lib/theme-chalk下。

上面提到過屢次components.json,下面就來了解下。

components.json

這個文件其實就是記錄了組件的路徑,在自動化生成文件以及入口時會用到:

{
  "pagination": "./packages/pagination/index.js",
  "dialog": "./packages/dialog/index.js",
  "autocomplete": "./packages/autocomplete/index.js",
  // ...
  "avatar": "./packages/avatar/index.js",
  "drawer": "./packages/drawer/index.js",
  "popconfirm": "./packages/popconfirm/index.js"
}

packages

存放着組件庫的源碼和組件樣式文件。

這裏以Alert組件爲例作下說明:

Alert 文件夾

image
這裏main.vue對應就是組件源碼,而index.js就是入口文件:

import Alert from "./src/main";

/* istanbul ignore next */
Alert.install = function (Vue) {
  Vue.component(Alert.name, Alert);
};

export default Alert;

引入組件,而後爲組件提供install方法,讓Vue能夠經過Vue.use(Alert)去使用。

關於 install能夠看 官方文檔

packages/theme-chalk

這裏面存放的就是全部組件相關的樣式,上面也已經作過說明了,裏面有index.scss(用於全局引入時導出全部組件樣式)和其餘每一個組件對應的scss文件(用於按需引入時導出對應的組件樣式)

src

說了半天,終於繞到了src文件夾。

上面的packages文件夾是分開去處理每一個組件,而src的做用就是把全部的組件作一個統一處理,同時包含自定義指令、項目總體入口、組件國際化、組件 mixins、動畫的封裝和公共方法。
image
咱們主要來看下入口文件,也就是src/index.js

/* Automatically generated by './build/bin/build-entry.js' */
// 導入了packages下的全部組件
import Pagination from "../packages/pagination/index.js";
import Dialog from "../packages/dialog/index.js";
import Autocomplete from "../packages/autocomplete/index.js";
// ...

const components = [
  Pagination,
  Dialog,
  Autocomplete,
  // ...
];

// 提供了install方法,幫咱們掛載了一些組件與變量
const install = function (Vue, opts = {}) {
  locale.use(opts.locale);
  locale.i18n(opts.i18n);
  // 把全部的組件註冊到Vue上面
  components.forEach((component) => {
    Vue.component(component.name, component);
  });

  Vue.use(InfiniteScroll);
  Vue.use(Loading.directive);

  Vue.prototype.$ELEMENT = {
    size: opts.size || "",
    zIndex: opts.zIndex || 2000,
  };

  Vue.prototype.$loading = Loading.service;
  Vue.prototype.$msgbox = MessageBox;
  Vue.prototype.$alert = MessageBox.alert;
  Vue.prototype.$confirm = MessageBox.confirm;
  Vue.prototype.$prompt = MessageBox.prompt;
  Vue.prototype.$notify = Notification;
  Vue.prototype.$message = Message;
};

/* istanbul ignore if */
if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}
// 導出版本號、install方法(插件)、以及一些功能好比國際化功能
export default {
  version: "2.13.2",
  locale: locale.use,
  i18n: locale.i18n,
  install,
  Pagination,
  Dialog,
  Autocomplete,
  // ...
};

文件開頭的:

/* Automatically generated by './build/bin/build-entry.js' */

其實在上面的scriptsbuild/bin/build-entry.js中咱們已經提到過:src/index.js是由build-entry腳本自動生成的。

這個文件主要作下如下事情:

  • 導入了 packages 下的全部組件
  • 對外暴露了install方法,把全部的組件註冊到Vue上面,並在Vue原型上掛載了一些全局變量和方法
  • 最終將install方法、變量、方法導出

examples

存放了 ElementUI的組件示例。
image
其實從目錄結構,咱們不難看出這是一個完整獨立的Vue項目。主要用於官方文檔的展現:
image
這裏咱們主要關注下docs文件夾:
image
Element官網支持 4 種語言,docs一共有 4 個文件夾,每一個文件夾裏面的內容基本是同樣的。

咱們能夠看到裏面所有都是md文檔,而每個md文檔,分別對應着官網組件的展現頁面。

其實如今各大主流組件庫文檔都是用採用 md編寫。

咱們上面大體瞭解了源碼的幾個主要文件目錄,可是都比較分散。下面咱們從構建指令到新建組件、打包流程、發佈組件完整的看一下構建流程。

構建流程梳理

構建指令(Makefile)

平時咱們都習慣將項目經常使用的腳本放在package.json中的scripts中。但ElementUI還使用了Makefile文件(因爲文件內容較多,這裏就選取了幾個作下說明):

.PHONY: dist test
default: help

# build all theme
build-theme:
    npm run build:theme

install:
    npm install

install-cn:
    npm install --registry=http://registry.npm.taobao.org

dev:
    npm run dev

play:
    npm run dev:play

new:
    node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))

dist: install
    npm run dist

deploy:
    @npm run deploy

pub:
    npm run pub

test:
    npm run test:watch

// Tip:
// make new <component-name> [中文]
// 一、將新建組件添加到components.json
// 二、添加到index.scss
// 三、添加到element-ui.d.ts
// 四、建立package
// 五、添加到nav.config.json

我是第一次見,因此就去Google下,網上對Makefile對定義大概是這樣:

Makefile 是一個適用於 C/C++ 的工具,較早做爲工程化工具出如今 UNIX 系統中, 經過 make 命令來執行一系列的編譯和鏈接操做。在擁有 make 環境的目錄下, 若是存在一個 Makefile 文件。 那麼輸入 make 命令將會執行 Makefile 文件中的某個目標命令。

這裏我以make install爲例簡要說明下執行流程:

  • 執行 make 命令, 在該目錄下找到 Makefile 文件。
  • 找到 Makefile 文件中對應命令行參數的 install 目標。這裏的目標就是 npm install

構建入口文件

咱們看下scripts中的dev指令:

"dev":
"npm run bootstrap &&
npm run build:file &&
cross-env NODE_ENV=development
webpack-dev-server --config build/webpack.demo.js &
node build/bin/template.js",

首先npm run bootstrap是用來安裝依賴的。

npm run build:file在前面也有提到,主要用來自動化生成一些文件。主要是node build/bin/build-entry.js,用於生成Element的入口js:先是讀取根目錄的components.json,這個json文件維護着Element全部的組件路徑映射關係,鍵爲組件名,值爲組件源碼的入口文件;而後遍歷鍵值,將全部組件進行import,對外暴露install方法,把全部import的組件經過Vue.component(name, component)方式註冊爲全局組件,而且把一些彈窗類的組件掛載到Vue的原型鏈上(這個在上面介紹scripts相關腳本時有詳細說明)。

在生成了入口文件的src/index.js以後就會運行webpack-dev-server

webpack-dev-server --config build/webpack.demo.js

這個前面也提過,用於跑Element官網的基礎配置。

新建組件

上面咱們提到了,Element中還用了makefile爲咱們編寫了一些額外的腳本。

這裏重點說一下 make new <component-name> [中文] 這個命令。

當運行這個命令的時候,其實運行的是 node build/bin/new.js

build/bin/new.js比較簡單,備註也很清晰,它幫咱們作了下面幾件事:

一、新建的組件添加到components.json

二、在packages/theme-chalk/src下新建對應到組件scss文件,並添加到packages/theme-chalk/src/index.scss

三、添加到 element-ui.d.ts,也就是對應的類型聲明文件

四、建立package(咱們上面有提到組件相關的源碼都在package目錄下存放)

五、添加到nav.config.json(也就是官網組件左側的菜單)

打包流程分析

ElementUI打包執行的腳本是:

"dist":
  "npm run clean &&
   npm run build:file &&
   npm run lint &&
   webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js &&
   npm run build:utils &&
   npm run build:umd &&
   npm run build:theme",

下面咱們一一來進行分析:

npm run clean(清理文件)

"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",

刪除以前打包生成文件。

npm run build:file(生成入口文件)

根據components.json生成入口文件src/index.js,以及i18n相關文件。這個在上面已經作過度析,這裏就再也不展開進行說明。

npm run lint(代碼檢查)

"lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",

項目eslint檢測,這也是如今項目必備的。

文件打包相關

webpack --config build/webpack.conf.js &&
webpack --config build/webpack.common.js &&
webpack --config build/webpack.component.js
build/webpack.conf.js

生成umd格式的js文件(index.js)

build/webpack.common.js

生成commonjs格式的js文件(element-ui.common.js),require時默認加載的是這個文件。

build/webpack.component.js

components.json爲入口,將每個組件打包生成一個文件,用於按需加載。

npm run build:utils(轉譯工具方法)

"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",

src目錄下的除了index.js入口文件外的其餘文件經過babel轉譯,而後移動到lib文件夾下。

npm run build:umd(語言包)

"build:umd": "node build/bin/build-locale.js",

生成umd模塊的語言包。

npm run build:theme(生成樣式文件)

"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",

根據components.json,生成package/theme-chalk/index.scss。用gulp構建工具,編譯scss、壓縮、輸出csslib目錄。

最後用一張圖來描述上述整個打包流程:
image

發佈流程

打包完成,緊跟着就是代碼的發佈了。Element中發佈主要是用shell腳本實現的。

Element發佈一共涉及三個部分:

一、git 發佈

二、npm 發佈

三、官網發佈

發佈對應的腳本是:

"pub":
  "npm run bootstrap &&
   sh build/git-release.sh &&
   sh build/release.sh &&
   node build/bin/gen-indices.js &&
   sh build/deploy-faas.sh",

sh build/git-release.sh(代碼衝突檢測)

運行 git-release.sh 進行git衝突的檢測,這裏主要是檢測dev分支是否衝突,由於Element是在dev分支進行開發的。

#!/usr/bin/env sh
# 切換至dev分支
git checkout dev
# 檢測本地和暫存區是否還有未提交的文件
if test -n "$(git status --porcelain)"; then
  echo 'Unclean working tree. Commit or stash changes first.' >&2;
  exit 128;
fi
# 檢測本地分支是否有誤
if ! git fetch --quiet 2>/dev/null; then
  echo 'There was a problem fetching your branch. Run `git fetch` to see more...' >&2;
  exit 128;
fi
# 檢測本地分支是否落後遠程分支
if test "0" != "$(git rev-list --count --left-only @'{u}'...HEAD)"; then
  echo 'Remote history differ. Please pull changes.' >&2;
  exit 128;
fi
# 經過以上檢查,表示代碼無衝突
echo 'No conflicts.' >&2;

發佈 npm && 官網更新

dev分支代碼檢測沒有衝突,接下來就會執行release.sh腳本,合併dev分支到master、更新版本號、推送代碼到遠程倉庫併發布到npm(npm publish)。

官網更新大體就是:將靜態資源生成到examples/element-ui目錄下,而後放到gh-pages分支,這樣就能經過github pages的方式訪問。

到這裏ElementUI的完整構建流程就分析完了。

ui 組件庫搭建指北

經過對ElementUI源碼文件和構建流程的分析,下面咱們能夠總結一下搭建一個完備的 ui 組件庫都須要作什麼工做。

目錄結構

目錄結構對於大型項目是尤爲重要的,合理清晰的結構對於後期的開發和擴展都是頗有意義的。ui組件庫的目錄結構,我感受ElementUI的就很不錯:

|-- Element
    |-- .babelrc                           // babel相關配置
    |-- .eslintignore
    |-- .eslintrc                          // eslint相關配置
    |-- .gitattributes
    |-- .gitignore
    |-- .travis.yml                        // ci配置
    |-- CHANGELOG.en-US.md
    |-- CHANGELOG.es.md
    |-- CHANGELOG.fr-FR.md
    |-- CHANGELOG.zh-CN.md                 // 版本改動說明
    |-- FAQ.md                             // 常見問題QA
    |-- LICENSE                            // 版權協議相關
    |-- Makefile                           // 腳本集合(工程化編譯)
    |-- README.md                          // 項目說明文檔
    |-- components.json                    // 組件配置文件
    |-- element_logo.svg
    |-- package.json
    |-- yarn.lock
    |-- .github                            // 貢獻者、issue、PR模版
    |   |-- CONTRIBUTING.en-US.md
    |   |-- CONTRIBUTING.es.md
    |   |-- CONTRIBUTING.fr-FR.md
    |   |-- CONTRIBUTING.zh-CN.md
    |   |-- ISSUE_TEMPLATE.md
    |   |-- PULL_REQUEST_TEMPLATE.md
    |   |-- stale.yml
    |-- build                              // 打包
    |-- examples                           // 示例代碼
    |-- packages                           // 組件源碼
    |-- src                                // 入口文件以及各類輔助文件
    |-- test                               // 單元測試文件
    |-- types                              // 類型聲明

組件開發

參考大多數 UI 組件庫的作法,能夠將 examples 下的示例代碼組織起來並暴露一個入口,使用 webpack 配置一個 dev-server,後續對組件的調試、運行都在此 dev-server 下進行。

單元測試

UI 組件做爲高度抽象的基礎公共組件,編寫單元測試是頗有必要的。合格的單元測試也是一個成熟的開源項目必備的。

打包

對於打包後的文件,統一放在 lib 目錄下,同時記得要在 .gitignore 中加上 lib 目錄,避免將打包結果提交到代碼庫中。

同時針對引入方式的不一樣,要提供全局引入(UMD)和按需加載兩種形式的包。

文檔

組件庫的文檔通常都是對外可訪問的,所以須要部署到服務器上,同時也需具有本地預覽的功能。

發佈

組件庫的某個版本完成開發工做後,須要將包發佈到 npm 上。發佈流程:

  • 執行測試用例
  • 打包構建
  • 更新版本號
  • npm 包發佈
  • 打 tag
  • 自動化部署

維護

發佈後須要平常維護以前老版本,通常須要注意一下幾點:

  • issue(bug 修復)
  • pull request(代碼 pr)
  • CHANGELOG.md(版本改動記錄)
  • CONTRIBUTING.md(項目貢獻者及規範)

參考

❤️ 愛心三連擊

1.若是以爲這篇文章還不錯,來個分享、點贊、在看三連吧,讓更多的人也看到~

2.關注公衆號前端森林,按期爲你推送新鮮乾貨好文。

3.特殊階段,帶好口罩,作好我的防禦。
image

相關文章
相關標籤/搜索