angular模块库开发实例
angular模塊庫開發(fā)實(shí)例
隨著前端框架的誕生,也會隨之出現(xiàn)一些組件庫,方便日常業(yè)務(wù)開發(fā)。今天就聊聊angular4組件庫開發(fā)流程。
下圖是button組件的基礎(chǔ)文件。
nk-button.component.ts為該組件的核心文件,看看代碼:
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() {}/***設(shè)置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、Renderer2、ViewEncapsulation做簡要說明:
ElementRef
在應(yīng)用層直接操作 DOM,就會造成應(yīng)用層與渲染層之間強(qiáng)耦合,通過 ElementRef 我們就可以封裝不同平臺下視圖層中的 native 元素 (在瀏覽器環(huán)境中,native 元素通常是指 DOM 元素),最后借助于 Angular 提供的強(qiáng)大的依賴注入特性,我們就可以輕松地訪問到 native 元素。
參考鏈接
Renderer2
渲染器是 Angular 為我們提供的一種內(nèi)置服務(wù),用于執(zhí)行 UI 渲染操作。在瀏覽器中,渲染是將模型映射到視圖的過程。模型的值可以是 JavaScript 中的原始數(shù)據(jù)類型、對象、數(shù)組或其它的數(shù)據(jù)對象。然而視圖可以是頁面中的段落、表單、按鈕等其他元素,這些頁面元素內(nèi)部使用DOM來表示。
參考鏈接
ViewEncapsulation
ViewEncapsulation 允許設(shè)置三個可選的值:
- ViewEncapsulation.Emulated - 無 Shadow DOM,但是通過 Angular 提供的樣式包裝機(jī)制來封裝組件,使得組件的樣式不受外部影響。這是 Angular 的默認(rèn)設(shè)置。
- ViewEncapsulation.Native - 使用原生的 Shadow DOM 特性
- ViewEncapsulation.None - 無 Shadow DOM,并且也無樣式包裝
參考鏈接
button組件創(chuàng)建思路:
- 針對button我們只需修改其樣式,因此在這里創(chuàng)建屬性指令
- 提供屬性接口
- 根據(jù)其傳入的屬性值動態(tài)渲染DOM
至此,最簡單的button就開發(fā)結(jié)束。
模塊打包流程
合并html、css到component文件
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!');})});}/*** 轉(zhuǎn)換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);ts編譯(tsconfig-aot.json)
{"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} }rollup打包 (rollup-config.js)
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 fileswitch (format) {case 'es':input = './publish/src/index.js'file = './publish/esm15/index.js'breakcase 'umd':input = './publish/esm5/index.js'file = './publish/bundles/ng-kylin.umd.js'breakdefault: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, }shell腳本定義執(zhí)行流程(build.sh)
#!/usr/bin/env bashrm -rf ./publishcp -r src/app/components src/temp_componentsnode ./html.merge.jsecho 'Generating entry file using Angular compiler' $(npm bin)/ngc -p tsconfig-aot.json rm -rf src/temp_componentsecho '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.jsonecho 'Transpiling es module to es5' $(npm bin)/tsc --allowJs --importHelpers --target es5 --module es2015 --outDir publish/esm5 publish/esm15/index.jsecho 'Bundling to umd module' export ROLLUP_FORMAT=umd $(npm bin)/rollup -c rollup-config.jsecho 'Minifying umd module' $(npm bin)/uglifyjs publish/bundles/ng-kylin.umd.js --output publish/bundles/ng-kylin.umd.min.jsecho 'Copying package.json' cp package.json publish/package.json至此,項(xiàng)目打包結(jié)束。
源碼
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的angular模块库开发实例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ansible1.7.2源码安装教程
- 下一篇: 利用 :placeholder-show