真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Angular腳手架開發(fā)的實現(xiàn)步驟

簡介

創(chuàng)新互聯(lián)主要從事成都網(wǎng)站建設(shè)、成都做網(wǎng)站、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)富縣,十多年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

寫一份自定義的angular腳手架吧
寫之前我們先解析一下antd的腳手架

前提

先把 Angular Schematic這篇文章讀一遍,確保了解了collection等基礎(chǔ)

antd腳手架

克隆項目

git clone https://github.com/NG-ZORRO/ng-zorro-antd.git

開始

打開項目

Angular腳手架開發(fā)的實現(xiàn)步驟

在schematics下的collection.json為入口,查看內(nèi)容

Angular腳手架開發(fā)的實現(xiàn)步驟

一共定了了4個schematic,每個schema分別指向了各文件夾的子schema.json,factory指向了函數(shù)入口,index.ts

ng-add/schema.json

{
 // 指定schema.json的驗證模式
 "$schema": "http://json-schema.org/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",
   // 默認(rèn)值為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",
   // 默認(rèn)為true
   "default": true,
   // 不指定--bootPage=false的話,你的app.html將會被覆蓋成antd的圖標(biāo)頁
   "description": "Set up boot page."
  },
  // 圖標(biāo)配置
  "dynamicIcon": {
   "type": "boolean",
   "default": false,
   "description": "Whether icon assets should be add.",
   "x-prompt": "Add icon assets [ Detail: https://ng.ant.design/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: https://ng.ant.design/docs/customize-theme/en ]"
  },
  // i18n配置,當(dāng)你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

當(dāng)你進(jìn)入index.ts時首先看到的是一個帶options:Schema的函數(shù),options指向的類型是Schema interface,而這個interface 恰好是schema.json中的properties,也就是cli的傳入?yún)?shù)類.

我們可以通過自定義傳入?yún)?shù)類來完成我們需要的操作.

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必須實現(xiàn)這個函數(shù),一行一行看代碼
// 我們的函數(shù)是一個更高階的函數(shù),這意味著它接受或返回一個函數(shù)引用。
// 在這種情況下,我們的函數(shù)返回一個接受Tree和SchematicContext對象的函數(shù)。
// options:Schema上面提到了
export default function(options: Schema): Rule {
// tree:虛擬文件系統(tǒng):用于更改的暫存區(qū)域,包含原始文件系統(tǒng)以及要應(yīng)用于其的更改列表。
// rule:A Rule是一個將動作應(yīng)用于Tree給定的函數(shù)SchematicContext。
 return (host: Tree, context: SchematicContext) => {
  // 如果需要安裝包,也就是--skipPackageJson=false
  if (!options.skipPackageJson) {
   // 調(diào)用addPackageToPackageJson,傳入,tree文件樹,包名,包版本
   addPackageToPackageJson(host, 'ng-zorro-antd', zorroVersion);
   // hmr模式包
   if (options.gestures) {
    addPackageToPackageJson(host, 'hammerjs', hammerjsVersion);
   }
  }

  
  const installTaskId = context.addTask(new NodePackageInstallTask());

  context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]);

  if (options.bootPage) {
   context.addTask(new RunSchematicTask('boot-page', options));
  }
 };
}

addPackageToPackageJson

// 看function名字就知道這是下載依賴的函數(shù)
// @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的內(nèi)容用utf-8編碼
  const sourceText = host.read('package.json').toString('utf-8');
  // 然后把package.json轉(zhuǎn)化為對象,轉(zhuǎn)為對象,轉(zhuǎn)為對象
  const json = JSON.parse(sourceText);
  // 如果package.json對象里沒有dependencies屬性
  if (!json.dependencies) {
    // 給package對象加入dependencies屬性
   json.dependencies = {};
  }
  // 如果package對象中沒有 pkg(包名),也就是說:如果當(dāng)前項目沒有安裝antd
  if (!json.dependencies[pkg]) {
    // 那么package的dependencies屬性中加入 antd:version
   json.dependencies[pkg] = version;
   // 排個序
   json.dependencies = sortObjectByKeys(json.dependencies);
  }
  // 重寫tree下的package.json內(nèi)容為(剛才不是有package.json對象嗎,現(xiàn)在在轉(zhuǎn)回去)
  host.overwrite('package.json', JSON.stringify(json, null, 2));
 }
  // 把操作好的tree返回給上一級函數(shù)
 return host;
}

現(xiàn)在在回過頭去看 ng-add/index.ts

// 給context對象增加一個安裝包的任務(wù),然后拿到了任務(wù)id
const installTaskId = context.addTask(new NodePackageInstallTask());
// context增加另一個任務(wù),然后傳入了一個RunSchematicTask對象,和一個id集合
  context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]);

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

