工欲善其事必先利其器,既然咱們的 air-ui
大部分都是借鑑 element-ui
項目,因此本章咱們就來分析一下 element-ui
項目源碼。javascript
首先咱們到這個 github.com/ElemeFE/ele… , 把源碼下載下來css
由於是基於 yarn 安裝的,因此咱們也全局裝一下 yarn:html
F:\code\github\element>npm install -g yarn
C:\Program Files\nodejs\yarn -> C:\Program Files\nodejs\node_modules\yarn\bin\yarn.js
C:\Program Files\nodejs\yarnpkg -> C:\Program Files\nodejs\node_modules\yarn\bin\yarn.js
+ yarn@1.19.1
added 1 package in 7.777s
複製代碼
安裝成功,試一下指令是否正常:vue
F:\code\github\element>yarn --version
1.19.1
複製代碼
element-ui
的當前版本是 2.12.0
(我在寫這個文章的時候,其實已經更新到 2.13.0
,可是差異不大,都是一些細節的調整)java
F:\code\github\element>npm run dev
> element-ui@2.12.0 dev F:\code\github\element
> 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
> element-ui@2.12.0 bootstrap F:\code\github\element
> yarn || npm i
yarn install v1.19.1
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.7: The platform "win32" is incompatible with this module.
info "fsevents@1.2.7" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > karma-webpack@3.0.5" has incorrect peer dependency "webpack@^2.0.0 || ^3.0.0".
[4/4] Building fresh packages...
Done in 202.16s.
。。。
i 「wdm」: Compiled successfully.
複製代碼
第一次的時候,會先安裝依賴。在本地瀏覽器打開 localhost:8085
,能夠看到跟官網的站點同樣:node
首先咱們先看一些目錄結構:jquery
element
|---build/ 構建相關的文件
|---example/ 放置element api的頁面文檔
|---lib/ 組件構建打包以後的存放目錄
|---packages/ 放置element的組件(css樣式放置在這個目錄下theme-chalk下)
| |---button/ button 組件的目錄
| | |---src/ button 組件的業務代碼
| | |---index.js button 組件的定義文件
... 這裏面 N 個相同目錄格式的組件 ...
| |---theme-chalk/ 存放 scss 樣式
| | |---lib/ scss 打包以後的css文件
| | |---src/ scss 樣式文件
| | |---gulpfiles gulp 構建任務,將src目錄中的 scss 打包成 css並放到 lib 目錄
|---src/
| |---directives/ 放置自定義指令
| |---locale/ 放置語言的配置文件
| | |---lang/ 放置多語言
| |---mixins/ 放置組件用的混合文件
| |---transitions/ 放置動畫配置文件
| |---utils/ 放置用到工具函數文件
| |---index.js 組件註冊的入口文件
|---test/ 測試文件
|---types/ typescript 的數據類,用來給 IDE 在寫 ts 代碼時候的提示
|---components.json 存放單獨導出組件的json文件
|---package.json
複製代碼
能夠看到幾個有點奇怪的地方:webpack
theme-chalk
的目錄,辨識度過低,不注意看,根本找不到,爲啥不放 src 目錄的 styles 目錄呢?npm run dev
複製代碼
想要更快的瞭解一個項目,除了將環境跑起來以後,另外一個就是分析構建的方式了,因此接下來咱們分析一下 dev 環境的構建方式,首先從指令來看:git
"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",
複製代碼
這個實際上是一連串指令結合的,咱們一個一個分析:es6
"bootstrap": "yarn || npm i"
複製代碼
這個就是純粹的安裝依賴,沒有實際操做
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js"
複製代碼
這個又是一連串指令的結合,繼續拆開分析:
node build/bin/iconInit.js
複製代碼
簡單的來講,就是把packages/theme-chalk/src/icon.scss
這個 css 文件中的全部的 icon 描述字符都提取出來,好比這個:
.el-icon-turn-off:before {
content: "\e734";
}
複製代碼
其實就是把turn-off
這個子串提取出來,而後放入到這個examples/icon.json
文件裏面,這個文件其實就是一個大的 json 數組:
後面這個文件會在顯示字體圖標的時候,會拿出來作映射。 這樣的好處就是,之後添加新的字體圖標的時候,寫文檔的時候,不用手動去添加,它會自動生成這張映射表。
在啓動dev模式的時候,入口的 js 文件examples/entry.js
就會把這個 json 文件引入進入:
import icon from './icon.json';
Vue.prototype.$icon = icon; // Icon 列表頁用
複製代碼
會在 icon 列表的時候,用上這個映射表: 以中文爲例,那麼文件就在 examples/docs/zh-CN/icon.md
這個展現頁用到
### 圖標集合
<ul class="icon-list">
<li v-for="name in $icon" :key="name">
<span>
<i :class="'el-icon-' + name"></i>
<span class="icon-name">{{'el-icon-' + name}}</span>
</span>
</li>
</ul>
複製代碼
展現頁就是下圖:
node build/bin/build-entry.js
複製代碼
生成入口文件 index.js
, 簡單的來講,就是經過這個任務,來生成 src
目錄下的 index.js
(每次打包,這個 index.js
都會從新生成), 這個也是整個組件庫的入口文件。 他這個有一個模板,裏面有一些組件是內置會有的,好比:
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;
export default {
version: '{{version}}',
locale: locale.use,
i18n: locale.i18n,
install,
CollapseTransition,
Loading,
{{list}}
};
複製代碼
他是經過獲取根目錄下的components.json
這個文件。這個文件是全部能夠配置的組件的列表。一旦這個組件在列表裏面,那麼我打包的時候,就會把這個組件打進去。反之,若是裏面有些組件,好比 drawer
和 divider
這種,個人項目根本不會用到。那麼我就把這兩個組件從 components.json
中剔除,那麼生成的 src/index.js
這個 js,就不會包含這兩個組件了。 從而實現可定製化組件的方式。
node build/bin/i18n.js
複製代碼
生成這個站點的 i18n 多語言的靜態頁面, 經過這個任務,咱們能夠生成你想要支持的多語言文件,這個多語言文件其實就是文檔站點的多語言的每個頁面的詞條,跟組件的多語言沒有關係
每個頁面用到的多語言都有,從 json
來看,目前就只支持 4 種多語言:
具體看生成的站點就知道了:
就四種,那麼是怎麼替換的呢? 其實很簡單,根據不一樣的語言的不一樣的頁面,而後把對應的詞條 json 傳進去替換就好了。
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);
});
複製代碼
模板就是 .tpl
,結果頁就是 .vue
文件, 舉個例子,就以 design 這個頁面來講:
他其實就是把中文下的design
頁面的詞條取出來,而後塞到design.tpl
這個模板裏面:
最後生成的結果頁就是 design.vue
這個頁面:
這樣就把這個站點的多語言相關的頁面都替換完了。
node build/bin/version.js
複製代碼
生成對應的版本列表文件 examples/version.json
, 這個沒啥好說的,硬編碼寫入到一個文件而已。 這個文件會在 changelog
這個頁面的時候, 動態用 ajax
來請求:
xhr.open('GET', '/versions.json');
複製代碼
而後再結合根目錄下的 CHANGELOG.{lang}.md
來顯示出真正的 log 列表, 這個 md 文件會在 changlog.vue
文件中被引用進去:
import ChangeLog from '../../../CHANGELOG.zh-CN.md';
複製代碼
cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js
複製代碼
到這一步纔開始進行webpack
構建了,對應的webpack
的配置文件是webpack.demo.js
。 cross-env
是爲了兼容各個OS系統的環境設置的方式。
由於咱們的 env
是 dev,而且是非 play
模式, 因此 js 執行的入口文件是 example/entry.js
這個文件。 這個也是這個項目的 入口 vue 文件 (就是初始化 vue 的那個文件,而入口文件是 index.tpl
),而後通過 webpack
打包以後,這個文件就會變成 [name].js
,而後被嵌進去到 index.tpl
這個模板文件裏面:
從源碼上能夠看到,他生成了入口的 js 文件以後,就嵌進去了 index.tpl
這個文件裏面。而index.tpl
這個模板文件,也會在打包的時候,進行 webpack
的參數填充,從而變成 真正的入口文件 index.html
:
new HtmlWebpackPlugin({
template: './examples/index.tpl',
filename: './index.html',
favicon: './examples/favicon.ico'
}),
複製代碼
至於爲何這個名字 [name]
是 main
,這個確實有點奇怪,按照道理說,個人入口文件是 entry.js
,若是output
沒有特別指定的話, 那麼output
的 filename
中的 name
,也應該是 entry
纔對,怎麼會變成 main
???
後面查了一下,原來entry
配置是:
entry: './examples/entry.js'
複製代碼
那麼在 webpack
的解析中,就會變成:
entry: {
  main: './examples/entry.js'
}
複製代碼
因此名字就會變成 main
了, 同時由於 mode 是 dev,因此還會開啓本地服務:
devServer: {
host: '0.0.0.0',
port: 8085,
publicPath: '/',
hot: true
},
複製代碼
這樣子 webpack 打包的部分就完成了。 可是還有最後一步呢。
node build/bin/template.js
複製代碼
其實這一步就是watch
,針對全部的頁面模板,進行監聽,其實就是針對 examples/pages/template
這個目錄下的全部的模板,進行監聽,若是有修改的話,就從新執行 npm run i18n
任務,也就是
"i18n": "node build/bin/i18n.js",
複製代碼
其實就是 1.2.3
的任務,就是爲模板插入多語言, 注意,這時候的 __dirname
就是 webpack 運行環境的內存中的目錄了,一旦 tpl 文件變了,就會致使對應的 vue 文件改變,從而致使 webpack
從新 reload,而後界面就刷新了。由於用的是 webpack-dev-server
這個 server, 他有自帶監聽機制。rules
裏面的 loader 相關的文件一旦修改的話,是會熱更新的:
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
複製代碼
最後總結一下:經過打包 dev 模式,咱們能夠知道,首頁文件是examples/index.tpl
, 入口 js 文件是 examples/entry.js
(這個其實就是vue 的入口文件), 而後經過 webpack-dev-server
啓動一個熱更新服務 (打包的文件都是放在內存裏面的)。事實上,以上這些都沒有涉及到 element-ui
是怎麼編寫 ui 組件庫的。只是將其 api 站點運行起來而已。
npm run deploy:build
複製代碼
以前是文檔站點的本地環境版本,也就是運行在內存的,接下來這個是線上部署版本,就是打包成靜態文件:
F:\code\github\element>npm run deploy:build
> element-ui@2.12.0 deploy:build F:\code\github\element
> npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME
...
複製代碼
跟開發模式有點像, 只不過這時候就輸出到 examples
目錄下了,而不是輸出到內存了。 整個靜態文件都輸出到 examples/element-ui/
這個目錄裏面,至關因而一個完整的站點。若是放到 webserver
下面。就是一個靜態站點。 這個就不分析,由於原理跟 dev 差很少。
npm run dist
複製代碼
具體log:
F:\code\github\element>npm run dist
> element-ui@2.12.0 dist F:\code\github\element
> 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:um
d && npm run build:theme
...
複製代碼
目標目錄就是 lib
目錄,原先的項目是沒有 lib
目錄的, 打完包,纔會有 lib
目錄。
總的指令是:
"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",
複製代碼
裏面有些指令前面已經分析過了,這邊就不細講了:
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
複製代碼
其實就是先清理目錄:
lib
目錄packages
下的 lib
目錄test
目錄下的測試目錄npm run build:file
複製代碼
同上面 dev 構建的 step 1.2
同樣,不在重複講,無非就是 初始化 icon
, 生成入口文件
, 生成 多語言靜態頁
, 生成版本列表文件
"lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet"
複製代碼
這個就是跑代碼檢測工具
webpack --config build/webpack.conf.js
複製代碼
用 webpack
打包,他是把整個 webpack 的打包任務分紅了好幾塊,這個是第一塊,這一塊的處理其實很簡單, 就是將 src/index.js
這個入口文件進行打包,而後生成 umd
通用加載模式的文件:
mode: 'production',
entry: {
app: ['./src/index.js']
},
output: {
path: path.resolve(process.cwd(), './lib'),
publicPath: '/dist/',
filename: 'index.js',
chunkFilename: '[id].js',
libraryTarget: 'umd',
libraryExport: 'default',
library: 'ELEMENT',
umdNamedDefine: true,
globalObject: 'typeof self !== \'undefined\' ? self : this'
},
複製代碼
這時候個人 lib 目錄就生成了,採用 umd
模塊化加載的方式來打包入口文件 index.js
文件。
webpack --config build/webpack.common.js
複製代碼
mode: 'production',
entry: {
app: ['./src/index.js']
},
output: {
path: path.resolve(process.cwd(), './lib'),
publicPath: '/dist/',
filename: 'element-ui.common.js',
chunkFilename: '[id].js',
libraryExport: 'default',
library: 'ELEMENT',
libraryTarget: 'commonjs2'
},
複製代碼
這個是另一種模塊化方式的打包, 這個是將入口文件src/index.js
打包成 element-ui.common.js
這個文件。
這時候應該會有一個疑問??? 這兩個任務打出來的文件,明顯大小不同?? 那爲啥同一個入口文件,能夠經過不一樣的配置,打出兩個不同的 出口文件??
後面發現原來是有差異的,這兩個其實都是入口文件,只不過 element-ui.common.js
這個是 ES6 Module
模塊化加載方式的入口文件。 而 index.js
這個是 umd
通用模塊化加載方式的入口文件。 他的 webpack
的 output
配置有一個這個配置:
libraryTarget: 'umd',
複製代碼
標明要打包成 umd
的模塊化方式的入口文件。這是一種能夠將你的 library
可以在全部的模塊定義下均可運行的方式。它將在 CommonJS
, AMD
環境下運行,或將模塊導出到 global
下的變量,也能夠用script
方式引入,像 jquery
, lodash
,underscore
這些工具庫, 都是這種打包方式。
打包出來實際上是這樣子:以jquery 爲例:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS之類的
module.exports = factory(require('jquery'));
} else {
// 瀏覽器全局變量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// 方法
function myFunc(){};
// 暴露公共方法
return myFunc;
}));
複製代碼
其實就是判斷環境,以兼容各類模塊加載方式。 因此事實上,打完包的這個 index.js
開頭就是這個:
!function (e, t) {
"object" == typeof exports && "object" == typeof module ? module.exports = t(require("vue")) : "function" == typeof define && define.amd ? define("ELEMENT", ["vue"], t) : "object" == typeof exports ? exports.ELEMENT = t(require("vue")) : e.ELEMENT = t(e.Vue)
}("undefined" != typeof self ? self : this, function (e) {
return function (e) {
var t = {};
複製代碼
而本步驟的這種方式,其實就是打包成相似於 CommonJs
的方式,只須要將全部的依賴文件都打包成一個文件便可。 而上面的 libraryTarget: commonjs2
其實跟 libraryTarget: commonjs
差很少,只不過 commonjs2
導出的是 module.exports.default
, 按照個人理解,他其實就是 es6
提出的模塊加載機制 ES6 Module
。
事實上,在我本身的理解中,關於 webpack
的 libraryTarget: commonjs
其實對應就是 CommonJS
的模塊化加載方式,而 libraryTarget: commonjs2
其實對應的是 es6 的 ES6 Module
的模塊化加載方式,他倆的不一樣之處在於:
CommonJs
導出的是變量的一份拷貝,ES6 Module
導出的是變量的綁定(export default 是特殊的)CommonJs
是單個值導出,ES6 Module
能夠導出多個, 通常不提倡 export default
和 export {a, b, c}
混用。(事實上,我後面這點踩坑了)CommonJs
是動態語法能夠寫在判斷裏,ES6 Module
靜態語法只能寫在頂層, 並且 ES6 Module
是隻讀的,不能被改變ES6 Module
默認就是嚴格模式而之因此要用 ES6 Module
的模塊化加載方式,主要是由於這邊涉及到一個模塊化的分類,好比:
AMD(Asynchronous Module Definition,異步模塊定義)
,表明產品爲:Require.js
CMD(Common Module Definition,通用模塊定義)
,表明產品爲:Sea.js
CommonJS
規範,具體規範是:
require
引入其餘模塊或者包exports
或者module.exports
導出模塊成員ES6模塊化
。ES6
語法規範中,在語言層面上定義了 ES6 模塊化規範,是瀏覽器端與服務器端通用的模塊化開發規範。規範以下:
import
關鍵字export
關鍵字總結就是:推薦使用ES6模塊化
,由於AMD
,CMD
侷限使用於瀏覽器端,而CommonJS
在服務器端使用。ES6模塊化
是瀏覽器端和服務器端通用的規範。事實上,後面在作 air-ui
的時候,我直接拋棄 umd
的通用模塊化加載方式,直接採用 ES6 Module
的方式來引用, 就是 import
的方式來引入(固然若是後面業務的擴展真的須要有相似於 umd
的引用方式,再把這種打包方式加上去,可是若是隻是用來作 vue 項目的話,基本上都是用ES6 Module
的方式來引用)。
webpack --config build/webpack.component.js
複製代碼
接下來將單獨的組件也打包出來
const Components = require('../components.json');
mode: 'production',
entry: Components,
output: {
path: path.resolve(process.cwd(), './lib'),
publicPath: '/dist/',
filename: '[name].js',
chunkFilename: '[id].js',
libraryTarget: 'commonjs2'
},
複製代碼
生成的這些文件其實就是單獨組件的es6 Module
的模塊化加載方式,是能夠被單獨引用的。
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
複製代碼
這個其實就是將 src
目錄下的 除了 src/index.js
這個文件以外的其餘全部的文件都先通過 babel
轉換,而後輸出到 lib
目錄下:
由於這幾個目錄在打包的時候,是經過 externals
的方式打包的,並無打進去 element-ui.common.js
中裏面去,因此這邊要另外處理。因此才用這種方式 , 其實就是這 5 個目錄。
"build:umd": "node build/bin/build-locale.js",
複製代碼
針對 umd
的加載方式,對詞條的多語言進行打包。其實就是把 src/locale/lang
這個目錄下的文件拷貝到 umd
目錄下,不過爲了兼容 umd
的加載方式,就作了一些兼容。
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk"
複製代碼
這個其實就是將 scss
文件編譯成 css
文件 而且跟字體文件一塊兒拷貝到 lib
目錄下的 theme-chalk
目錄下。
這樣子,整個庫的 打包就完成了。
"deploy:extension": "cross-env NODE_ENV=production webpack --config build/webpack.extension.js",
"dev:extension": "rimraf examples/extension/dist && cross-env NODE_ENV=development webpack --watch --config build/webpack.extension.js",
複製代碼
這個是關於組件對應的 chrome 插件。用來配合作主題自定義的。在本文分析中不重要。
"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 狀態,若是狀態是異常的,那麼就繼續,不然就退出sh build/release.sh
這個其實就是將 dev 分支 合併到 master 分支,而後將這個合併的 commit 提交上去,最後提交到 master 分支。node build/bin/gen-indices.js
這個就是替換多語言的文本,主要是替換索引sh build/deploy-faas.sh
更新文檔站點這個在本文分析中,其實也不重要。
接下來咱們試着裝一下這個第三方庫:
npm install element-ui
複製代碼
這個包下載下來是這樣子的, 跟下面完整項目比的話, 大部分的資源仍是在的
可是也能夠看出,這個用來被安裝的分支,其實不是 master 分支的代碼,而是另一個分支的代碼。
具體在項目中引用是這樣的:
import Vue from 'vue'
import Element from 'element-ui'
Vue.use(Element)
// orimport {
Select,
Button
// ...
} from 'element-ui'
Vue.component(Select.name, Select)
Vue.component(Button.name, Button)
複製代碼
這時候代碼引用的 element-ui
其實就是 package.json
中的:
"main": "lib/element-ui.common.js",
"name": "element-ui",
複製代碼
這兩個配置,也就是實際引用的是 element-ui.common.js
這個 js,咱們看下這個 js 打包後是什麼樣子的,應該是能夠導出各類組件的, 看了一下,他的入口仍是原來的 src/index.js
這個文件:
ps: 事實上我後面又從新試了一下,其實引入的並非
element-ui.common.js
這個文件,而是lib/index.js
這個文件,因此仍是經過用umd
的模塊化加載方式去實現組件的按需加載功能。
version: '2.12.0',
locale: lib_locale_default.a.use,
i18n: lib_locale_default.a.i18n,
install: src_install,
CollapseTransition: collapse_transition_default.a,
Loading: packages_loading,
Pagination: packages_pagination,
Dialog: dialog,
Autocomplete: packages_autocomplete,
Dropdown: packages_dropdown,
DropdownMenu: packages_dropdown_menu,
DropdownItem: packages_dropdown_item,
Menu: packages_menu,
Submenu: packages_submenu,
MenuItem: packages_menu_item,
MenuItemGroup: packages_menu_item_group,
Input: packages_input,
InputNumber: packages_input_number,
Radio: packages_radio,
RadioGroup: packages_radio_group,
RadioButton: packages_radio_button,
Checkbox: packages_checkbox,
CheckboxButton: packages_checkbox_button,
CheckboxGroup: packages_checkbox_group,
Switch: packages_switch,
Select: packages_select,
Option: packages_option,
OptionGroup: packages_option_group,
Button: packages_button,
ButtonGroup: packages_button_group,
Table: packages_table,
TableColumn: packages_table_column,
DatePicker: packages_date_picker,
TimeSelect: packages_time_select,
TimePicker: packages_time_picker,
Popover: popover,
Tooltip: packages_tooltip,
MessageBox: message_box,
Breadcrumb: packages_breadcrumb,
BreadcrumbItem: packages_breadcrumb_item,
Form: packages_form,
FormItem: packages_form_item,
Tabs: packages_tabs,
TabPane: packages_tab_pane,
Tag: packages_tag,
Tree: packages_tree,
Alert: packages_alert,
Notification: notification,
Slider: slider,
Icon: packages_icon,
Row: packages_row,
Col: packages_col,
Upload: packages_upload,
Progress: packages_progress,
Spinner: packages_spinner,
Message: packages_message,
Badge: badge,
Card: card,
Rate: rate,
Steps: packages_steps,
Step: packages_step,
Carousel: carousel,
Scrollbar: scrollbar,
CarouselItem: carousel_item,
Collapse: packages_collapse,
CollapseItem: packages_collapse_item,
Cascader: packages_cascader,
ColorPicker: color_picker,
Transfer: transfer,
Container: packages_container,
Header: header,
Aside: aside,
Main: packages_main,
Footer: footer,
Timeline: timeline,
TimelineItem: timeline_item,
Link: packages_link,
Divider: divider,
Image: packages_image,
Calendar: calendar,
Backtop: backtop,
InfiniteScroll: infinite_scroll,
PageHeader: page_header,
CascaderPanel: packages_cascader_panel,
Avatar: avatar,
Drawer: drawer
});
/***/ })
/******/ ])["default"];
複製代碼
若是是直接導出的 Element
,其實就是包含了這些組件的一個大的對象。固然還得導入 css:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
複製代碼
至於爲啥要單獨引用 css, 而不是把 css 也經過 webpack
打到 js 裏面去。這個是由於組件式的開發, 所涉及到的全部的樣式都不會寫在 vue 文件裏面。這個最大的緣由就是後面若是要自定義主題的話,能夠直接引用自定義主題的 css 文件。而不須要管這個默認主題的 css 文件。
以 element-ui
爲例,他全部的組件都是 js 加 vue, 而 vue 文件裏面所有都沒有 scss 的:
他所有的 scss 文件都放在 package/theme-chalk/src
這個下面:
打完包的話,其實也會在 package/theme-chalk/lib
下也會放一份打包後的:
這個操做,實際上是經過這個指令來跑的:
gulp build --gulpfile packages/theme-chalk/gulpfile.js
複製代碼
簡單的來講,就是將 theme-chalk
目錄下 src
下的 scss
文件編譯成 css
, 放到 theme-chalk
目錄下的 lib
目錄,同時將字體文件也一塊兒挪過去。最後再把編譯好的 theme-chalk/lib
目錄 拷貝到 lib/theme-chalk
, 就能夠了
cp-cli packages/theme-chalk/lib lib/theme-chalk
複製代碼
這樣就實現了 scss
的編譯,以及 css
文件的單獨引入機制。 並且從代碼裏面看,這個入口的 scss 文件,其實就是將其餘的 scss
文件所有引入過去:
能夠理解爲這個 index.css
就是總的樣式文件了。 而咱們只須要引入這個 css 就能夠了。 其餘的都不須要引用了。
理解了構建以後,組件的開發反而是比較好理解的。element-ui
每個組件都是一個單獨的目錄,雖然有時候會相互引用,可是邏輯幾乎是互不干擾。遵循必定的目錄結構,以 alert
這個組件爲例,在 packages
目錄的目錄結構是這樣子的:
alert
|---src/
| |---main.vue 組件的業務邏輯
|---index.js 組件的聲明
複製代碼
這個目錄是很清晰的,因此基本上大框架搞懂了,反而組件的開發是顯得比較容易的,由於耦合性很低,不會影響到其餘的組件。 並且從整個構建來看,其實打包出來的都沒有涉及到主題自定義的方面,打包出來的 index.css
就是默認主題, 因此它的主題自定義功能,實際上是經過用戶交互界面來讓用戶本身手動自定義的,這個後面講到air-ui
怎麼作主題自定義的時候,會詳細講,其實並無所謂的優劣,就是使用場景不同,致使的解決方式也不同。
經過對構建的分析,大概就能夠知道 element-ui
的大體脈絡,固然,若是說徹底的瞭解,那確定是不夠,事實上我也沒有把全部的代碼所有啃一遍,一方面是時間上不容許,另外一方面,我在獲得我想要的思路和流程以後,不少東西我都是按照本身的方式和習慣寫的,不必定要跟 element-ui
同樣。我看源碼只是爲了一個思路而已,接下來說一下咱們怎麼基於 element-ui
來作 air-ui
的。
系列文章: