最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Angular Schematics在DevUI Admin中的实践

    正文概述 掘金(DevUI团队)   2021-06-15   606

    DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸灵活至简的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠、取悦眼球的设计。如果你正在开发 ToB工具类产品,DevUI 将是一个很不错的选择!

    Angular Schematics在DevUI Admin中的实践

    引言

    Angular Schematics 是基于模板(Template-based)的,Angular 特有的代码生成器,当然它不仅仅是生成代码,也可以修改我们的代码,它使得我们可以基于 Angular CLI 去实现我们自己的一些自动化操作。

    相信在平时开发 Angular 项目的同时,大家都用过 ng generate component component-name, ng add @angular/materials, ng generate module module-name,这些都是 Angular 中已经为我们实现的一些 CLI,那么我们应该如何在自己的项目中去实现基于自己项目的 CLI 呢?本文将会基于我们在 ng-devui-admin 中的实践来进行介绍。欢迎大家持续的关注,后续我们将会推出更加丰富的 CLI 帮助大家更快搭建一个 Admin 页面。

    如何在本地开发你的 Angular Schematics

    在本地开发你需要先安装 schematics 脚手架

    npm install -g @angular-devkit/schematics-cli
    
    # 安装完成之后新建一个schematics项目
    schematics blank --name=your-schematics
    

    新建项目之后你会看到如下目录结构,代表你已经成功创建一个 shematics 项目。

    Angular Schematics在DevUI Admin中的实践

    重要文件介绍

    • tsconfig.json: 主要与项目打包编译相关,在这不做具体介绍

    • collection.json:与你的 CLI 命令相关,用于定义你的相关命令

    {
      "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
      "schematics": {
        "first-schematics": {
          "description": "A blank schematic.",
          "factory": "./first-schematics/index#firstSchematics"
        }
      }
    }
    

    first-schematics: 命令的名字,可以在项目中通过 ng g first-schematics:first-schematics 来运行该命令。 description: 对该条命令的描述。 factory: 命令执行的入口函数 通常还会有另外一个属性 schema,我们将在后面进行讲解。

    • index.ts:在该文件中实现你命令的相关逻辑
    import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
    
    export function firstSchematics(_options: any): Rule {
      return (tree: Tree, _context: SchematicContext) => {
        return tree;
      };
    }
    

    在这里我们先看几个需要了解的参数: tree:在这里你可以将 tree 理解为我们整个的 angular 项目,你可以通过 tree 新增文件,修改文件,以及删除文件。 _context:该参数为 schematics 运行的上下文,比如你可以通过 context 执行 npm installRule:为我们制定的操作逻辑。

    实现一个 ng-add 指令

    现在我们通过实现一个 ng-add 指令来更好的熟悉。

    同样是基于以上我们已经创建好的项目。

    新建命令相关的文件

    首先我们在 src 目录下新建一个目录 ng-add,然后在该目录下添加三个文件 index.ts, schema.json, schema.ts,之后你的目录结构应该如下:

    Angular Schematics在DevUI Admin中的实践

    配置 collection.json

    之后我们在 collection.json 中配置该条命令:

    {
      "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
      "schematics": {
        ...,
        "ng-add": {
          "factory": "./ng-add/index",
          "description": "Some description about your schematics",
          "schema": "./ng-add/schema.json"
        }
      }
    }
    

    files 目录中加入我们想要插入的文件

    关于 template 的语法可以参考 ejs 语法

    app.component.html.template

    <div class="my-app">
      <% if (defaultLanguage === 'zh-cn') { %>你好,Angular Schematics!<% } else { %>Hello, My First Angular Schematics!<% } %>
      <h1>{{ title }}</h1>
    </div>
    

    app.component.scss.template

    .app {
      display: flex;
      justify-content: center;
      align-item: center;
    }
    

    app.component.ts.template

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    export class AppComponent {
      title = <% if (defaultLanguage === 'zh-cn') { %>'你好'<% } else { %>'Hello'<% } %>;
    }
    

    开始实现命令逻辑

    • schema.json:在该文件中定义与用户的交互
    {
      "$schema": "http://json-schema.org/schema",
      "id": "SchematicsDevUI",
      "title": "DevUI Options Schema",
      "type": "object",
      "properties": {
        "defaultLanguage": {
          "type": "string",
          "description": "Choose the default language",
          "default": "zh-cn",
          "x-prompt": {
            "message": "Please choose the default language you want to use: ",
            "type": "list",
            "items": [
              {
                "value": "zh-cn",
                "label": "简体中文 (zh-ch)"
              },
              {
                "value": "en-us",
                "label": "English (en-us)"
              }
            ]
          }
        },
        "i18n": {
          "type": "boolean",
          "default": true,
          "description": "Config i18n for the project",
          "x-prompt": "Would you like to add i18n? (default: Y)"
        }
      },
      "required": []
    }
    

    在以上的定义中,我们的命令将会接收两个参数分别为 defaultLanguagei18n,我们以 defaultLanguage 为例讲解对参数的相关配置:

    {
      "defaultLanguage": {
        "type": "string",
        "description": "Choose the default language",
        "default": "zh-cn",
        "x-prompt": {
          "message": "Please choose the default language you want to use: ",
          "type": "list",
          "items": [
            {
              "value": "zh-cn",
              "label": "简体中文 (zh-ch)"
            },
            {
              "value": "en-us",
              "label": "English (en-us)"
            }
          ]
        }
      }
    }
    

    type 代表该参数的类型是 stringdefault 为该参数的默认值为 zh-cnx-prompt 定义与用户的交互,message 为我们对用户进行的相关提问,在这里我们的 typelist 代表我们会为用户提供 items 中列出的选项供用户进行选择。

    • schema.ts:在该文件中定义我们接收到的参数类型
    export interface Schema {
      defaultLanguage: string;
      i18n: boolean;
    }
    
    • index.ts:在该文件中实现我们的操作逻辑,假设在此次 ng-add 操作中,我们根据用户输入的 defaultLanguage, i18n 来对用户的项目进行相应的更改,并且插入相关的 npm 包,再进行安装。
    import {
      apply,
      applyTemplates,
      chain,
      mergeWith,
      move,
      Rule,
      SchematicContext,
      SchematicsException,
      Tree,
      url
    } from '@angular-devkit/schematics';
    import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
    import { Schema as AddOptions } from './schema';
    
    let projectWorkspace: {
      root: string;
      sourceRoot: string;
      defaultProject: string;
    };
    
    export type packgeType = 'dependencies' | 'devDependencies' | 'scripts';
    export const PACKAGES_I18N = [
      '@devui-design/icons@^1.2.0',
      '@ngx-translate/core@^13.0.0',
      '@ngx-translate/http-loader@^6.0.0',
      'ng-devui@^11.1.0'
    ];
    export const PACKAGES = ['@devui-design/icons@^1.2.0', 'ng-devui@^11.1.0'];
    export const PACKAGE_JSON_PATH = 'package.json';
    export const ANGULAR_JSON_PATH = 'angular.json';
    
    export default function (options: AddOptions): Rule {
      return (tree: Tree, context: SchematicContext) => {
        // 获取项目空间中我们需要的相关变量
        getWorkSpace(tree);
    
        // 根据是否选择i18n插入不同的packages
        const packages = options.i18n ? PACKAGES_I18N : PACKAGES;
        addPackage(tree, packages, 'dependencies');
    
        // 执行 npm install
        context.addTask(new NodePackageInstallTask());
    
        // 自定义的一系列 Rules
        return chain([removeOriginalFiles(), addSourceFiles(options)]);
      };
    }
    

    下面时使用到的函数的具体实现:

    // getWorkSpace
    function getWorkSpace(tree: Tree) {
      let angularJSON;
      let buffer = tree.read(ANGULAR_JSON_PATH);
      if (buffer) {
        angularJSON = JSON.parse(buffer.toString());
      } else {
        throw new SchematicsException(
          'Please make sure the project is an Angular project.'
        );
      }
    
      let defaultProject = angularJSON.defaultProject;
      projectWorkspace = {
        root: '/',
        sourceRoot: angularJSON.projects[defaultProject].sourceRoot,
        defaultProject
      };
    
      return projectWorkspace;
    }
    
    // removeOriginalFiles
    // 根据自己的需要选择需要删除的文件
    function removeOriginalFiles() {
      return (tree: Tree) => {
        [
          `${projectWorkspace.sourceRoot}/app/app.component.ts`,
          `${projectWorkspace.sourceRoot}/app/app.component.html`,
          `${projectWorkspace.sourceRoot}/app/app.component.scss`,
          `${projectWorkspace.sourceRoot}/app/app.component.css`
        ]
          .filter((f) => tree.exists(f))
          .forEach((f) => tree.delete(f));
      };
    }
    

    将 files 下的文件拷贝到指定的路径下,关于 chain, mergeWith, apply, template 的详细使用方法可以参考 Schematics

    // addSourceFiles
    function addSourceFiles(options: AddOptions): Rule {
      return chain([
        mergeWith(
          apply(url('./files'), [
            applyTemplates({
              defaultLanguage: options.defaultLanguage
            }),
            move(`${projectWorkspace.sourceRoot}/app`)
          ])
        )
      ]);
    }
    
    // readJson
    function readJson(tree: Tree, file: string, type?: string): any {
      if (!tree.exists(file)) {
        return null;
      }
    
      const sourceFile = tree.read(file)!.toString('utf-8');
      try {
        const json = JSON.parse(sourceFile);
        if (type && !json[type]) {
          json[type] = {};
        }
        return json;
      } catch (error) {
        console.log(`Failed when parsing file ${file}.`);
        throw error;
      }
    }
    
    // writeJson
    export function writeJson(tree: Tree, file: string, source: any): void {
      tree.overwrite(file, JSON.stringify(source, null, 2));
    }
    
    // readPackageJson
    function readPackageJson(tree: Tree, type?: string): any {
      return readJson(tree, PACKAGE_JSON_PATH, type);
    }
    
    // writePackageJson
    function writePackageJson(tree: Tree, json: any): any {
      return writeJson(tree, PACKAGE_JSON_PATH, json);
    }
    
    // addPackage
    function addPackage(
      tree: Tree,
      packages: string | string[],
      type: packgeType = 'dependencies'
    ): Tree {
      const packageJson = readPackageJson(tree, type);
    
      if (packageJson == null) {
        return tree;
      }
    
      if (!Array.isArray(packages)) {
        packages = [packages];
      }
      packages.forEach((pck) => {
        const splitPosition = pck.lastIndexOf('@');
        packageJson[type][pck.substr(0, splitPosition)] = pck.substr(
          splitPosition + 1
        );
      });
    
      writePackageJson(tree, packageJson);
      return tree;
    }
    

    为了保持 index.ts 文件的简洁,可以将相关操作的方法抽取到一个新的文件中进行引用。

    测试 ng-add

    至此我们已经完成了 ng-add 命令,现在我们对该命令进行测试:

    • ng new test 初始化一个 Angular 项目
    • cd test && mkdir libs 在项目中添加一个 libs 文件夹,将图中标蓝的文件拷贝到其中

    Angular Schematics在DevUI Admin中的实践

    • 之后在命令行中执行 npm link libs/
    • link 完成之后 cd libs && npm run build && cd ..
    • 现在执行 ng add first-schematics 之后会看到如下提示

    Angular Schematics在DevUI Admin中的实践

    • 最后我们通过 npm start 来查看执行的结果如下

    Angular Schematics在DevUI Admin中的实践

    结语

    综上简单介绍了一个 Schematics 的实现,更多的一些应用欢迎大家查看 ng-devui-admin 中的实现。

    欢迎加DevUI小助手微信:devui-official,一起讨论Angular技术和前端技术。

    欢迎关注我们DevUI组件库,点亮我们的小星星?:

    github.com/devcloudfe/…

    也欢迎使用DevUI新发布的DevUI Admin系统,开箱即用,10分钟搭建一个美观大气的后台管理系统!

    加入我们

    我们是DevUI团队,欢迎来这里和我们一起打造优雅高效的人机设计/研发体系。招聘邮箱:muyang2@huawei.com。

    文/DevUI Kagol

    往期文章推荐

    《号外号外!DevUI Admin V1.0 发布啦!》

    Monorepo初体验:将现有的NG CLI工程改造成Monorepo方式

    《今天是儿童节,整个贪吃蛇到编辑器里玩儿吧》

    《如何将龙插入到编辑器中?》

    《Quill富文本编辑器的实践》


    起源地下载网 » Angular Schematics在DevUI Admin中的实践

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元