资讯专栏INFORMATION COLUMN

angular模块库开发实例

JerryZou / 2915人阅读

摘要:模块库开发实例随着前端框架的诞生,也会随之出现一些组件库,方便日常业务开发。在浏览器中,渲染是将模型映射到视图的过程。然而视图可以是页面中的段落表单按钮等其他元素,这些页面元素内部使用来表示。

angular模块库开发实例

随着前端框架的诞生,也会随之出现一些组件库,方便日常业务开发。今天就聊聊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 = [];

  @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

至此,项目打包结束。

源码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89156.html

相关文章

  • angular - 收藏集 - 掘金

    摘要:如何在中使用动画前端掘金本文讲一下中动画应用的部分。与的快速入门指南推荐前端掘金是非常棒的框架,能够创建功能强大,动态功能的。自发布以来,已经广泛应用于开发中。 如何在 Angular 中使用动画 - 前端 - 掘金本文讲一下Angular中动画应用的部分。 首先,Angular本生不提供动画机制,需要在项目中加入Angular插件模块ngAnimate才能完成Angular的动画机制...

    AlexTuan 评论0 收藏0
  • Angular(01)-- 架构概览

    摘要:正文架构概览正文架构概览接触大概一个月吧,期间写了个项目,趁现在稍微有点时间,来回顾梳理一下。里的模块,并不等同于项目中的模块概念。当然,这只是我目前阶段的理解。声明 本系列文章内容梳理自以下来源: Angular 官方中文版教程 官方的教程,其实已经很详细且易懂,这里再次梳理的目的在于复习和巩固相关知识点,刚开始接触学习 Angular 的还是建议以官网为主。 因为这系列文章,更多的会...

    bitkylin 评论0 收藏0
  • 前端每周清单半年盘点之 Angular

    摘要:延伸阅读学习与实践资料索引与前端工程化实践前端每周清单半年盘点之篇前端每周清单半年盘点之与篇前端每周清单半年盘点之篇 前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源项目、巅峰人生等栏目。欢迎关注【前端之巅】微信公众号(ID:frontshow),及时获取前端每周清单;本文则是对于半年来发布的前端每周清单...

    LeviDing 评论0 收藏0
  • 从安装认识Angular 2

    摘要:首先,要确认安装了,并且创建了目录并执行初始化。想必看见上面的那么多包会一脸懵逼,没关系,我第一眼看见这些的那刻,和你现在的表情一样,下面在适当的时候我会逐个解释的,你只需要相信我上面的包都是跑所必须的,缺一不可。 关于介绍,只说一句:Angular 2是一个强大、全面、庞大的MVVM框架。 安装 安装,也算是一个坎,因为你需要安装一大堆东西,却不知道每个东西是做什么的,尽管有Angu...

    xietao3 评论0 收藏0
  • 2017年前端框架、类、工具大比拼

    摘要:相比于开发人员的数量,目前框架类库和工具的数量似乎更多一些。本文将会讨论目前最为流行的客户端框架类库和工具以及它们之间的基本差异。典型的类库包括字符串处理日期元素事件动画网络请求等功能。所以不需要明确的区分类库框架和工具。 相比于JavaScript开发人员的数量,目前JavaScript框架、类库和工具的数量似乎更多一些。截至2017年5月,GitHub上的快速搜索显示,有超过110...

    Alliot 评论0 收藏0

发表评论

0条评论

JerryZou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<