build:file
腳本的執行目的是生成包括 icon
, 入口文件
, i18n 國際化
, version
在內的文件, 內容爲:css
node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js
複製代碼
執行這個腳本的做用是:讀取 packages/theme-chalk/src/icon.scss
文件, 對文件中的全部相似於 el-icon-close
這樣的圖標類名進行正則匹配,把全部符合正則的圖標類名組成一個圖標數組,最後把圖標數組寫入到 example/icon.json
。html
postcssvue
PostCSS 是一個容許使用 JS
插件轉換樣式的工具。 PostCSS
接受一個 CSS
文件並提供了一個 API 來分析、修改它的規則(經過把 CSS
轉換成一個 AST
抽象語法樹的方式。 PostCSS 更多說明, PostCSS API)。node
postcss.parsegit
解析一個 css
文件, 返回文件中所包含的 css
節點。github
<!-- icon.scss -->
.el-icon-info:before { content: "\e61a"; }
複製代碼
const postcss = require('postcss')
const fs = require('fs')
const fontFile = fs.readFileSync('./testIcon.scss', 'utf-8')
const nodes = postcss.parse(fontFile).nodes
console.log(nodes)
複製代碼
打印出來的結果爲:正則表達式
[
Rule {
raws: {
before: '\r\n\r\n',
between: ' ',
semicolon: true,
after: '\r\n'
},
type: 'rule',
nodes:[...],
parent:
Root {
raws: [Object],
type: 'root',
nodes: [Circular],
source: [Object]
},
source: {
start: [Object],
input: [Object],
end: [Object]
},
selector: '[class^="el-icon-"], [class*=" el-icon-"]'
}
]
複製代碼
對上面的前提知識瞭解後,開始看執行的腳本代碼邏輯json
var postcss = require('postcss');
var fs = require('fs');
var path = require('path');
var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/src/icon.scss'), 'utf8');
var nodes = postcss.parse(fontFile).nodes;
var classList = [];
nodes.forEach((node) => {
var selector = node.selector || '';
var reg = new RegExp(/\.el-icon-([^:]+):before/);
var arr = selector.match(reg);
if (arr && arr[1]) {
classList.push(arr[1]);
}
});
fs.writeFile(path.resolve(__dirname, '../../examples/icon.json'), JSON.stringify(classList), () => {});
複製代碼
循環中的邏輯:api
forEach
遍歷取到的全部 css
文件節點。選擇器
屬性(包含 class
類、 id
) selector
。/\.el-icon-([^:]+):before/
匹配全部知足 Element iocn
命名規則的 selector
。 這裏使用 字符串的 match
方法。它將返回全部匹配項。match
匹配不到結果時返回 null
),而且匹配的第一個結果也爲真時。 爲了不重複 只將第一個匹配結果放在 classList
數組中。最後將 生成的 classList
以字符串的形式寫入到 examples/icon.js
數組
執行這個文件的目的是自動生成 整個 Elemnet
框架的入口文件。 這個入口文件須要暴露 一個 默認對象, 這個對象上包括 install
方法, install
方法中須要在傳入的 Vue
參數上添加 所有的組件、指令、全局掛載的方法。除了 install
方法外,爲了支持單組件的使用, 還須要在這個默認對象上面添加 所有的組件做爲該對象的屬性。( 關於 vue
插件的開發 , 和 Element
入口文件的分析)。
json-templater/String
一種模板語言實現。能夠預先寫一個字符串模板,在這個字符串模板中能夠存在 以 {{var}}
包裹的變量, 使用該方法能夠將字符串中的變量替換爲其餘值。
var render = require('json-templater/string');
let template = `A {{platform}} UI Library `
render(template, { platform: 'Desktop'});
複製代碼
uppercamelcase
將給定字符串轉換成 駝峯寫法
os.EOL
一個字符串常量,定義操做系統相關的行末標誌:
components.json
以每個組件名爲對象的 key
, 以該組件的所在目錄爲 value
組成的對象。
"pagination": "./packages/pagination/index.js",
"dialog": "./packages/dialog/index.js",
"autocomplete": "./packages/autocomplete/index.js",
"dropdown": "./packages/dropdown/index.js",
"dropdown-menu": "./packages/dropdown-menu/index.js",
......
複製代碼
// 組件-組件地址 json 對象
var Components = require('../../components.json');
var fs = require('fs');
// 替換 json 模板中的變量的方法
var render = require('json-templater/string');
// 字符串轉換爲 駝峯寫法
var uppercamelcase = require('uppercamelcase');
var path = require('path');
// 當前操做系統的 換行符
var endOfLine = require('os').EOL;
// 將字符串文件模板輸出的文件地址, 這個地址就是項目的入口文件的地址
var OUTPUT_PATH = path.join(__dirname, '../../src/index.js');
// 文件模板頭部的 import 引用 字符串模板
var IMPORT_TEMPLATE = 'import {{name}} from \'../packages/{{package}}/index.js\';';
// 文件模板中的 component 的字符串模板
var INSTALL_COMPONENT_TEMPLATE = ' {{name}}';
// 整個文件模板中 主體字符串模板 裏面有四個替代變量,分別是 include、 install、version、list
var MAIN_TEMPLATE = `......`
delete Components.font;
var ComponentNames = Object.keys(Components);
// 替換 include 的變量數組
var includeComponentTemplate = [];
// 替換 install 的變量數組
var installTemplate = [];
// 替換 list 的變量數組
var listTemplate = [];
// 循環 component.json 中的 key 組成的對象
ComponentNames.forEach(name => {
// 組件名 轉換爲 駝峯寫法
var componentName = uppercamelcase(name);
// include 變量數組中添加 變量替換後的 IMPORT_TEMPLATE 字符串
includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
name: componentName,
package: name
}));
// install 變量數組中添加 變量替換後的 INSTALL_COMPONENT_TEMPLATE 字符串 這裏排除 'Loading', 'MessageBox', 'Notification', 'Message' 是由於這幾個組件將會全局掛載到 Vue 的實例上。
if (['Loading', 'MessageBox', 'Notification', 'Message'].indexOf(componentName) === -1) {
installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
name: componentName,
component: name
}));
}
// list 的變量數組 中添加 組件的 key 的駝峯寫法的字符串
if (componentName !== 'Loading') listTemplate.push(` ${componentName}`);
})
// 最終將 MAIN_TEMPLATE 模板中的變量進行替換,生成最終的 框架入口文件的 字符串模板
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)
});
// 將生成的 字符串模板寫入到 框架入口文件
fs.writeFileSync(OUTPUT_PATH, template);
複製代碼
關於
MAIN_TEMPLATE
字符串模板的寫法分析,能夠看 Element UI 項目分析
這個 文件中是經過循環已經配置好的 i18n
形式的數據字典, 對每個數據字典中語種對象都生成一個 語種目錄,在每個目錄中, 根據 模板引擎 生成 不一樣語種對應的模板。 以後 根據數據字典中的 pages
屬性裏面的配置,替換掉模板中的變量。 最後寫入到 example/pages/
。 這樣每個語種都有一套對應的 .vue
代碼。
page.json
這個文件裏面配置了 i18n
的數據字典數組。
[
{
"lang": "zh-CN",
"pages": {
"index": { },
"component": {},
"changelog": {},
"design": {},
"guide": {},
"nav": {},
"resource": {}
}
}
...
]
複製代碼
var fs = require('fs');
var path = require('path');
var langConfig = require('../../examples/i18n/page.json');
langConfig.forEach(lang => {
// 在../../examples/pages 文件夾下 讀取 與 `lang.lang` (指 zh-CN、 en-US、 es ) 對應的文件信息, 若是拋出異常,說明該文件夾下沒有改文件,就新建一個對應 `lang.lang` 的文件
try {
fs.statSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
} catch (e) {
fs.mkdirSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
}
// pages 裏面包含 index component changelog design guide nav resource
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]; // 就是本次循環裏的 page
// 將 page 再次遍歷, 並將 與 page 變量相對應的 模板中的 變量 替換成 page 對象中的 value
Object.keys(pairs).forEach(key => {
content = content.replace(new RegExp(`<%=\\s*${ key }\\s*>`, 'g'), pairs[key]);
});
// 最終將 替換後的 模板寫入到 輸入地址
fs.writeFileSync(outputPath, content);
});
});
複製代碼
這個文件是根據 命令行參數 process.env.VERSION
或者 package.json
中的 version
的值, 來生成 框架的版本對象,並最終在 examples
下生成 version.json
文件
var fs = require('fs');
var path = require('path');
var version = process.env.VERSION || require('../../package.json').version;
var content = { '1.4.13': '1.4', '2.0.11': '2.0', '2.1.0': '2.1', '2.2.2': '2.2', '2.3.9': '2.3' };
if (!content[version]) content[version] = '2.4';
fs.writeFileSync(path.resolve(__dirname, '../../examples/versions.json'), JSON.stringify(content));
複製代碼