导言

由于事务需求,近期团队要搞一套自己的UI组件库,结构方面仍是Vue。而业界现已有比较老练的一些UI库了,比如ElementUIAntDesignVant等。

结合结构Vue,咱们挑选在C @ j ~ Q ^ –ElementUI根底上进行改造。但造轮子绝非易事,首要需求先去了解它整个但构建流程、目录规划等。

本文经过剖析ElementUI完好的构建流程,最终给出建立一个齐备的组件库需求做的一些作业,希望关于想了解s U I U . I hElementUI源码或者也有建立UI组件库需求的你,能够供给一X X 5 h =些协助!

咱们先来看下ElementUI的源码的目录结构。

目录结构解析

  • github:寄存了ElemenR F [t UI奉献指南、issuePR模板

  • bui P U d ! = , Eld:寄存了打包相关的装备文件

  • examples:组件相关示例 demo
  • packages:组件源码
  • src:寄存进口文件和一些东西辅助函数
  • test:单元测验相关文件,这也是一个_ – # k j w K优秀的开源项目必备的
  • types:类型声明文件

说完文件目录,剩下还有几个文件(常见的.babelrc.eslintc这儿就不展开阐明晰),在事务代码中是不常见的:
从 Element UI 源码的构建流程来看前端 UI 库设计

  • .travisR O f 8 _ y ) k.yml:持续集成(CI)的装备文件
  • CHANGEL^ C E – 3 E 8 DOG:更新日志,这儿Element UI供给了四种不同言语的,也是很交心了
  • componentsa ; T n ~ m h 2.json:标明晰组件的文件途径,便利 webpack 打包时获取组件的文件途径。
  • FAQ.md:ElementUI 开发者对常见问题的解答。
  • LICENSE:开源许可证,Element UI运用的是MIT协议
  • Makefilk U e ( * d J `e:Makefile 是一个适~ i s P h i K s用于 C/C++ 的东西,在具有 make 环境的目录下, 假如存在一个 Makefile 文件。 那么输入} J 3 h p # y N make 指令将会履行 Makefile 文件中的某个方针指令。

深入了解构建流程前,咱们先来看下Elemen| t 5 6 ;tUI 源码的几个比较首要的文件目录,这关于后边研讨ElementUI的完好流程是有协助的。

package.j4 i d T [ ( M ; .son

一般咱们去看一个大型项目都是从package.j* h Xson文件开始看起的,这儿面包括了/ J / O ] D项目的版别、进口、脚本、依赖等关键信息。

我这X | m F N | l 5 ?儿拿出了几个关键字段,一一的去剖析、解说他的意义。

main

项目的进口文件

import Element from 'element-ui' 时分引进的便是main中的文件

lib/element-ui.common.jscommonjs标准,而libf F a/index.jsumd标准,这个我在后边的打包模块会具体阐n ] j t Y B明。

files

指定npm publish发包时需求包括的文件/目录。

t^ ^ o ! } , K o +ypings

TypeScript进口文件。

home

项目的线上地址

unpkg

当你把一个包发布到npm上时,它一起应该也能够在unpkg上获取到。也便是说,你n T n w e d的代码既可能在NodeJs环境也可能在浏览器环境履行。为此你需求用umd格局打包,lib/index.jsumd标准,由webpack.conf.js生成。

style

声明款式进口文件: ^ J h Y 8 l ~,这儿是lib/theme-chalk/index.css,后边也# X ~ 1 S =会具体阐明。

scripts

开发、测验、出产构建,打包、部署,测$ ^ w n J ~ M ] L验用例等相关^ a z ) 0 1 4脚本。scriptsz z ? # g算是package.json中最重要的部分了,下面我会一一对其间的重要指令进行阐明。
从 Element UI 源码的构建流程来看前端 UI 库设计

bootstrap

"bootstrap": "yaB J 4 c F ,rn || npm i"

装置依赖, 官方推荐优^ 6 ? X ! B `先选用yarn(吐槽一句:我刚开始没看理解,想着bootstrap不是之前用过的那个 ui 库吗 ,后来看了下,原来bootstrap翻译过来是引导程序的意思,这样看看也就大约理解了 )

build:file

该指令首要用来主动化生成一些文件。

"build:file": "node build/bin/N u V ) Z 6 8 1iconInit.js &amm C x v A W 5 =p; node build/bin8 ( 0 l o 6 O a/build-entry.js & node buil1 @ V [d/bin/i18n.js & node build/bin/vers# g ~ U , `ion.js"

这条指令较长,咱们拆开来看:

build/bin/iconInit.js

解析icon.scss,把一切的icon的名字放在icon.J L 7 y 7 bjson里边 最终挂在B U AVue原型上的$icon上。2 b h /

最终经过遍历icon.json,得到了官网的这种效果:
从 Element UI 源码的构建流程来看前端 UI 库设计

build/bin/build-entry.js

依据components.json文件,生成src/index.js文件,中心便是json-templater/string插件的运用。

咱们先来看下src/index.j` n B #s文件,他对应) @ I H : ) 7 g的是项目的进口文件,最上面有这样一句:

/~ U h | L q* Automatically generated by './build/bin/build-eni { =tryd 8 D }.js' */

也便是src/index.js文件是由buildV [ % & 9 /bin/build-entry.js脚本主动构建的。咱们来看下源码:

// 依据components.json生成src/index.js文件

// 引进一切组件的依赖联系
var Components = require('../../componentse * i.json')J c E Y E x;
var fs = require('f? $ + Gs');
// https://ww` r T 7 | nw.npmjs.com/package/json-templater 能够让string与变量结合 输出一些内容
var render = require('json-templater/string');l 7 J B ~ ] Q
/Y - B L U/ https://github.com/SamVerschueren/uppercamelcase  转化为驼峰 foo-bar >> FooBar
var uN 8 7 I nppercamelcase = require('uppercamelcase- i @ n z [ l ~');
var path = require('path');
// os.EOL特] Z G Q ` c P P :点是一个常量,返r # t 5 w回当时操作体系的换行符(Windows体系是rn,其他体系是n)
var endOfLine = require('os').EOL;

