隨着前端框架的誕生,也會隨之出現一些組件庫,方便平常業務開發。今天就聊聊angular4組件庫開發流程。javascript
下圖是button組件的基礎文件。css
nk-button.component.ts爲該組件的核心文件,看看代碼:html
import {Component, Renderer2, ElementRef, AfterContentInit, ViewEncapsulation, Input} from '@angular/core'; @Component({ selector: '[nk-button]', templateUrl: './nk-button.component.html', encapsulation: ViewEncapsulation.None, styleUrls: ['./nk-button.component.scss'] }) export class NkButtonComponent implements AfterContentInit { _el: HTMLElement; _prefixCls = 'ky-btn'; _type: string; _size: string; _shape: string; _classList: Array<string> = []; @Input() get nkType() { return this._type; } set nkType(value) { this._type = value; this._setClass(); } @Input() get nkSize() { return this._size; } set nkSize(value: string) { this._size = value; this._setClass(); } @Input() get nkShape() { return this._shape; } set nkShape(value: string) { this._shape = value; this._setClass(); } constructor(private _elementRef: ElementRef, private _renderer: Renderer2) { this._el = this._elementRef.nativeElement; this._renderer.addClass(this._el, this._prefixCls); } ngAfterContentInit() { } /** *設置class屬性 */ _setClass(): void { this._classList = [ this.nkType && `${this._prefixCls}-${this.nkType}`, this.nkSize && `${this._prefixCls}-${this.nkSize}`, this.nkShape && `${this._prefixCls}-${this.nkShape}` ].filter(item => { return item; }); this._classList.forEach(_className => { this._renderer.addClass(this._el, _className); }); } }
針對核心概念ElementRef、Renderer二、ViewEncapsulation作簡要說明:前端
在應用層直接操做 DOM,就會形成應用層與渲染層之間強耦合,經過 ElementRef 咱們就能夠封裝不一樣平臺下視圖層中的 native 元素 (在瀏覽器環境中,native 元素一般是指 DOM 元素),最後藉助於 Angular 提供的強大的依賴注入特性,咱們就能夠輕鬆地訪問到 native 元素。java
參考連接node
渲染器是 Angular 爲咱們提供的一種內置服務,用於執行 UI 渲染操做。在瀏覽器中,渲染是將模型映射到視圖的過程。模型的值能夠是 JavaScript 中的原始數據類型、對象、數組或其它的數據對象。然而視圖能夠是頁面中的段落、表單、按鈕等其餘元素,這些頁面元素內部使用DOM來表示。git
參考連接github
ViewEncapsulation 容許設置三個可選的值:shell
參考連接npm
button組件建立思路:
至此,最簡單的button就開發結束。
let fs = require('fs'); let pathUtil = require('path'); let sass = require('node-sass'); let filePath = pathUtil.join(__dirname, 'src', 'temp_components'); let fileArray = []; function fildFile(path) { if (fs.statSync(path).isFile()) { if (/\.component.ts/.test(path)) { fileArray[0] = path; } if (/\.html$/.test(path)) { fileArray[1] = readFile(path) } if (/\.component.scss$/.test(path)) { fileArray[2] = path; } } else if (fs.statSync(path).isDirectory()) { let paths = fs.readdirSync(path); if (fileArray.length === 3) { writeFile(fileArray); fileArray = []; } paths.forEach((p) => { fildFile(pathUtil.join(path, p)); }); } } function readFile(file) { return fs.readFileSync(file); } function writeFile(fileArray) { let file = fileArray[0]; let content = fileArray[1]; let scssPath = fileArray[2]; mergeContent(file, content, scssPath) .then(result => { if (!result) return; fs.writeFile(file, result, function (err) { if (err) console.error(err); console.log('file merge success!'); }) }); } /** * 轉換scss * @param path * @returns {Promise} */ function processScss(path) { return new Promise((resolve, reject) => { sass.render({ file: path }, (err, result) => { if (!err) { resolve(result.css.toString()) } else { reject(err); } }) }) } function mergeContent(file, content, scssPath) { let componentContent = readFile(file); let htmlRegex = /(templateUrl *:\s*[\"|\'])(.*[\"|\']\,?)/g; let scssRegex = /(styleUrls *:\s*)(\[.*\]\,?)/g; let newContent = ''; if (htmlRegex.test(componentContent) && scssRegex.test(componentContent)) { let contentArray = componentContent.toString().split(htmlRegex); contentArray[1] = 'template:`'; contentArray[2] = content + '`,'; contentArray.forEach(con => { newContent += con; }) contentArray = newContent.toString().split(scssRegex); return new Promise((resolve, reject) => { processScss(scssPath) .then(result => { newContent = ''; contentArray[1] = 'styles:[`'; contentArray[2] = result + '`],'; contentArray.forEach(con => { newContent += con; }) resolve(newContent) }, err => { reject(err); }) }); } } fildFile(filePath);
{ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./publish/src", "baseUrl": "./", "declaration": true, "importHelpers": true, "module": "es2015", "sourceMap": false, "target": "es2015", "types": [ "node" ] }, "files": [ "./src/temp_components/ng-kylin.module.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, "strictMetadataEmit": true, "flatModuleOutFile": "index.js", "flatModuleId": "ng-kylin", "skipTemplateCodegen": true } }
import resolve from 'rollup-plugin-node-resolve' import replace from 'rollup-plugin-replace' const format = process.env.ROLLUP_FORMAT || 'es' let globals = { '@angular/animations': 'ng.animations', '@angular/cdk': 'ng.cdk', '@angular/core': 'ng.core', '@angular/common': 'ng.common', '@angular/compiler': 'ng.compiler', '@angular/forms': 'ng.forms', '@angular/platform-browser': 'ng.platformBrowser', 'moment': 'moment', 'moment/locale/zh-cn': null, 'rxjs/BehaviorSubject': 'Rx', 'rxjs/Observable': 'Rx', 'rxjs/Subject': 'Rx', 'rxjs/Subscription': 'Rx', 'rxjs/observable/fromPromise': 'Rx.Observable', 'rxjs/observable/forkJoin': 'Rx.Observable', 'rxjs/observable/fromEvent': 'Rx.Observable', 'rxjs/observable/merge': 'Rx.Observable', 'rxjs/observable/of': 'Rx.Observable', 'rxjs/operator/auditTime': 'Rx.Observable.prototype', 'rxjs/operator/catch': 'Rx.Observable.prototype', 'rxjs/operator/debounceTime': 'Rx.Observable.prototype', 'rxjs/operator/distinctUntilChanged': 'Rx.Observable.prototype', 'rxjs/operator/do': 'Rx.Observable.prototype', 'rxjs/operator/filter': 'Rx.Observable.prototype', 'rxjs/operator/finally': 'Rx.Observable.prototype', 'rxjs/operator/first': 'Rx.Observable.prototype', 'rxjs/operator/map': 'Rx.Observable.prototype', 'rxjs/operator/pluck': 'Rx.Observable.prototype', 'rxjs/operator/startWith': 'Rx.Observable.prototype', 'rxjs/operator/switchMap': 'Rx.Observable.prototype', 'rxjs/operator/takeUntil': 'Rx.Observable.prototype', 'rxjs/operator/throttleTime': 'Rx.Observable.prototype', } if (format === 'es') { globals = Object.assign(globals, { 'tslib': 'tslib', }) } let input let file switch (format) { case 'es': input = './publish/src/index.js' file = './publish/esm15/index.js' break case 'umd': input = './publish/esm5/index.js' file = './publish/bundles/ng-kylin.umd.js' break default: throw new Error(`format ${format} is not supported`) } export default { input, output: { file, format, }, exports: 'named', name: 'ngKylin', plugins: [replace({ "import * as moment": "import moment" }), resolve()], external: Object.keys(globals), globals, }
#!/usr/bin/env bash rm -rf ./publish cp -r src/app/components src/temp_components node ./html.merge.js echo 'Generating entry file using Angular compiler' $(npm bin)/ngc -p tsconfig-aot.json rm -rf src/temp_components echo 'Bundling to es module' export ROLLUP_FORMAT=es $(npm bin)/rollup -c rollup-config.js rm -rf publish/src/*.js rm -rf publish/src/**/*.js sed -e "s/from '.\//from '.\/src\//g" publish/src/index.d.ts > publish/index.d.ts sed -e "s/\":\".\//\":\".\/src\//g" publish/src/index.metadata.json > publish/index.metadata.json rm publish/src/index.d.ts publish/src/index.metadata.json echo 'Transpiling es module to es5' $(npm bin)/tsc --allowJs --importHelpers --target es5 --module es2015 --outDir publish/esm5 publish/esm15/index.js echo 'Bundling to umd module' export ROLLUP_FORMAT=umd $(npm bin)/rollup -c rollup-config.js echo 'Minifying umd module' $(npm bin)/uglifyjs publish/bundles/ng-kylin.umd.js --output publish/bundles/ng-kylin.umd.min.js echo 'Copying package.json' cp package.json publish/package.json
至此,項目打包結束。