0

0

什么是Angular Schematics?如何搭建?(详解)

青灯夜游

青灯夜游

发布时间:2022-02-22 11:20:50

|

2965人浏览过

|

来源于掘金社区

转载

什么是angular schematics?如何在本地开发你的 angular schematics?下面本篇文章就来给大家详细介绍一下,并通过一个例子来更好的熟悉,希望对大家有所帮助!

什么是Angular Schematics?如何搭建?(详解)

什么是Angular Schematics?

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

相信在平时开发 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 项目。

1.png

重要文件介绍

  • 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,之后你的目录结构应该如下:

2.png

配置 <span style="font-size: 18px;">collection.json</span>

之后我们在 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"
    }
  }
}

<span style="font-size: 18px;">files</span> 目录中加入我们想要插入的文件

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

云从科技AI开放平台
云从科技AI开放平台

云从AI开放平台

下载

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 文件的简洁,可以将相关操作的方法抽取到一个新的文件中进行引用。

测试 <span style="font-size: 18px;">ng-add</span>

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

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

3.png

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

4.png

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

5.png

结语

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

更多编程相关知识,请访问:编程学习!!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

1

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

41

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

171

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

50

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

90

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

226

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

528

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue.js:纪录片
Vue.js:纪录片

共1课时 | 0.2万人学习

Angular js入门篇
Angular js入门篇

共17课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号