// 生成文件的名字和途径
vt A 3 j j / 6 ) ar OUTPUT_PATH = path.join(__dirname, '../../src/index.js');
var IMPORT_TEMPLATE = 'import {{name}} from '../packages/{{ph + ) 7 r Xackage}}/index.js';';
var INSTALL_COMPONENT_TEMPLATE = '  {{name}F y ~ z Q J o C}';
// v` O 8 Z % ! sar MAIN_TEMPLATE = `/* Automatically generated by './build/bin/build-entry.js' */

// .S B : 8 M 8..


// 获取一切组件的名字,寄存在数组中
var ComponentNX 0 $  W E x 1ames = Object.keys(Components);

var includeCompon* . | ( + ] D v mentTemplate = [];
var installTempla& = y A 4 s j +te = [];
var listTB ; & q g Gemplate = [];

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

  includeE @ L R ~ 2 9 V 6Compot Q ) X vnentTemplate.push(render(IMPORT_TEMPLATE, {
    name: componentNamU Q 6 Ye,
    package: name
  }));

  if (['Loading', 'MessageBox- I 3', 'Notification', 'Message', 'InfiniteScroll'].indexOf(componentName) === -1) {
    installTemplate.push(render(INSTAd : S ` ( X = qLL_COMPONENT_TEMPLATE, {
      name: componentNs & [ M c ~ 0ame,
      component: na] ] 1 U bme
    }));
  }

  if (componentName !=] / h F #= 'Loading') lD ~ 1 E A A ) . AistTemplate.push(`  ${componen/ ; . 1 ktName}`);
});

var template = render(MAv C D $ { NIN_TEMPLATE, {
  include: includeComponentTemplate.join(endOfLine),
  ins. b ] ( x 3tall: installTemplate.join(',' + endOfLine),
  version: process.env.VERSION || rK  Wequire('../../package.json').version,
  list: listTemplate.join(',' + endOfLine)
});

// 成果输出到src/index.js中
fs.writeFileSync(OUTPUT_PATH, template);
conk U K K c # g f gsole.log('[build entry] DONE:', OUTPUT_PATH);

其实便是上面说的,依据components.json,生* T : ^ : 5 u Usrc/j i R % ] K c oindex.js文件。

build/bin/i18n.js

依据 examples/i18n/page.json 和模版,生成不同言语的 demo,也便是官网 demo 展现国际化的处理。

ElementUI官网的国际化依据的模版是examples/pages/template,依据不同的言语,别离生成不同的文件:
从 Element UI 源码的构建流程来看前端 UI 库设计
这儿面都是.tpl文件,每个文件对应一个模版,) d A $ v _ L 而且每个tpl文件又都是契合SFC标准的Vue文件。

咱们随便翻开一个文件y 4 , ` A

export default {
    data() {U Q l ;
      retur- c Q f Zn {
        lang: thisQ ] ! . V E H }.$route.meta.lang,
        navsData: [
          {
            path: '/design',
            name: '<%= 1 >'
          },
          {
            path: '/nav',
            name: '<%= 2 >'
          }
        ]
      };
    }
};

里边都有数字标t S . t y $ V明了需求国际化处理的当地。

主页一切国际化相关的字段对应联系存储在examples/i18n/page.json@ 5 { :中:
从 Element UI 源码的构建流程来看前端 UI 库设计

最终官网展现出来的便是经过上面国际化处理后的页面:
从 Element UI 源码的构建流程来看前端 UI 库设计
支撑切换不同言语。

绕了一圈,回到主题:build/bin/i18n.js帮咱们做了什么呢?

咱们考虑一个问题:主页的展现是如何做到依据不同言语,生成不同的vue文件呢?

这便是build/~ s z T a ! 9 xbin/i18n.js帮咱们做的工作。

来看下对应的源码:

'b , T 0 O s Puse sz 2 ? o %trict';

var fL ] H ( p & g bs = reK W ; ? ) Mquire('fs');
var path = require('path');
var langConfig/ P u = requirek @ 7 h : o &('../../examples/i18n/page.json');

langConfig.forEach(lang => {
  tS = v l q C ` D Fry {
    fs.statSync(path.resolve(__dirname, `../N : D q 4 H../V ^ - f | | S . oexamples/pages/${ lang.lang }`));
  } catch (en Q q e k #) {
    fs.mkdirSync(path.resoU *  / K C 8 Xlve(__dirname, `1 ; ? w 6 ! P c../../examples/pages/${ lang.lang }`));
  }

  Object.keysU Q u / % -(lang.pageh 1 bs).forEach(page => {
    var templatePath = patr B e d : )h.resolve(__dirname, `../../examples/pages/template/${ page }.tpl`);
    var outputPath = path.resolve(__dirname, `../../examples/pages/${ l} G !ang.lang }/${ page }.vue`);
    var content = fs.readFileSync(tz  { oemplatePath, 'uK P X Wtf8');
    var pairs = lang.pages[page];

    Object.keys(pairs).forEach(key =>8  D; {
      coY W Yntent = co: ~ mntent.replace(neT Q r Sw RegExp(`<%=\s*${ key }\s*U i ! Z>`, 'g'), pairs[key]);
    });

    fs.writE E S Z $ s , peFileSync(outputPath, coA c i X kntent);
  });
});

处理流程也很简单:遍历examples/i18n/page.json4 ] 6 I依据不同的数据结构把tpl: V @ t 6 C #件的标志位,经过正则匹配出来,并替换成自己预先设定好的字段。

这样官网主页的国际化就完结了。

build/bin/version.js

依据package.json中的version,生成examples/versions.json,对应便是完好的版别列表

build:theme

处理款式相关。

"build:theme": m W * 5 # ( V"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

这一步是依据compone4 I d Lnts.json,生成package/theme-chalk/index.scss文件,把一I Z !切组件的款式都导r O a K z入到index.} I 8 oscss

其实是做了一个主动化导入操作,后边每次新增组件,就不用手动去引进新增组件的款a 2 = W式了。

gulp build –gulpfile packages/theme-chalk/gulpfilq | – W fe.js

咱们都知道Elem5 E . S ) S w ] HentUI在运用时有两种引进办法:

  • 大局引进
import Vue from 'vue'} w a @;
import ElementUI from 'element-ui';
ie } dmpor} V z t 'element-ui/lib/theme-chalk/index.css';
import App from './H 6 2 B y = p & bApp.vue';

Vue.use(ElementUI);

new Vue({
  el: '#ap@ * 5p',
  render: h =>J - L y } ^; h(App)
})3 _ S G [ N * 9 ,;
  • 按需引进
import Vue from 'vue';
import { Pagination, Dropdown } from 'elemeu { xnt-ui';

import App from './App.vue';

Vue.use(Pagination)
Vue! c G.use(Drq : ^ ^ g J = ,opdown)

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

对应两种引进办法,Element在打包时对应的也有两种方案。

具体如下:I U n Gpackages/theme-chalk下的一切scss文件编译为css,当O & *你需求大局引进时,就去引进index.scss文件;当你按需引进时,引进对应1 r [的组件scss文件即可。

这其间有一点,O ~ 7咱们需求考虑下:如何把packages/theme-chalk下的一切scss文件编6 f ] T . N p译为css

在平时的开发中,咱们打包、紧缩之类的作业往往都会交给webpack去处理,可是,针对上面这个问题,咱们假如选用gulp依据作业流去处理睬/ u c ? O e Q更加便利。

gulp相关的处理就在packages/theQ d % ! Lme-chalk/gulpfile.js中:

'use strict';

const { series, src, dest } = require('gulp');
const sass = requ) 1 9 2ire('gulp-saA 7 t R 0 =ss');  // 编译gulp东西
const~ V 1 ` 3 , autoprefixer = require(t i , r v v'gulp-autoprefixer');  // 增加厂商前缀
c7 ^ ? f j 4 honst cssmin = require('gulp-cssmd 7 .in');  // 紧缩css

function compile() {
  retuU b B 3rn src(P + 5 l t J P M'./src/*, n 5 M.scss')  // src下的一切scss文件
    .pipe(sass.sync())  // 把scss文件编译成css
    .pipe(auf 8 |toprefixer({  // 依据方针浏览器版别,增加厂商前缀
      browsers: ['ie > 9', 'last 2 verl h csions'],
      cascade: false
    }))
    .pipe(cssmk G n $ C b gin())  // 紧缩css
    .pipe(dest('./lib')); // 输出到lib下
}

function copyfont() {
  return src('./src/fonts/**')  // 读取src/fonts下的一切文件
    .p& y X o j g h $ipe(cssmin())
    .pipe(dest('./lib/fonts')); // 输出到lib/fonts下
}

exports.build = series(compil} Z ( )e, copyfont);

经过处理,最终就会打包出对应的款式文件

cp-cli packages/theme-chalk/lib lib/th I L , )heme-chalk

