不得不說,和傳統的複制黏貼來建立元件的方法相比,使用angular-cli的腳手架功能來建立子產品、元件顯得非常高效,不僅僅有了建立了檔案,還包含了一些必須的代碼,同時也将元件導入了最近的子產品,一些重複性工作就使用cli可以節省掉。angular提供了豐富的檔案類型,但是總歸是有些我們自己的項目需要,我們需要建立自定義字尾的元件,這時候就不得不舍棄cli了,那麼能不能使用自定義的方式來達到腳手架建立呢?
angular 腳手架建立的方式
我們首先來看看angular-cli提供的一些指令是怎麼建立檔案的。
看angular文檔我們可以看到這個詞:
Schematic
,這個詞意為原理圖。
Schematic
是一個腳手架庫,定義如何通過建立、修改、重構或移動檔案和代碼來生成或轉換程式設計項目。Angular Cli使用原理圖生成和修改項目檔案。庫開發人員可以建立原理圖,使Cli能夠生成其已釋出的庫。可以檢視https://www.npmjs.com/package/@angular-devkit/schematics。
那這樣的話,我們可以知道angular是借助的
Schematic
來生成項目檔案。再檢視發現
node_module
裡面有個
@Schematics/angular
,裡面定義了我們可以使用cli生成的所有檔案,包括
components\class\enum\interface
等等。
點開檢視
components
,裡面有一些ts檔案,還有一個files檔案夾,裡面包含着所有我們生成component的檔案:
-
[email protected]@if-flat__
-
__nam[email protected]herize__.component.__styleext__
-
[email protected]__.component.html
-
[email protected]__.component.spec.ts
-
[email protected]__.component.ts
-
那想想,在我們運作Cli建立元件的時候,會使用這裡的模闆,用file檔案夾裡面的檔案生成項目元件。其他的先不管,我們按照這裡的
components
是不是可以來構造我們自己的“原理圖”呢?
自定義原理圖
先看看我們的需求,我們現在項目的項目裡面,頁面是page,按照angular原來的寫法,所有的page的元件都是:
XXXX.component.ts
。我們為了将頁面群組件進行區分,頁面的檔案都是
XXX.page.ts
。我們先在
node_module/@Schematics/angula/
下面複制
component
建立一個
page
。
現在,将page下面的files檔案夾中的檔案名
.component
都改為
.page
(由于我們不用單元測試檔案,直接删除
.spec.ts
檔案即可):
- page
- files
-
[email protected]@if-flat__
-
[email protected]__.page.__styleext__
-
[email protected]__.page.html
-
[email protected]__.page.ts
-
-
- index.d.ts
- index.js 指令運作時會執行這個js檔案
- schema.d.ts
- schema.json 定義了這個生成器指令可以接受的參數
- files
接下來再看page裡面的
index.js
,這個js檔案在我們跑自己的指令的時候會執行。看這個檔案,裡面的代碼雖然有點看不懂,但是猜猜還是可以的,有些關鍵地方:
const componentPath = `/${options.path}/`
+ (options.flat ? '' : core_1.strings.dasherize(options.name) + '/')
+ core_1.strings.dasherize(options.name)
+ '.component';
const classifiedName = core_1.strings.classify(`${options.name}Component`);
類似于這樣的地方,我們發現就是建立對應的元件檔案和裡面的元件類。是以我們把所有
.component
和
}Component
的地方替換為
.page
和
}Page
:
const componentPath = `/${options.path}/`
+ (options.flat ? '' : core_1.strings.dasherize(options.name) + '/')
+ core_1.strings.dasherize(options.name)
+ '.page';
const classifiedName = core_1.strings.classify(`${options.name}Page`);
然後接下來再看
page/files/[email protected]__.page.ts
:
import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
@Component({
selector: '<%= selector %>',<% if(inlineTemplate) { %>
template: `
<p>
<%= dasherize(name) %> works!
</p>
`,<% } else { %>
templateUrl: './<%= dasherize(name) %>.component.html',<% } if(inlineStyle) { %>
styles: []<% } else { %>
styleUrls: ['./<%= dasherize(name) %>.component.<%= styleext %>']<% } %><% if(!!viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
})
export class <%= classify(name) %>Component implements OnInit {
constructor() { }
ngOnInit() {
}
}
這個是生成的元件的ts模闆,我們需要根據我們的需求來改造,首先是檔案裡面的類,既然我們現在的檔案名是
XXX.page.ts
,那麼裡面的類也就需要時
XXXPage
形式的,并且我們的頁面是不允許作為指令的形式出現的,是以也要去掉
selector
中繼資料。那綜合下來,我們的
[email protected]__.page.ts
應該修改為:
import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
@Component({
templateUrl: './<%= dasherize(name) %>.page.html',
<% if(inlineStyle) { %>
styles: []<% } else { %>
styleUrls: ['./<%= dasherize(name) %>.page.<%= styleext %>']<% } %><% if(!!viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
})
export class <%= classify(name) %>Page implements OnInit {
constructor() { }
ngOnInit() {
}
}
OK,目前為止,我們的“原理圖”就建立的差不多了,我們現在需要加入cli指令上去。在
@Schematics/angular/collection.json
裡面定義了cli的指令,同樣,先觀察componet的指令:
"component": {
"aliases": [ "c" ], // 簡寫形式
"factory": "./component", // 采用生成器
"description": "Create an Angular component.",
"schema": "./component/schema.json"
},
我們來建立我們自己的指令:
"component": {
"aliases": [ "pa" ], // 簡寫形式
"factory": "./page", // 采用生成器
"description": "Create an Angular component page.",
"schema": "./page/schema.json"
},
測試指令
目前為止,我們已經添加好了我們自己的生成指令,現在來嘗試着生成一個page元件,在
app/pages/user
下面生成元件
user-test
,指令:
ng g page pages/user/user-test
,檢視結果:
CREATE src/app/pages/user/user-test/user-test.page.css (0 bytes)
CREATE src/app/pages/user/user-test/user-test.page.html (28 bytes)
CREATE src/app/pages/user/user-test/user-test.page.ts (239 bytes)
UPDATE src/app/pages/user/user.module.ts (1803 bytes)
看看生成的ts檔案:
import { Component, OnInit } from '@angular/core';
@Component({
templateUrl: './user-test.page.html',
styleUrls: ['./user-test.page.css']
})
export class UserTestPage implements OnInit {
constructor() { }
ngOnInit() {
}
}
非常好啊,完全滿足我們的需求。
慢着,我現在項目中使用的是less,而且使用
component
建立的元件裡面的樣式檔案都是less,為啥我們自定義的生成的是css檔案???
很可能是沒有識别我們自定義的less,那我們自定義的less是怎麼定的呢?看看
angular.json
檔案中有個
project
裡面:
"schematics": {
"@schematics/angular:component": {
"styleext": "less"
}
},
也就是說,我們在這裡配置了生成
component
元件時,
styleext
為
less
,我們的
page
指令是沒有配置的,是以會找預設的樣式檔案字尾。那我們在這裡嘗試加上試試看:
"schematics": {
"@schematics/angular:component": {
"styleext": "less"
},
"@schematics/angular:page": {
"styleext": "less"
}
},
再生成一下:
CREATE src/app/pages/user/user-test/user-test.page.less (0 bytes)
CREATE src/app/pages/user/user-test/user-test.page.html (28 bytes)
CREATE src/app/pages/user/user-test/user-test.page.ts (240 bytes)
UPDATE src/app/pages/user/user.module.ts (1804 bytes)
完美~