Yeoman使用踩坑記

官網(wǎng)傳送門(mén):https://yeoman.io/authoring/index.html


1.npm link 后,yo name 報(bào)錯(cuò):

Error: EACCES: permission denied, open '/Users/sherry/Library/Preferences/insight-nodejs/insight-yo.json.1293917385'

? ? at Object.openSync (fs.js:443:3)

? ? at Function.writeFileSync [as sync] (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/write-file-atomic/index.js:212:13)

? ? at Conf.set store [as store] (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/conf/index.js:142:19)

? ? at Conf.set (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/conf/index.js:64:14)

? ? at Insight.set optOut [as optOut] (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/insight/lib/index.js:56:15)

? ? at Object. (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/lib/cli.js:206:18)

? ? at Module._compile (internal/modules/cjs/loader.js:778:30)

? ? at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)

? ? at Module.load (internal/modules/cjs/loader.js:653:32)

? ? at tryModuleLoad (internal/modules/cjs/loader.js:593:12)

【解決方式】

sudo chown -R Sherry /Users/sherry/Library/Preferences/insight-nodejs


2.yeoman工程的基礎(chǔ)目錄結(jié)構(gòu)設(shè)計(jì)(個(gè)人設(shè)計(jì),僅供參考)

【各文件夾分工】

generators:存放各種 generator,其中,app/index.js 用于腳手架創(chuàng)建的全流程控制邏輯??梢允褂?this.composeWiith('../generator1') 引入其他 generator 的功能。

projects:存放待生成給用戶使用的,完整腳手架(這里,腳手架也可以選擇不存放到 yeoman 工程本地,放到獨(dú)立的 git 倉(cāng)庫(kù),進(jìn)行遠(yuǎn)程引用)

根目錄下的 package.json:服務(wù)于 yeoman 工程

projects/vue、projects/react等目錄下的package.json:服務(wù)于業(yè)務(wù)。留給用戶去手動(dòng) install


3.Managing Dependencies(npm包管理)

生命周期 install(),專門(mén)用來(lái)執(zhí)行 npm 包的安裝。用法如下:

install()?{

? ?this.yarnInstall();

? ?// 使用yarn/npm,自選?

? ?// this.npmInstall();

}

【解析】

這里,相當(dāng)于在命令行執(zhí)行 yarn / npm。

直接安裝?根目錄?下的package.json。

!!注意!! 無(wú)論在哪個(gè)子generator 中的 install 調(diào)用 this.yarnInstall,都只會(huì)安裝?根目錄?下的 package.json。如果子 generator 文件夾中有定義 package.json,它定義的包不會(huì)被安裝。

當(dāng)你需要安裝指定的包時(shí):

this.npmInstall(['lodash'], { 'save-dev': true });

相當(dāng)于在根目錄執(zhí)行:

npm install lodash --save-dev

當(dāng)你需要向現(xiàn)有的 package.json 中動(dòng)態(tài)添加些依賴,或者,不想創(chuàng)建 package.json 文件時(shí),你可以這樣:

這里的 this.destinationPath('package.json'),是在創(chuàng)建 package.json 文件。默認(rèn)在根目錄下創(chuàng)建。

如果根目錄下已經(jīng)存在 package.json,yeoman 會(huì)提示用戶,存在文件沖突,根據(jù)用戶的選擇,決定是否在已有的 package.json 文件中寫(xiě)入 pkgJson 內(nèi)容。

【注】

截圖中的代碼,無(wú)論寫(xiě)在 app/index.js 中,還是形如 generator1/index.js 中,this.destinationPath('package.json') 都只是在根目錄下創(chuàng)建 package.json,而不是在當(dāng)前 generator 文件夾下創(chuàng)建。


那么,如果想改變其他目錄下的 package.json(如動(dòng)態(tài)改變某個(gè)工程的package.json),怎么辦?

假如自定義 generator 的目錄結(jié)構(gòu)如下(generators和node_modules同級(jí),都是根目錄下的一級(jí)文件夾):

當(dāng)期望向 vue/package.json 中動(dòng)態(tài)寫(xiě)入一些?dependencies 定義:

