导言
由于事务需求,近期团队要搞一套自己的UI
组件库,结构方面仍是Vue
。而业界现已有比较老练的一些UI
库了,比如ElementUI
、AntDesign
、Vant
等。
结合结构Vue
,咱们挑选在C @ j ~ Q ^ –ElementUI
根底上进行改造。但造轮子
绝非易事,首要需求先去了解它整个但构建流程、目录规划等。
本文经过剖析ElementUI
完好的构建流程,最终给出建立一个齐备的组件库需求做的一些作业,希望关于想了解s U I U . I hElementUI
源码或者也有建立UI
组件库需求的你,能够供给一X X 5 h =些协助!
咱们先来看下ElementUI
的源码的目录结构。
目录结构解析
-
github:寄存了
ElemenR F [t UI
奉献指南、issue
和PR
模板 -
bui P U d ! = , Eld:寄存了打包相关的装备文件
-
examples:组件相关示例 demo
-
packages:组件源码
-
src:寄存进口文件和一些东西辅助函数
-
test:单元测验相关文件,这也是一个_ – # k j w K优秀的开源项目必备的
-
types:类型声明文件
说完文件目录,剩下还有几个文件(常见的.babelrc
、.eslintc
这儿就不展开阐明晰),在事务代码中是不常见的:
-
.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 Nmake
指令将会履行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.js
是commonjs
标准,而libf F a/index.js
是umd
标准,这个我在后边的打包模块会具体阐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.js
是umd
标准,由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
中最重要的部分了,下面我会一一对其间的重要指令进行阐明。
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 /
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 U成src/j i R % ] K c oindex.js
文件。
build/bin/i18n.js
依据 examples/i18n/page.json
和模版,生成不同言语的 demo
,也便是官网 demo 展现国际化的处理。
ElementUI
官网的国际化依据的模版是examples/pages/template
,依据不同的言语,别离生成不同的文件:
这儿面都是.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 { :
中:
最终官网展现出来的便是经过上面国际化处理后的页面:
支撑切换不同言语。
绕了一圈,回到主题: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.json
,4 ] 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 G将packages/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 文件夹
这儿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、动画的封装和公共办法。
咱们首要来* 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' */
其实在上面的scripts
的bu^ p * $ ! Iild/bin/build-entry.js
中咱们现已提到过:src/index.js
是由build-entry
脚Y t T C @ }本主动生成的。
这个文件首要做下以下工作:
-
导入了 packagex c 0 ts
下的一切组件 -
对外露出了 install
办法,把一k | 2 n u j {切的组件注册到Vue
上面,并在Vue
原型上挂载了一些大局变量和办法 -
最终将 install
办法、变量、办法导出
examples
寄存了 ElementUI
的组件示例。
其实从目录结构,咱们不难看出这是一个完好独立的Vue
项目。首要用于官方文档w | . { P的展现:
这儿咱们首要重视下docs
文件夹:
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 @,$(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 r过make
指令来履行一系列的编译和连接操作。在具有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
文件保护着Element
一A 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 0了makefile
为咱们编写了一些额定的脚本。
这儿要点说一下 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、创立package
(z 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
、紧缩、输出css
到lib
目录。
发布流程
打包完结,紧跟着便是代码的发布了。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 ` Z型W B v ! v项目是特别重要的e y,合理明晰的] R 0 Q l t `结构关于后期的开发和扩展都是很有意义的。ui
组件库的目录结构,我感] 2 H w ) @ Y觉ElementUI
的就很不错:
|-- 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
本文运用 mdnice 排版