如何在本地开发Angular Schematics

发布时间:2022-02-22 13:46:20 作者:iii
来源:亿速云 阅读:176
# 如何在本地开发Angular Schematics

## 目录
- [1. 理解Angular Schematics](#1-理解angular-schematics)
  - [1.1 什么是Schematics](#11-什么是schematics)
  - [1.2 Schematics的核心概念](#12-schematics的核心概念)
  - [1.3 典型应用场景](#13-典型应用场景)
- [2. 环境准备](#2-环境准备)
  - [2.1 Node.js与npm版本要求](#21-nodejs与npm版本要求)
  - [2.2 全局安装必要工具](#22-全局安装必要工具)
  - [2.3 开发工具推荐](#23-开发工具推荐)
- [3. 创建第一个Schematic项目](#3-创建第一个schematic项目)
  - [3.1 初始化项目结构](#31-初始化项目结构)
  - [3.2 核心文件解析](#32-核心文件解析)
  - [3.3 编译与测试配置](#33-编译与测试配置)
- [4. Schematic开发基础](#4-schematic开发基础)
  - [4.1 Tree结构详解](#41-tree结构详解)
  - [4.2 Rule与Action](#42-rule与action)
  - [4.3 模板文件处理](#43-模板文件处理)
- [5. 高级开发技巧](#5-高级开发技巧)
  - [5.1 用户交互处理](#51-用户交互处理)
  - [5.2 动态文件生成](#52-动态文件生成)
  - [5.3 外部依赖集成](#53-外部依赖集成)
- [6. 调试与测试](#6-调试与测试)
  - [6.1 本地调试方法](#61-本地调试方法)
  - [6.2 单元测试编写](#62-单元测试编写)
  - [6.3 集成测试策略](#63-集成测试策略)
- [7. 发布与分发](#7-发布与分发)
  - [7.1 打包优化](#71-打包优化)
  - [7.2 发布到npm](#72-发布到npm)
  - [7.3 版本管理](#73-版本管理)
- [8. 实战案例](#8-实战案例)
  - [8.1 组件生成器](#81-组件生成器)
  - [8.2 项目脚手架](#82-项目脚手架)
  - [8.3 代码转换器](#83-代码转换器)
- [9. 最佳实践](#9-最佳实践)
  - [9.1 性能优化](#91-性能优化)
  - [9.2 错误处理](#92-错误处理)
  - [9.3 文档编写](#93-文档编写)
- [10. 常见问题解决](#10-常见问题解决)
- [结语](#结语)

## 1. 理解Angular Schematics

### 1.1 什么是Schematics
Angular Schematics是Angular团队开发的强大代码生成工具,它通过定义可重用的代码生成逻辑,帮助开发者自动化重复性开发任务。本质上,它是一个基于模板的代码生成器,但功能远不止于此。

Schematics的核心优势在于:
- **标准化代码结构**:确保团队遵循一致的代码规范
- **提升开发效率**:自动化重复的初始化工作
- **降低错误率**:减少手动操作带来的失误
- **支持复杂转换**:可执行代码重构和迁移操作

### 1.2 Schematics的核心概念
理解这些关键概念对开发至关重要:

1. **Tree**:虚拟文件系统表示,包含源文件结构和要应用的变更
2. **Rule**:接收Tree并返回新Tree的函数,代表一个转换操作
3. **Action**:具体的文件操作类型,如Create、Delete、Rename等
4. **Template**:带有动态插值的文件模板
5. **Collection**:一组相关schematics的集合

### 1.3 典型应用场景
Schematics在Angular生态中有多种应用:

- **项目初始化**:`ng new`命令背后的实现
- **生成组件/服务**:`ng generate`的各种生成器
- **代码迁移**:Angular版本升级时的自动代码转换
- **自定义模板**:企业特定的代码规范和模板
- **开发工作流**:集成CI/CD流程中的自动化步骤

## 2. 环境准备

### 2.1 Node.js与npm版本要求
开发Schematics需要满足以下环境要求:

- Node.js: ^14.15.0 || ^16.10.0 || >=18.0.0
- npm: >=7.0.0 或 yarn: >=1.13.0
- Angular CLI: >=15.0.0

推荐使用nvm管理Node版本:
```bash
nvm install 18
nvm use 18

2.2 全局安装必要工具

安装开发所需的全局依赖:

npm install -g @angular-devkit/schematics-cli @angular/cli

验证安装:

schematics --version
ng version

2.3 开发工具推荐

提高开发效率的工具配置:

  1. VS Code扩展

    • Angular Language Service
    • Schematics Snippets
    • Debugger for Chrome
  2. 调试配置

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Schematic",
      "program": "${workspaceFolder}/node_modules/.bin/schematics",
      "args": [".:my-schematic", "--debug", "false"],
      "cwd": "${workspaceFolder}"
    }
  ]
}

3. 创建第一个Schematic项目

3.1 初始化项目结构

创建新的schematics项目:

schematics blank --name=my-schematics
cd my-schematics
npm install

项目结构说明:

my-schematics/
├── src/
│   ├── collection.json        # Schematic集合声明
│   ├── my-schematic/          # 默认schematic
│   │   ├── files/             # 模板文件目录
│   │   ├── index.ts           # 主逻辑文件
│   │   └── schema.json        # 参数配置
│   └── my-schematic_spec.ts   # 测试文件
├── .npmignore
├── package.json
└── tsconfig.json

3.2 核心文件解析

collection.json - Schematic集合定义:

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

schema.json - 参数配置:

{
  "$schema": "http://json-schema.org/schema",
  "id": "MySchematicSchema",
  "title": "My Schematic Options",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The name of the component",
      "$default": {
        "$source": "argv",
        "index": 0
      }
    }
  },
  "required": ["name"]
}

3.3 编译与测试配置

修改tsconfig.json添加开发配置:

{
  "compilerOptions": {
    "baseUrl": ".",
    "lib": ["es2018", "dom"],
    "module": "commonjs",
    "moduleResolution": "node",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "target": "es6",
    "types": ["jasmine", "node"]
  },
  "include": ["src/**/*"],
  "exclude": ["src/**/*.spec.ts"]
}

添加构建脚本到package.json:

{
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "test": "npm run build && jasmine src/**/*_spec.js",
    "link": "npm run build && cd dist && npm link"
  }
}

4. Schematic开发基础

4.1 Tree结构详解

Tree是Schematics的核心抽象,表示虚拟文件系统:

import { Tree } from '@angular-devkit/schematics';

function mySchematic(options: any): Rule {
  return (tree: Tree) => {
    // 检查文件是否存在
    if (tree.exists('/README.md')) {
      // 读取文件内容
      const content = tree.read('/README.md')!.toString();
      
      // 修改内容
      const newContent = content + '\nModified by schematic';
      
      // 写回文件
      tree.overwrite('/README.md', newContent);
    }
    
    // 创建新文件
    tree.create('/hello.txt', 'Hello World!');
    
    return tree;
  };
}

4.2 Rule与Action

Rule是转换Tree的函数,可以组合多个操作:

import { Rule, apply, mergeWith, template, url } from '@angular-devkit/schematics';

function generateComponent(options: any): Rule {
  return () => {
    // 1. 加载模板源
    const templateSource = url('./files');
    
    // 2. 应用模板参数
    const parameterizedSource = apply(templateSource, [
      template({
        ...options,
        classify: strings.classify,
        dasherize: strings.dasherize
      })
    ]);
    
    // 3. 合并到目标Tree
    return mergeWith(parameterizedSource);
  };
}

4.3 模板文件处理

模板文件使用特殊语法进行插值:

示例模板文件 (files/__name@dasherize__.component.ts):

import { Component } from '@angular/core';

@Component({
  selector: 'app-<%= dasherize(name) %>',
  templateUrl: './<%= dasherize(name) %>.component.html',
  styleUrls: ['./<%= dasherize(name) %>.component.css']
})
export class <%= classify(name) %>Component {
  constructor() {}
}

常用模板工具函数: - classify: 转换为大驼峰 (my-component → MyComponent) - dasherize: 转换为短横线连接 (MyComponent → my-component) - camelize: 转换为小驼峰 (my-component → myComponent)

5. 高级开发技巧

5.1 用户交互处理

使用@angular-devkit/core处理用户输入:

import { strings } from '@angular-devkit/core';
import { prompt } from 'inquirer';

async function interactiveSchematic(options: any): Promise<Rule> {
  const answers = await prompt([
    {
      type: 'input',
      name: 'style',
      message: 'Which stylesheet format would you like to use?',
      choices: ['css', 'scss', 'sass', 'less'],
      default: 'css'
    }
  ]);
  
  return mergeWith(
    apply(url('./files'), [
      template({
        ...options,
        ...answers,
        classify: strings.classify,
        dasherize: strings.dasherize
      })
    ])
  );
}

5.2 动态文件生成

根据条件动态生成不同文件:

function dynamicFiles(options: any): Rule {
  return (tree: Tree) => {
    const templatePath = options.withTests ? 
      './files-with-tests' : 
      './files-basic';
    
    return mergeWith(
      apply(url(templatePath), [
        template({
          ...options,
          classify: strings.classify,
          dasherize: strings.dasherize
        })
      ])
    );
  };
}

5.3 外部依赖集成

在Schematic中使用外部npm包:

  1. 安装依赖:
npm install lodash --save
  1. 在Schematic中使用:
import * as _ from 'lodash';

function useExternalLib(options: any): Rule {
  return (tree: Tree) => {
    const names = _.split(options.name, '-');
    const upperNames = _.map(names, _.upperFirst);
    
    tree.create(
      '/generated.txt', 
      `Processed name: ${upperNames.join(' ')}`
    );
    
    return tree;
  };
}

6. 调试与测试

6.1 本地调试方法

  1. 构建并链接项目:
npm run build
cd dist
npm link
  1. 在测试项目中链接:
npm link my-schematics
ng generate my-schematics:my-schematic --name=test
  1. 使用VS Code调试配置

6.2 单元测试编写

使用Jasmine测试Schematics:

import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { join } from 'path';

describe('My Schematic', () => {
  const collectionPath = join(__dirname, '../collection.json');
  const runner = new SchematicTestRunner('schematics', collectionPath);

  it('should create expected files', async () => {
    const tree = await runner.runSchematicAsync(
      'my-schematic', 
      { name: 'test' }, 
      Tree.empty()
    ).toPromise();
    
    expect(tree.files).toEqual([
      '/test.component.ts',
      '/test.component.html',
      '/test.component.css'
    ]);
  });
});

6.3 集成测试策略

测试完整的Angular项目集成:

import { UnitTestTree } from '@angular-devkit/schematics/testing';
import { getWorkspace } from '@schematics/angular/utility/workspace';

describe('Angular Workspace Test', () => {
  let runner: SchematicTestRunner;
  let appTree: UnitTestTree;

  beforeEach(async () => {
    runner = new SchematicTestRunner('schematics', collectionPath);
    appTree = await runner.runExternalSchematicAsync(
      '@schematics/angular', 
      'workspace', 
      { name: 'workspace', version: '15.0.0' }
    ).toPromise();
    
    appTree = await runner.runExternalSchematicAsync(
      '@schematics/angular',
      'application',
      { name: 'my-app' },
      appTree
    ).toPromise();
  });

  it('should work in Angular workspace', async () => {
    const tree = await runner.runSchematicAsync(
      'my-schematic',
      { name: 'test', project: 'my-app' },
      appTree
    ).toPromise();
    
    const workspace = await getWorkspace(tree);
    const project = workspace.projects.get('my-app');
    // 验证项目配置
  });
});

7. 发布与分发

7.1 打包优化

配置生产构建:

{
  "scripts": {
    "build:prod": "tsc -p tsconfig.json && npm run copy-schemas",
    "copy-schemas": "cpx \"src/**/*.json\" dist"
  }
}

7.2 发布到npm

  1. 登录npm:
npm login
  1. 更新版本号:
npm version patch
# 或 minor/major
  1. 发布:
npm publish --access public

7.3 版本管理

遵循语义化版本控制: - MAJOR: 不兼容的API更改 - MINOR: 向后兼容的功能新增 - PATCH: 向后兼容的问题修复

使用npm标签管理版本:

npm dist-tag add my-schematics@1.0.0 next

8. 实战案例

8.1 组件生成器

创建带storybook的Angular组件:

export function storybookComponent(options: any): Rule {
  return async (tree: Tree) => {
    const angularComponent = await externalSchematic(
      '@schematics/angular',
      'component',
      {
        ...options,
        skipTests: true
      }
    ).toPromise() as Rule;
    
    const storySource = apply(url('./files/story'), [
      template({
        ...options,
        classify: strings.classify,
        dasherize: strings.dasherize
      }),
      move(`/src/app/${dasherize(options.name)}`)
    ]);
    
    return chain([
      angularComponent,
      mergeWith(storySource)
    ]);
  };
}

8.2 项目脚手架

自定义企业级项目模板:

export function enterpriseProject(options: any): Rule {
  return chain([
    // 1. 创建标准Angular项目
    externalSchematic('@schematics/angular', 'ng-new', {
      name: options.name,
      version: '15.0.0',
      routing: true,
      style: 'scss'
    }),
    
    // 2. 添加企业模块
    (_tree: Tree, context: SchematicContext) => {
      return mergeWith(
        apply(url('./files/enterprise'), [
          template({
            ...options,
            classify: strings.classify
          }),
          move(`/${options.name}`)
        ])
      )(context);
    },
    
    // 3. 安装依赖
    (_tree: Tree, context: SchematicContext) => {
      const dependencies = [
        'ngx-translate-core',
        'rxjs-tslint-rules',
        'prettier'
      ];
      
      context.addTask(new NodePackageInstallTask({
        packageName: dependencies.join(' ')
      }));
      
      return tree;
    }
  ]);
}

8.3 代码转换器

Angular版本迁移工具:

”`typescript export function migrateToV15(): Rule { return (tree: Tree) => { // 1. 更新package.json if (tree.exists(‘/package.json’)) { const content = JSON.parse(tree.read(‘/package.json’)!.toString());

  content.dependencies = {
    ...content.dependencies,
    '@angular/core': '^15.0.0',
    '@angular/cli': '^15.0.0'
  };

  tree.overwrite('/package.json', JSON.stringify(content, null, 2));
}
推荐阅读:
  1. 小谈angular ng deploy的实现
  2. angular8和ngrx8结合使用的步骤介绍

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

schematics angular

上一篇:MySQL中如何复制表

下一篇:H5页面中如何使用html5-canvas

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》