????writing()?{

????????const?pkgJson?=?{

????????????dependencies:?{

????????????????vue:?'^2.0.0'

????????????}

????????};

????????this.fs.extendJSON(this.destinationPath('projects/vue/package.json'),?pkgJson);

????}

【個(gè)人思考】

動(dòng)態(tài)寫(xiě)入?dependencies,尤其適用于用戶選擇是否使用某種功能(某個(gè)模塊)的情況。

例如:

用戶交互時(shí),選擇使用 redux,則可以這樣動(dòng)態(tài)寫(xiě)入 redux 包引用。否則,腳手架的 package.json 中,默認(rèn)不寫(xiě)入 redux 引用。


但要時(shí)刻注意,這里僅僅只是進(jìn)行 package.json 的寫(xiě)入,并不會(huì)執(zhí)行安裝。

任何地方的 install() 中調(diào)用 this.yarnInstall(),都只會(huì)執(zhí)行根目錄下 package.json 的包安裝。

這個(gè)也很好理解。因?yàn)槲覀冮_(kāi)發(fā)的是自定義的 generator,因此,執(zhí)行包安裝,理應(yīng)服務(wù)于這個(gè) yeoman 工程,而不是它內(nèi)部的其他子工程(腳手架)。

而腳手架中的 package.json,也理應(yīng)由用戶創(chuàng)建項(xiàng)目后,自行手動(dòng)安裝。


4.Interacting with the file system(文件系統(tǒng)交互)

(1)各種路徑的獲取方式、自定義方式


· destinationRoot:生成文件的存放目錄(目標(biāo)文件夾)

【獲取】this.destinationRoot()?

***默認(rèn)值***

如果當(dāng)前執(zhí)行 yo name 的目錄下包含.yo-rc.json,則為當(dāng)前文件夾。

否則,則為最近的、包含.yo-rc.json的父文件夾。

如果當(dāng)前文件夾、及所有父文件夾,都不包含.yo-rc.json,則在當(dāng)前目錄下創(chuàng)建.yo-rc.json,并以當(dāng)前目錄作為生成文件存放的默認(rèn)目錄。

【自定義】this.destinationRoot('new/folder')

【生成文件的路徑定義】this.destinationPath('index.js') —— 在?this.destinationRoot() 目錄下,生成 index.js

·?contextRoot:用戶當(dāng)前執(zhí)行 yo 指令的目錄

【獲取】this.contextRoot


·sourceRoot:(用于復(fù)制的)模板文件的存放目錄

【獲取】this.sourceRoot()?

***默認(rèn)值***

與當(dāng)前調(diào)用 this.sourceRoot() 的 generator 的 index.js 同級(jí)的 templates 文件夾。

例如,在 app/index.js 調(diào)用 this.sourceRoot(),sourceRoot 默認(rèn)指向如圖:

【自定義】this.sourceRoot('new/folder') —— 根目錄下的 new/folder 文件夾

【注意】

自定義為:this.sourceRoot('./new/folder') ,指向同上。依然指向根目錄下的 new/folder 文件夾,而不是在當(dāng)前文件所在目錄下尋找 new/folder

【模板文件的路徑指定】this.templatePath('tpl.html') —— 去 this.sourceRoot() 下,找 tpl.html 文件

(2)格式化生成的文件

例如:

使用這個(gè)包,會(huì)把生成的文件(也就是寫(xiě)入到this.destinationPath的那個(gè)文件),按照?qǐng)D中指定的規(guī)則格式化。


5. .yo-rc.json 文件

作用:用來(lái)存放所有 generators 的配置對(duì)象。

可通過(guò)?this.config.xxx api 進(jìn)行配置的設(shè)置、獲取等操作。詳見(jiàn)官網(wǎng):https://yeoman.io/authoring/storage.html

形如:

【注】

每一個(gè) generator 一個(gè)命名空間。不能通過(guò) this.config.xxx 進(jìn)行配置信息的共享。

可以通過(guò) options 和 arguments 在多個(gè) generator 間分享數(shù)據(jù)。(具體使用方式及意義待測(cè)試)


6.在任意文件夾下,執(zhí)行 yo name 創(chuàng)建項(xiàng)目,邏輯流程運(yùn)行過(guò)程中的各種 permission denied 錯(cuò)誤(mkdir / rm -rf / this.fs.copyTpl)

