共计 7207 个字符,预计需要花费 19 分钟才能阅读完成。
随着前端框架的诞生,也会随之呈现一些组件库,不便日常业务开发。明天就聊聊 angular4 组件库开发流程。
下图是 button 组件的根底文件。
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() {} | |
/** | |
* 设置 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
在应用层间接操作 DOM,就会造成应用层与渲染层之间强耦合,通过 ElementRef 咱们就能够封装不同平台下视图层中的 native 元素 (在浏览器环境中,native 元素通常是指 DOM 元素),最初借助于 Angular 提供的弱小的依赖注入个性,咱们就能够轻松地拜访到 native 元素。
参考链接
Renderer2
渲染器是 Angular 为咱们提供的一种内置服务,用于执行 UI 渲染操作。在浏览器中,渲染是将模型映射到视图的过程。模型的值能够是 JavaScript 中的原始数据类型、对象、数组或其它的数据对象。然而视图能够是页面中的段落、表单、按钮等其余元素,这些页面元素外部应用 DOM 来示意。
参考链接
ViewEncapsulation
ViewEncapsulation 容许设置三个可选的值:
- ViewEncapsulation.Emulated – 无 Shadow DOM,然而通过 Angular 提供的款式包装机制来封装组件,使得组件的款式不受内部影响。这是 Angular 的默认设置。
- ViewEncapsulation.Native – 应用原生的 Shadow DOM 个性
- ViewEncapsulation.None – 无 Shadow DOM,并且也无款式包装
参考链接
button 组件创立思路:
- 针对 button 咱们只需批改其款式,因而在这里创立属性指令
- 提供属性接口
- 依据其传入的属性值动静渲染 DOM
至此,最简略的 button 就开发完结。
模块打包流程
合并 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!"); | |
}); | |
}); | |
} | |
/** | |
* 转换 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 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, | |
}; |
shell 脚本定义执行流程 (build.sh)
#!/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 |
至此,我的项目打包完结。
正文完
发表至: javascript
2020-09-17