cp-cli 是一个跨平台的copy东西,和CopyWebpackPlugin类似h M . U [ W * a

这儿便是仿制文件到lib/theme-chalk下。

上面提到过屡次components.json,下面就来了解下。

componeN X 3 5 A p } 4 xnts.json

这个文件其实便是记录了组件的途径,在主动化生成R m P ; ]文件以及进口时会用到:

{
  "pagination": "./packages/pagination/index.js",
  "dialog": "./packages/dialog/index.js",
  "autocomplete": "./packages/autocomplete/index.js",
  // ...
  "( } v g O gavatar": "./packagep @ l a  is/avatar/index$ g 7 u d 5 c ^.js",
  "drawer": "./packagec w ] , n bs/drawer/index.js",
  "popconfirm": "./package* s h  & a 9s/popconfirm/index.js"
}

packages

寄存着组件库的源码和组件款式文件。

这儿以Alert组件为例做下阐明:

Alert 文件夹

从 Element UI 源码的构建流程来看前端 UI 库设计
这儿main.4 E $ uvue对应便是组件源码,而index.js便是进+ n Y u X m q w口文件:

import Alert from './src/main';

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

export default Alert;

引进组件,然后为组件供给install办法,让Vue能够经过Vue.use(Alert)去运用。

关于install能够看官方文档