任務(wù)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,
   // 這個任務(wù)的函數(shù)指向
   "factory": "./ng-add/setup-project/index",
   // 任務(wù)配置項
   "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一樣,實現(xiàn)了一個函數(shù)
export default function (options: Schema): Rule {
 // 這里其實就是調(diào)用各種函數(shù)的一個集合.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 : '@angular/common/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));
  // 循環(huán)字典
  for (const module in modulesMap) {
  // 調(diào)用下面的函數(shù),意思就是:給appModule引一些模塊,好吧,傳入了tree,字典key(模塊名稱),字典value(模塊所在包),project對象,appModule的路徑,Schema配置項
   addModuleImportToApptModule(host, module, modulesMap[ module ],
    project, appModulePath, options);
  }
  // 將構(gòu)建好的tree返回給上層函數(shù)
  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 內(nèi)容差不多,略過

registerLocale

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

先上一張圖片,記得腦子里哦

Angular腳手架開發(fā)的實現(xiàn)步驟

Angular腳手架開發(fā)的實現(xiàn)步驟

接下來的函數(shù)都是為了做上面這個工作

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 時選擇的zh_cn,en_us啥的就是一個字符串
  const locale = getCompatibleLocal(options);
  // 拿到 zh en這種
  const localePrefix = locale.split('_')[ 0 ];
  // recorder可以理解成?快照,一個目錄下多個文件組成的文件快照,re coder
  // 為什么要beginUpdate,實際上我的理解是拿appModulePath文件建立了快照
  // 直到后文 host.commitUpdate(recorder);才會把快照作出的修改提交到tree上面
  // 也可以理解成你的項目有g(shù)it控制,在你commit之前你操作的是快照,理解理解
  const recorder = host.beginUpdate(appModulePath);
  // 對快照的操作列表
  // insertImport = import {xxx} from 'xxx'這種
  // 結(jié)合代碼看一下app.module.ts上面的import內(nèi)容(上面圖片)
  const changes = [
   insertImport(moduleSource, appModulePath, 'NZ_I18N',
    'ng-zorro-antd'),
   insertImport(moduleSource, appModulePath, locale,
    'ng-zorro-antd'),
   insertImport(moduleSource, appModulePath, 'registerLocaleData',
    '@angular/common'),
   insertImport(moduleSource, appModulePath, localePrefix,
    `@angular/common/locales/${localePrefix}`, true),
   registerLocaleData(moduleSource, appModulePath, localePrefix),
   // 這個函數(shù)特殊,看下面
   ...insertI18nTokenProvide(moduleSource, appModulePath, locale)
  ];

  // 循環(huán)變更列表如果是insertChange(import)那么引入
  changes.forEach((change) => {
   if (change instanceof InsertChange) {
    recorder.insertLeft(change.pos, change.toAdd);
   }
  });
  // 提交變更到tree
  host.commitUpdate(recorder);
  // 返回tree給上一級函數(shù)
  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;
 }
}

// 這個函數(shù)主要是為了生成調(diào)用angular本地化的代碼registerLocaleData(zh);
function registerLocaleData(moduleSource: ts.SourceFile, modulePath: string, locale: string): Change {
 ...

 if (registerLocaleDataFun.length === 0) {
  // 最核心的要在app.module中加入registerLocaleData(zh);才能把本地化做到angular上面
  return insertAfterLastOccurrence(allImports, `\n\nregisterLocaleData(${locale});`,
   modulePath, 0) as InsertChange;
 } 
...
}


 * 這個change在change列表略特殊
 * @param moduleSource module文件
 * @param modulePath module路徑
 * @param locale zh
 */
function insertI18nTokenProvide(moduleSource: ts.SourceFile, modulePath: string, locale: string): Change[] {
 const metadataField = 'providers';
 // 獲取app.module中NgModule注釋的內(nèi)容
 //{
 //  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數(shù)組中 **的操作**(只是生成一個動作)還沒應(yīng)用到文件上
 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 {
  // 如果都沒什么大問題,則把增加Provide的動作返回到changes列表,等待commit然后作出更改動作
  return addProvide;
 }
}

參考文章

AST:https://www.kevinschuchard.com/blog/2018-07-17-jest-schematic/
Schematic:https://brianflove.com/2018/12/11/angular-schematics-tutorial/
Ng add:https://brianflove.com/2018/12/15/ng-add-schematic/

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。


網(wǎng)站標(biāo)題:Angular腳手架開發(fā)的實現(xiàn)步驟
網(wǎng)站鏈接:http://weahome.cn/article/gepiho.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部