網(wǎng)上大家都說(shuō)設(shè)置chmod +w filename,試了,無(wú)效。mac系統(tǒng)。

實(shí)測(cè),假如在 test 目錄下執(zhí)行 yo name,報(bào) permission denied 錯(cuò)誤,請(qǐng)嘗試!!??

sudo chown -R userName test


7.this.fs.copyTpl 的回調(diào)問(wèn)題

首先明確一點(diǎn),this.fs.copyTpl 是同步方法,并不是異步的,因此,沒(méi)有提供回調(diào)函數(shù)。

可能你也遇到了和我一樣的問(wèn)題:

使用 this.fs.copyTpl 生成了一些文件夾,并在?this.fs.copyTpl 調(diào)用后面,執(zhí)行刪除操作,期望刪掉某些/某個(gè)文件夾。但是發(fā)現(xiàn),并刪不掉。

于是,很自然的覺(jué)得,這是異步問(wèn)題,我只要在?this.fs.copyTpl 的回調(diào)里面去刪,就 ok 了。

但是,如前所述,this.fs.copyTpl 其實(shí)是同步方法。

那么,是什么原因造成了無(wú)法刪除的問(wèn)題呢?

【答案是】

yeoman 在進(jìn)行文件處理的時(shí)候,把所有即將生成的文件/文件夾都放在了內(nèi)存里,而不是直接寫(xiě)到磁盤(pán)上。

因此,這時(shí)候執(zhí)行,形如 shelljs.rm('-rf', 'xxxxx') 的操作,是不會(huì)成功的。因?yàn)樗械奈募?,都還沒(méi)有寫(xiě)到磁盤(pán)上。

詳見(jiàn)官網(wǎng)說(shuō)明:https://yeoman.io/authoring/file-system.html

【解決方法】

在 install() 或 end() 中,進(jìn)行?shelljs.rm('-rf', 'xxxxx') 這類操作。從語(yǔ)義上,建議在 end() 中執(zhí)行。

原理:在文件/文件夾寫(xiě)入磁盤(pán)后操作。

【解析】

yeoman 共計(jì) 8 個(gè)生命周期函數(shù),執(zhí)行順序如下:

initializing: 1

prompting: 2

configuring: 3

default: 4

// 自定義的原型方法在這個(gè)地方按順序執(zhí)行

writing: 5

conflicts: 6

// 文件/文件夾寫(xiě)入磁盤(pán),在這里進(jìn)行

install: 7

end: 8


8.關(guān)于argument和option

(1)argument

【如何定義】

constructor(args, opts) {

? ? super(args, opts);

? ? this.argument('projectName', {

? ? ? ? type: Array,

? ? ? ? required: false, // 這里不設(shè)置,或 this.argument 不傳第二個(gè) options 參數(shù),默認(rèn)都為 必傳

? ? ? ? default: this.appname, // 運(yùn)行 yo name 的文件夾名稱

? ? ? ? desc: '項(xiàng)目名稱'

????});

}

【如何使用】

yo name my-project

【如何讀取】

this.log('argument?projectName:',?this.options.projectName);

【是否定義多個(gè)argument?】

—— 可以。

多個(gè) argument 如何區(qū)分?

—— 通常,yo name argument1 argument2 傳入多個(gè) argument 時(shí),按照 this.argument 的定義順序分別賦值。

eg:

this.argument('name');

this.argument('age');

=> this.options.name ===?argument1

? ? ?this.options.age === argument2

【注】

argument 為數(shù)組的情況,會(huì)取當(dāng)前定義位置之后的所有 argument 的集合。

定義eg:

this.argument('name');

this.argument('friends', {

? ? type: Array,

? ??required: false,

? ??default: [], // 運(yùn)行 yo name 的文件夾名稱? ??

? ? desc: '朋友們'

})

this.argument('age');

使用eg:

yo name Sherry Dennis Jack Tom 25

結(jié)果:

this.options.friends:[Dennis, Jack, Tom, 25]

this.options.age:Jack

(2)option(類似flag)

【如何定義】

this.option('coffee', {

? ? alias: 'co'

})

【如何使用】

yo name --coffee / yo name --co

【如何讀取】

this.log('argument?projectName:',?this.options.coffee);

【查看我們自定義的option】

yo name --help

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容