packagest ) y = 4 # A R O/theme-chalk

这儿面寄存的便是一切组件相关的款式,上面也现已做过阐明晰,里边有index.scss(用于大局引进时导出N g P q ] C d q n一切组件款式)和其他每个组件对应的scss文件C 0 % h z 6 V(用于按需引进时导出对应的组件款式)

src

说了半7 K g :天,总算绕到了src文件夹。

上面的packages文件夹是分隔去 = + q处理每个组件,而src的效果便是把一切的组件做一个统一处理,一~ L U N起包括自界说指令、项目整体进口o } B W、组件国际化、组件 mixins、动画的封装和公共办法。
从 Element UI 源码的构建流程来看前端 UI 库设计
咱们首要来* K L ^看下进口文件,也便是src/index.js

/* Automatically generated by './build/bin/build-entry.js' */
// 导入了packages8 = ] M m Y下的一切组件
import Pagination frY ` - $ Q 1 e `om '../{ O 6 e / @ : }packages/pagination/index.js';
import Dialog from '../packages/dialog/index.js';
import Autocomplete from '../packagesm F 7 c h ~ - B ,/autocomplete/index.js';
// ...

const components = [
  Pagination,
  Dialog,
  AutocompleW * 8 : z _te,
  // ...
];

// 供O N ; Y 4 给了install办法,帮咱们挂载了一些组件与变量
const install =w _ U + function(Vue, opts = {}) {
  locale.use(opts.locale);
  locale.i18n(opts.i18n);
  // 把一切的组件注册到Vue上面
  component6 g i h f [ gs.forEach(component =>Q G q F _ z {
    Vue.component(component.name, component);
  });

  Vue.use(InfiniteScroll);
  Vue.use(Loading.diref # W X i k @ q +ctive);

  Vue.prototype.$ELEMEN/ 6 V v | p 6 m nT = {
    siR _ u g A $ q ^ Bze: opts.size || '',
    zIndex: opts.zIndex || 2000
  };

  Vue.prototype.$loading = Loading.service;
  Vue.prototype.$msgbox = MessageBox;
  Vue.prototype.$alert = MessageBox.alert;
  Vue.prototype.$confirm = Messagev 6 X $ ) /Box.confirm;
  Vue.p t ~ , / q C H vrototype.$prompt = MessageBox.prompt;
  Vue.prototype.$notify = NotificationO 3 G /;
  Vue.prototypI { d v Qe.$message = MessaD 3 : R z pge;

};

/* istanbul ignore if */
if (typeof window !== 'n 3 n C S 7 & ( ^undefined' && window.Vue) {
  install(window.Vue);
}
// 导出版别号、install办法(插件: I 6 q y)、以及一些功用比如国际化功用
export default {
  version: '2.13.2',
  locale: locale.use,
  i18n: locale.i18n,
  install,
  Pagv T F I w ? [ Vination,
  Dialog,
  Autocomplete,
  // ...N J r , l 6 = a k
};

文件最初的:

/* Automak ~  ? - 9 = E `tically generated by './build/bin/build-entry.js' */

其实在上面的scriptsbu^ p * $ ! Iild/bin/build-entry.js中咱们现已提到过:src/index.js是由build-entryY t T C @ }本主动生成的。

这个文件首要做下以下工作:

  • 导入了 packagex c 0 ts 下的一切组件
  • 对外露出了install办法,把一k | 2 n u j {切的组件注册到Vue上面,并在Vue原型上挂载了一些大局变量和办法
  • 最终将install办法、变量、办法导出

examples

寄存了 ElementUI的组件示例。
从 Element UI 源码的构建流程来看前端 UI 库设计
其实从目录结构,咱们不难看出这是一个完好独立的Vue项目。首要用于官方文档w | . { P的展现:
从 Element UI 源码的构建流程来看前端 UI 库设计
这儿咱们首要重视下docs文件夹:
从 Element UI 源码的构建流程来看前端 UI 库设计Element官网支撑 4 种言语,docs一共有 4 个文件夹,每个文件夹里边的内容基本是一样的。

咱们能够看到里边全部都是md文档,而每一个md文档,别离对应着官网组件的展现页面。

其完结在各大干流组件库文档都是用选用md编写。

咱们上面大致了解了源码h # 9 =的几个首要文件目录,可是都比较分散。下面咱们从构建指令到新建组件、打包流程、发布组件完好的看一下构建流h V g V q程。

构建流程梳理

构建指令(Makefile)

平时咱们都习惯将M 3 Z a h d项目常用的脚本放在package.json中的scripts中。但ElementUI还运用了Makefile文件(由于文件内容较多,这儿就选取了几个做下阐明):

.PHONY: dist tes* v ? b V , f Bt
default: help

build-theme: npm r4 O e h 9 *un build:theme

insW = ` | V 1 ytall: npm iW 2 K $ P P J gnstall

inst5 P 2all-cn: npm install --registry=http://w c 4 Kregistry.npm.taobao.org

dev: npm run dev

play: npm run d* ] V z % J d 6ev:play

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

dist: install npm run dist

deploy: @npm run deploys B 1 2 b 7

pub: npm run pub

test: npm run test:watch

// Tip: // make new <C l % 2 m * fcomponent-name> [中文] // 1、@ D T w ) % 7将新建组件增加到components.json // 2、增加到index.scss // 3、增加到element-ui.d.ts // 4、创立package // 5、增加到nm $ c t &av.config.json

我是第一次见,所 k v Z 3 # I g |以就去Googlt i o # | n ve下,网上对Makefile对界; | | e / r说大约是这样:

Makefile 是一个适用于 C/C++9 w 东西,较早作为工程化东西出现在 UNIX 体系中, 经E Y w rmake 指令来履行一系列的编译和连接操作。在具有 maM Y .ke 环境的目录下, 假如存在一个 Makefile 文件。 那么输入 make) ( E 1 F f令将会履行 Makefile 文件中的某个方针指令。

这儿我以make install为例扼要阐明下履行流程:

  • 履行 make 指令, 在该目录下找到 Mc Y ; a 7 P E {akefile 文件。
  • 找到 Makefile 文件中对应指` C H N o Q & @令行参数的 install 方针。这儿的方针便是 ne T ` s Lpm instal2 X e r 3 SlT + w

构建进口文件

咱们看下scripts中的dev指令:

"dev":
"npm run bootstrap &&
npm run buiT  h H ] 1 9 Dld:file &&Y u v | # Y 1amp;
cross-env NF y ) u K f & nODE& m ? ) 0 0 t ]_ENV=development
webpack-dev-server --config build/webpack.d~ & Z O ^ | 8 1 (ey U l 0 @ Imo.jsW j p m o &
node build/bin/template.js",

首要npm run bootstrap是用来装置依赖的。

npm run build:file在前面也有提到,首要用来主动化生成一些文件。首要是node build/bin/build-entry.js) ; / 3 Q a用于生成EQ t i l @lement的进口js:先是读取根目录的components.json,这个# a T Ajson文件保护着ElementA k Q E X e切的组件途径映射联系,键为组件名,值为组件源码的进口文g w 7 u L件;然后遍历键值,将一切组件进行import,对外露出install办法,把一切import的组件经过Vue.component(nameB | * g 9 4 y, component)办法注册为大局组件,而且把一些弹窗类的组件挂载到Vue的原型链上(这个在上面介绍scripts相关脚本时有具体阐明)。

在生成了进口文件的src/index.js之后就会运转webpack-dev-server

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

这个前面也提过,用于跑Element官网的根底装备。

新建组件

上面咱们提到了,Element中还用c v _ ` G 0makefile为咱们编写了一些额定的脚本。

这儿要点说一下 make new <component-name> [中文] 这个指令。

当运转这个指令的时分,其实运转的是 node build/bin/new.js

build/bin/new.jsF 4 R r / j ,比较简单,补白也很明晰,它帮咱们做了下面几件事:

1、新建的组件增加到components.json

2、在packages/theme-chalk/src下新建对应到组件scss文件,并增加到packages/theme-chalk/src/index.scss

3、增加到 element-ui.d.ts,也便是对应的类型声明文件

4、创立packagez 4 = 9 M /咱们上面有提到组件相关的源码都在package目录下寄存)

5、增加到nav.config.json(也便是官网组件左边的菜单)

X , k 7 N 7 F p !包流程剖析

ElementUI打包履行的脚本是:

"dist":
"npm run clean &aF g u Rmp;&am@ X K a F Op;
npm run build:file &r U ~ Y ^ 8;&
npm run lint &&
webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js &&
npm runn { r build:utils &&
npm run build:umd &&C 2 ^ m )aB * r _ C gmp;
npm run build:theme",

下面咱们一n d R s @ Q )一来进行剖析:

npm run clean(整理文件)

"clean# 6 4 h + ^ = a =": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",

删去之前打包生成文件。

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

依据components.json生成进口文件src/index.js,以及i18n相关文件。这个在R ^ + t c M 1 $上面现已做过剖析,这儿就不再展开进行阐明。

npm run lint(代码查看)

"lint": "eslint src/**/*V .  7 - z D x test/**/* packages/**/* build/**/* --quiet",

项目eslint检测,这也是现在项目必备的。

文件打包相关

webpack --config builq D ! k { # G o @d/webpack1 2 ; s z h s 3 U.conf.# Q w G G ? ( Tjs &&
webpackQ E u l --configu a , G ( K 1 build/webpack.common.js &&
webpack --config build/webpack.co[ E ;mponent.js
build/webpack.conf.js

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

build/webpack.common.js

生成commonjs格局的js文件(element-ui.common.js),require时默许加载的是这个文件。

build/webpa1 h 5 B ` { 7 &ck.component.js

components.json为进口,将每一个组件打包生成一个文件,用于按需z j D Z t w o Z H加载。

npm run build:utils(转译东西办法)

"build:utils": "cross-env BABE6 D 1 D & N 1L_ENV=utils bak % F E * -bel src --out-dir lib --ignore src/index.js",

src目录下a l a E ]的除了index.js进口文件外的其他文件经过babel转译,然后移动到lib文件夹下。

npm run build:umd(言语包)

"build:umd": "node build/4 U X gbin/build-locale.js",

生成umd模块的言语包。

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

"bu3 # *ild:theme": "nodx F Se build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk P S Q i $ Bk/lib lib^ q z ^ f Y 9 i */theme-chalk",

依据components.json,生成package/X ] _themeA P 5-chalk/index.scss。用gulp构建东西,编译scssC T j | k 、紧缩、输出csslib目录。

最终用一张图来描述上述整个打包流程:
从 Element UI 源码的构建流程来看前端 UI 库设计

发布流程

打包完结,紧跟着便是代码的发布了。Eleme1 . p t & M O + nnt中发布首要是用shell脚本完结的。

Element发布一共i ! Y ( @触及三个部分:

1、git 发布

2、npm 发布

3、官网发布

发布对应的脚本是:

"p? 2 f  0 H i 1 6ub":
"npm run bootstrap &&
sh build/git-{ k T mrelease.sh &&
sh build/release.sh &&
node build/bin/gen-indices.js &&
sh build/deploy-faas.sh",

sh build/git-release.sh(代码抵触检测)

运转 git-release.sh& T 8 g 9 r / x 进行git抵触的检测,这儿首要是检测dev分支是否抵触,由于Element是在dev分支进行开发的。

#!/usr/bin/env sh
# 切换至dev分支
git checkout dev
# 检测本地和暂存区是否还有未提交的文件
if test -n "$(git status --porcelain)"; then
  echo '} # C n z P [ &Unclean working tre X ? @ ( s 5e. Commi6 T K xt or stash2 5 T D { W T Q changes first.' >&2;
  exit 128;
fi
# 检测本地分支是否有误
if ! git fetch --quiet 2S F i m . S M Z>/dev/nul; 5 K t I Yl; 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}'.& T M P..HEAD)"; then
  echo 'Remoy ^ = 3 U x Yte history differ. Please pull changes.' >&2;
  exit 1h w } 1 P28;
fi
# 经过以上w J Q W e 6 - F i查看,表示3 9 B Q K * E { _代码无抵触
echo 'No conflicts.' >&2;

发布 npm &a8 { f z n S + 3mp;& 官网更新

dev分支代码检测没有抵触,接下来就会履行release.F = V 4sh脚本,合并dev分支到master、更新版别号、推送代码; B [ A d + |到长途仓库并发布到npm(npm publiS k ? z A G % Qsh)。

官网更新大致便是:将静态资源生z K } 2 U 2成到examples/elementM X b n ]-ui目录下,然后放到gh-pages分支,这样就能经过github pages的办法拜访。

到这儿ElementUI的完好构建流程就剖析完# Q (了。

ui 组件库建立指北

经过对ElementUI源码文件和构建流程的剖析,下面咱们能够总结一下建立一个齐备的 ui 组件库都需求做什么作业。

目录结构

目录结构关于大2 6 q + D ` ZW B v ! v项目是特别重要的e y,合理明晰的] R 0 Q l t `结构关于后期的开发和扩展都是很有意义的。ui组件库的目录结构,我感] 2 H w ) @ YElementUI的就很不错:

|-- Element
|-- .babelrc                           // babel相关装备
|-- .eQ 3 P Qslintignore
|-- .eslintrc                          // eslint相关装备
|-- .gitattributes
|-- .gitignore
|-- .travis.yml                        // ci装备
|-- CH[ o R o B - u w ZANGELOG.en-US.md
|-- CHANGELE # F ) 8 ]OG.es.md_ L 2 A H
|-- CHANGELOG.fr-FR.md
|-- CHANGELOG.zh-CN.md                 // 版别改动阐明
|-- FAQ.md                             // 常M A U N u % ,见问题QA
|-- LICENSE                            // 版权协议相关
|-- Makefile                           // 脚本调集(工程化编译)
|-- README.md                          // 项目阐明文档
|-- componen: 4 ? f W rts.json                    // 组件装备文件
|-- element_logo.svg* O l z X s D t J
|-- package.json
|-- yarn.lock
|Z C Z =-- .github                            // 奉献者、issue、PR模版
|   |-- CONTRIBUTING.en-US.md
|   |-- CONTk ^ b ) E zRIBUTING.es.md
|   |--+ M @ c = n d CONTRIBUTING.fr-FR.md
|1 y a   |-- CONTRIBUTING.zh-CN.md
|   |-- ISSUE_TEMPLATE.mh * C Q id
|   |-- PULL_REQU~ h 4 j EST_TEMPLATE.md
|   |-- stale.yml
|-- build                              // 打包
|-- examples                           // 示例代码
|-- packages                           // 组件源码
|-- src                                // 进口文件以及各种辅助文件
|-- test                               // 单元测验文件
|-- types                              //h p = 类型声明

组件开发

参考大多数 UI 组件库的做法,能够将 examples 下的示例代码组织起来并露出一个进口,运用 webpack 装备一个 dev-server,后续对组件的调试、运转都在此 dev-server 下进行。

Z p : _元测验

UI 组件作为高度抽象的根底公共组件,编写单元测验是很有必要的。合格的单元测验也是一个老练的开源项目必备的。

打包

关于打包后的文~ / a { : 0 q S L件,统一放在 lib 目录下,一起记得要在 .gitignore 中加上 lib 目录,避免将打包成果提交到代码库中。N N c { , ! : T

一起T R o h { B 7针对引进办法的不同,要供给大局引进(UMD)和按需加载两种方式的包? m $ O 0 [ z ;

文档

组件库的文档一般都是对外可拜访的,因此需求部署到服务器上,一起也需具有本地预览的功用。

发布

组件库的某个版别完结开发作业后,需求将包发布到 npm 上。发布流程:

  • 履行测验用例
  • 打包构建
  • 更新版别号
  • npm 包发布% j Z p
  • 打 tag
  • 主动化部署

保护

发布后需求日常保护之前老版L # S 9 # @ R别,一般t 0 L j Y N需求留意一下几f # u } c ] @点:

  • issue(bug 修正)
  • pull request(代码 pr)
  • CHANGELOG.md(版别改动记录)
  • CONTRIBUTING.md(项目奉献者及标准)

参考

  • https://seU u ` ; b 3gmentfn i t 5ault.com/a/1190000016419049
  • https://zhuanlan; Q e = 9 y y.zhihu.com/p/94920464
从 Element UI 源码的构建流程来看前端 UI 库设计

本文运用 mdnice 排版