国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

Angular腳手架開發的實現步驟

2019-11-19 11:50:52
字體:
來源:轉載
供稿:網友

簡介

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

前提

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

antd腳手架

克隆項目

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

開始

打開項目

在schematics下的collection.json為入口,查看內容

一共定了了4個schematic,每個schema分別指向了各文件夾的子schema.json,factory指向了函數入口,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",   // 默認值為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,   // 不指定--bootPage=false的話,你的app.html將會被覆蓋成antd的圖標頁   "description": "Set up boot page."  },  // 圖標配置  "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配置,當你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);   }  }    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名字就知道這是下載依賴的函數// @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對象增加一個安裝包的任務,然后拿到了任務idconst 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 : '@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));  // 循環字典  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 時選擇的zh_cn,en_us啥的就是一個字符串  const locale = getCompatibleLocal(options);  // 拿到 zh en這種  const localePrefix = locale.split('_')[ 0 ];  // recorder可以理解成?快照,一個目錄下多個文件組成的文件快照,re coder  // 為什么要beginUpdate,實際上我的理解是拿appModulePath文件建立了快照  // 直到后文 host.commitUpdate(recorder);才會把快照作出的修改提交到tree上面  // 也可以理解成你的項目有git控制,在你commit之前你操作的是快照,理解理解  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',    '@angular/common'),   insertImport(moduleSource, appModulePath, localePrefix,    `@angular/common/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  host.commitUpdate(recorder);  // 返回tree給上一級函數  return host; };}//上面說了,就是那個zh_CN/en_Usfunction 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上面  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注釋的內容 //{ //  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 {  // 如果都沒什么大問題,則把增加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/

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 香格里拉县| 张北县| 阳谷县| 濮阳市| 宁晋县| 邛崃市| 前郭尔| 崇明县| 漳州市| 铜川市| 阿勒泰市| 黔东| 大冶市| 隆德县| 突泉县| 莲花县| 武隆县| 广河县| 海门市| 安阳县| 晋宁县| 黄平县| 榕江县| 浦城县| 彝良县| 湘潭市| 淮滨县| 五莲县| 沛县| 兴业县| 余江县| 门源| 获嘉县| 措勤县| 榆社县| 泰兴市| 九台市| 无锡市| 都安| 香港| 扶沟县|