18720358503 在线客服 人才招聘 返回顶部
企业动态 技术分享 行业动态

昆明微信小程序定制_Angular脚手架开发的完成步骤

2021-01-07分享 "> 对不起,没有下一图集了!">
Angular脚手架开发的实现步骤       这篇文章主要介绍了Angular脚手架开发的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前提

先把 这篇文章读一遍,确保了解了collection等基础

antd脚手架

克隆项目

git clone NG-ZORRO/ng-zorro-antd.git

开始

打开项目

在schematics下的collection.json为入口,查看内容

一共定了了4个schematic,每个schema分别指向了各文件夹的子schema.json,factory指向了函数入口,index.ts

ng-add/schema.json

 // 指定schema.json的验证模式
 "$schema": "schema",
 "id": "nz-ng-add",
 "title": "Ant Design of Angular(NG-ZORRO) ng-add schematic",
 "type": "object",
 // 包含的属性
 "properties": {
 "project": {
 "type": "string",
 "description": "Name of the project.",
 "$default": {
 "$source": "projectName"
 // 是否跳过package.json的安装属性
 "skipPackageJson": {
 // 类型为布尔
 "type": "boolean",
 // 默认值为false
 "default": false,
 // 这是个描述,可以看到,如果在ng add ng-zorro-antd时不希望自动安装可以加入--skipPackageJson配置项
 "description": "Do not add ng-zorro-antd dependencies to package.json (e.g., --skipPackageJson)"
 // 开始页面
 "bootPage": {
 // 布尔
 "type": "boolean",
 // 默认为true
 "default": true,
 // 将会被覆盖成antd的图标页
 "description": "Set up boot page."
 // 图标配置
 "dynamicIcon": {
 "type": "boolean",
 "default": false,
 "description": "Whether icon assets should be add.",
 "x-prompt": "Add icon assets [ Detail: components/icon/en ]"
 // 主题配置
 "theme": {
 "type": "boolean",
 "default": false,
 "description": "Whether custom theme file should be set up.",
 "x-prompt": "Set up custom theme file [ Detail: docs/customize-theme/en ]"
 // i18n配置,当你ng add ng-antd-zorro 的时候有没有让你选择这个选项呢 
 "i18n": {
 "type": "string",
 "default": "en_US",
 "enum": [
 "ar_EG",
 "bg_BG",
 "ca_ES",
 "cs_CZ",
 "da_DK",
 "de_DE",
 "el_GR",
 "en_GB",
 "en_US",
 "es_ES",
 "et_EE",
 "fa_IR",
 "fi_FI",
 "fr_BE",
 "fr_FR",
 "is_IS",
 "it_IT",
 "ja_JP",
 "ko_KR",
 "nb_NO",
 "nl_BE",
 "nl_NL",
 "pl_PL",
 "pt_BR",
 "pt_PT",
 "sk_SK",
 "sr_RS",
 "sv_SE",
 "th_TH",
 "tr_TR",
 "ru_RU",
 "uk_UA",
 "vi_VN",
 "zh_CN",
 "zh_TW"
 "description": "add locale code to module (e.g., --locale=en_US)"
 "locale": {
 "type": "string",
 "description": "Add locale code to module (e.g., --locale=en_US)",
 "default": "en_US",
 "x-prompt": {
 "message": "Choose your locale code:",
 "type": "list",
 "items": [
 "en_US",
 "zh_CN",
 "ar_EG",
 "bg_BG",
 "ca_ES",
 "cs_CZ",
 "de_DE",
 "el_GR",
 "en_GB",
 "es_ES",
 "et_EE",
 "fa_IR",
 "fi_FI",
 "fr_BE",
 "fr_FR",
 "is_IS",
 "it_IT",
 "ja_JP",
 "ko_KR",
 "nb_NO",
 "nl_BE",
 "nl_NL",
 "pl_PL",
 "pt_BR",
 "pt_PT",
 "sk_SK",
 "sr_RS",
 "sv_SE",
 "th_TH",
 "tr_TR",
 "ru_RU",
 "uk_UA",
 "vi_VN",
 "zh_TW"
 "gestures": {
 "type": "boolean",
 "default": false,
 "description": "Whether gesture support should be set up."
 "animations": {
 "type": "boolean",
 "default": true,
 "description": "Whether Angular browser animations should be set up."
 "required": []
}

schema.ts

当你进入index.ts时首先看到的是一个带options:Schema的函数,options指向的类型是Schema interface,而这个interface 恰好是schema.json中的properties,也就是cli的传入参数类.

我们可以通过自定义传入参数类来完成我们需要的操作.

export type Locale =
 | 'ar_EG'
 | 'bg_BG'
 | 'ca_ES'
 | 'cs_CZ'
 | 'da_DK'
 | 'de_DE'
 | 'el_GR'
 | 'en_GB'
 | 'en_US'
 | 'es_ES'
 | 'et_EE'
 | 'fa_IR'
 | 'fi_FI'
 | 'fr_BE'
 | 'fr_FR'
 | 'is_IS'
 | 'it_IT'
 | 'ja_JP'
 | 'ko_KR'
 | 'nb_NO'
 | 'nl_BE'
 | 'nl_NL'
 | 'pl_PL'
 | 'pt_BR'
 | 'pt_PT'
 | 'sk_SK'
 | 'sr_RS'
 | 'sv_SE'
 | 'th_TH'
 | 'tr_TR'
 | 'ru_RU'
 | 'uk_UA'
 | 'vi_VN'
 | 'zh_CN'
 | 'zh_TW';
export interface Schema {
 bootPage : boolean;
 /** Name of the project to target. */
 project : string;
 /** Whether to skip package.json install. */
 skipPackageJson : boolean;
 dynamicIcon : boolean;
 theme : boolean;
 gestures : boolean;
 animations : boolean;
 locale : Locale;
 i18n : Locale;
}

ng-add/index.ts

import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask, RunSchematicTask } from '@angular-devkit/schematics/tasks';
import { addPackageToPackageJson } from '../utils/package-config';
import { hammerjsVersion, zorroVersion } from '../utils/version-names';
import { Schema } from './schema';
// factory指向的index.ts必须实现这个函数,一行一行看代码
// 我们的函数是一个更高阶的函数,这意味着它接受或返回一个函数引用。
// 在这种情况下,我们的函数返回一个接受Tree和SchematicContext对象的函数。
// options:Schema上面提到了
export default function(options: Schema): Rule {
// tree:虚拟文件系统:用于更改的暂存区域,包含原始文件系统以及要应用于其的更改列表。
// rule:A Rule是一个将动作应用于Tree给定的函数SchematicContext。
 return (host: Tree, context: SchematicContext) = {
 // 如果需要安装包,也就是--skipPackageJson=false
 if (!options.skipPackageJson) {
 // 调用addPackageToPackageJson,传入,tree文件树,包名,包版本
 addPackageToPackageJson(host, 'ng-zorro-antd', zorroVersion);
 // hmr模式包
 if (options.gestures) {
 addPackageToPackageJson(host, 'hammerjs', hammerjsVersion);

context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]); if (options.bootPage) { context.addTask(new RunSchematicTask('boot-page', options)); }

addPackageToPackageJson

// 看function名字就知道这是下载依赖的函数
// @host:Tree 文件树
// @pkg:string 包名
// @vserion:string 包版本
// @return Tree 返回了一个修改完成后的文件树
export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree {
 // 如果文件树里包含package.json文件
 if (host.exists('package.json')) {
 // 读取package.json的内容用utf-8编码
 const sourceText = host.read('package.json').toString('utf-8');
 // 然后把package.json转化为对象,转为对象,转为对象
 const json = JSON.parse(sourceText);
 // 如果package.json对象里没有dependencies属性
 if (!json.dependencies) {
 // 给package对象加入dependencies属性
 json.dependencies = {};
 // 如果package对象中没有 pkg(包名),也就是说:如果当前项目没有安装antd
 if (!json.dependencies[pkg]) {
 // 那么package的dependencies属性中加入 antd:version
 json.dependencies[pkg] = version;
 // 排个序
 json.dependencies = sortObjectByKeys(json.dependencies);
 // 重写tree下的package.json内容为(刚才不是有package.json对象吗,现在在转回去)
 host.overwrite('package.json', JSON.stringify(json, null, 2));
 // 把操作好的tree返回给上一级函数
 return host;
}

现在在回过头去看 ng-add/index.ts

// 给context对象增加一个安装包的任务,然后拿到了任务id
const installTaskId = context.addTask(new NodePackageInstallTask());
// context增加另一个任务,然后传入了一个RunSchematicTask对象,和一个id集合
 context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]);

RunSchematicTask('ng-add-setup-project')

任务ng-add-setup-project定义在了schematic最外层的collection.json里,记住如下4个schematic,后文不再提及

 "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
 "schematics": {
 "ng-add": {
 "description": "add NG-ZORRO",
 "factory": "./ng-add/index",
 "schema": "./ng-add/schema.json"
 // 在这里
 "ng-add-setup-project": {
 "description": "Sets up the specified project after the ng-add dependencies have been installed.",
 "private": true,
 // 这个任务的函数指向
 "factory": "./ng-add/setup-project/index",
 // 任务配置项
 "schema": "./ng-add/schema.json"
 "boot-page": {
 "description": "Set up boot page",
 "private": true,
 "factory": "./ng-generate/boot-page/index",
 "schema": "./ng-generate/boot-page/schema.json"
 "add-icon-assets": {
 "description": "Add icon assets into CLI config",
 "factory": "./ng-add/setup-project/add-icon-assets#addIconToAssets",
 "schema": "./ng-generate/boot-page/schema.json",
 "aliases": ["fix-icon"]
}

ng-add/setup-project

// 刚才的index一样,实现了一个函数
export default function (options: Schema): Rule {
 // 这里其实就是调用各种函数的一个集合.options是上面的index.ts中传过来的,配置项在上文有提及
 return chain([
 addRequiredModules(options),
 addAnimationsModule(options),
 registerLocale(options),
 addThemeToAppStyles(options),
 options.dynamicIcon addIconToAssets(options) : noop(),
 options.gestures hammerjsImport(options) : noop()
}

addRequiredModules

// 模块字典
const modulesMap = {
 NgZorroAntdModule: 'ng-zorro-antd',
 FormsModule : '@angular/forms',
 HttpClientModule : 'mon/http'
// 加入必须依赖模块
export function addRequiredModules(options: Schema): Rule {
 return (host: Tree) = {
 // 获取tree下的工作目录
 const workspace = getWorkspace(host);
 // 获取项目
 const project = getProjectFromWorkspace(workspace, options.project);
 // 获取app.module的路径
 const appModulePath = getAppModulePath(host, getProjectMainFile(project));
 // 循环字典
 for (const module in modulesMap) {
 // 调用下面的函数,意思就是:给appModule引一些模块,好吧,传入了tree,字典key(模块名称),字典value(模块所在包),project对象,appModule的路径,Schema配置项
 addModuleImportToApptModule(host, module, modulesMap[ module ],
 project, appModulePath, options);
 // 将构建好的tree返回给上层函数
 return host;
function addModuleImportToApptModule(host: Tree, moduleName: string, src: string,
 project: WorkspaceProject, appModulePath: string,
 options: Schema): void {
 // 如果app.module引入了NgZorroAntdModule等字典中的模块
 if (hasNgModuleImport(host, appModulePath, moduleName)) {
 // 来个提示
 console.log(chalk.yellow(`Could not set up "${chalk.blue(moduleName)}" ` +
 `because "${chalk.blue(moduleName)}" is already imported. Please manually ` +
 `check "${chalk.blue(appModulePath)}" file.`));
 return;
 //如果没有引入过就直接引入
 addModuleImportToRootModule(host, moduleName, src, project);
}

addAnimationsModule 内容差不多,略过

registerLocale

不怕多,一点一点看,这里主要做的工作就是i18n本地化啥的

先上一张图片,记得脑子里哦

接下来的函数都是为了做上面这个工作

export function registerLocale(options: Schema): Rule {
 return (host: Tree) = {
 // 获取路径
 const workspace = getWorkspace(host);
 const project = getProjectFromWorkspace(workspace, options.project);
 const appModulePath = getAppModulePath(host, getProjectMainFile(project));
 const moduleSource = getSourceFile(host, appModulePath);
 // 获取add ,en_us啥的就是一个字符串
 const locale = getCompatibleLocal(options);
 // 拿到 zh en这种
 const localePrefix = locale.split('_')[ 0 ];
 // recorder可以理解成?快照,一个目录下多个文件组成的文件快照,re coder
 // 为什么要beginUpdate,实际上我的理解是拿appModulePath文件建立了快照
 // mitUpdate(recorder);才会把快照作出的修改提交到tree上面
 // mit之前你操作的是快照,理解理解
 const recorder = host.beginUpdate(appModulePath);
 // 对快照的操作列表
 // insertImport = import {xxx} from 'xxx'这种
 // 结合代码看一下app.module.ts上面的import内容(上面图片)
 const changes = [
 insertImport(moduleSource, appModulePath, 'NZ_I18N',
 'ng-zorro-antd'),
 insertImport(moduleSource, appModulePath, locale,
 'ng-zorro-antd'),
 insertImport(moduleSource, appModulePath, 'registerLocaleData',
 'mon'),
 insertImport(moduleSource, appModulePath, localePrefix,
 `mon/locales/${localePrefix}`, true),
 registerLocaleData(moduleSource, appModulePath, localePrefix),
 // 这个函数特殊,看下面
 ...insertI18nTokenProvide(moduleSource, appModulePath, locale)
 // 循环变更列表如果是insertChange(import)那么引入
 changes.forEach((change) = {
 if (change instanceof InsertChange) {
 recorder.insertLeft(change.pos, change.toAdd);
 // 提交变更到tree
 mitUpdate(recorder);
 // 返回tree给上一级函数
 return host;
//上面说了,就是那个zh_CN/en_Us
function getCompatibleLocal(options: Schema): string {
 const defaultLocal = 'en_US';
 if (options.locale === options.i18n) {
 return options.locale;
 } else if (options.locale === defaultLocal) {
 console.log();
 console.log(`${chalk.bgYellow('WARN')} ${chalk.cyan('--i18n')} option will be deprecated, ` +
 `use ${chalk.cyan('--locale')} instead`);
 return options.i18n;
 } else {
 return options.locale || defaultLocal;
// 这个函数主要是为了生成调用angular本地化的代码registerLocaleData(zh);
function registerLocaleData(moduleSource: ts.SourceFile, modulePath: string, locale: string): Change {
 if (registerLocaleDataFun.length === 0) {
 // 最核心的要在app.module中加入registerLocaleData(zh);才能把本地化做到angular上面
 urrence(allImports, `\n\nregisterLocaleData(${locale});`,
 modulePath, 0) as InsertChange;

function insertI18nTokenProvide(moduleSource: ts.SourceFile, modulePath: string, locale: string): Change[] { const metadataField = 'providers'; // 获取app.module中NgModule注释的内容 // declarations: [ // AppComponent // ], // imports: [ // BrowserModule, // AppRoutingModule, // NgZorroAntdModule, // FormsModule, // HttpClientModule, // BrowserAnimationsModule // ], // providers: [{ provide: NZ_I18N, useValue: zh_CN }], // bootstrap: [AppComponent] // } const nodes = getDecoratorMetadata(moduleSource, 'NgModule', '@angular/core'); // 生成一个provide到app.module中的ngModule注释中,生成到providers数组中 **的操作**(只是生成一个动作)还没应用到文件上 const addProvide = addSymbolToNgModuleMetadata(moduleSource, modulePath, 'providers', `{ provide: NZ_I18N, useValue: ${locale} }`, null); let node: any = nodes[ 0 ]; // tslint:disable-line:no-any // 然后下面开始做了一堆校验工作 if (!node) { return []; const matchingProperties: ts.ObjectLiteralElement[] = (node as ts.ObjectLiteralExpression).properties .filter(prop = prop.kind === ts.SyntaxKind.PropertyAssignment) .filter((prop: ts.PropertyAssignment) = { const name = prop.name; switch (name.kind) { case ts.SyntaxKind.Identifier: return (name as ts.Identifier).getText(moduleSource) === metadataField; case ts.SyntaxKind.StringLiteral: return (name as ts.StringLiteral).text === metadataField; return false; if (!matchingProperties) { return []; if (matchingProperties.length) { const assignment = matchingProperties[ 0 ] as ts.PropertyAssignment; if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) { return []; const arrLiteral = assignment.initializer as ts.ArrayLiteralExpression; if (arrLiteral.elements.length === 0) { return addProvide; } else { node = arrLiteral.elements.filter(e = e.getText e.getText().includes('NZ_I18N')); if (node.length === 0) { return addProvide; } else { console.log(); console.log(chalk.yellow(`Could not provide the locale token to your app.module file (${chalk.blue(modulePath)}).` + `because there is already a locale token in provides.`)); console.log(chalk.yellow(`Please manually add the following code to your provides:`)); console.log(chalk.cyan(`{ provide: NZ_I18N, useValue: ${locale} }`)); return []; } else { // 如果都没什么大问题,mit然后作出更改动作 return addProvide; }

参考文章

AST:
Schematic:
Ng add:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持凡科。


"> 对不起,没有下一图集了!">
在线咨询