前端工程化系列[06] Yeoman腳手架核心機(jī)制

前端工程化系列[05] Yeoman腳手架使用入門(mén)這邊文章中,對(duì)Yeoman的使用做了簡(jiǎn)單的入門(mén)介紹,這篇文章我們將接著探討Yeoman這個(gè)腳手架工具內(nèi)部的核心機(jī)制,主要包括以下內(nèi)容

? Yeoman腳手架工具的價(jià)值討論
? generator[生成器]的內(nèi)部結(jié)構(gòu)
? generator[生成器]的項(xiàng)目模板
? Yeoman腳手架工具的核心運(yùn)轉(zhuǎn)機(jī)制
? Yeoman 的主要組裝流程

Yeoman這樣的腳手架工具解決了什么問(wèn)題?

所有新事物都不是憑空產(chǎn)生的,它們的出現(xiàn)總有某些內(nèi)在的驅(qū)動(dòng)力。一項(xiàng)新技術(shù),一個(gè)新工具的出現(xiàn)更是如此。不知道從什么時(shí)候開(kāi)始起,我接觸新事物新技術(shù)以及某些工具的時(shí)候,總愿意多花點(diǎn)時(shí)間想一想它出現(xiàn)的原因是什么?因?yàn)闀r(shí)間、精力等等這些東西都很寶貴,IT從業(yè)人員對(duì)這些資源尤其敏感,所以新技術(shù)或者新工具的出現(xiàn)我認(rèn)為有幾種情況:

  • 已有的技術(shù)或工具存在缺陷,作者們靠自己的才學(xué)推出更完美的替代方案
  • 已有的技術(shù)或工具無(wú)法解決既定的需求,作者們探索出解決問(wèn)題的技術(shù)方案
  • 純粹閑的蛋疼(這種情況一般比較少見(jiàn))

現(xiàn)在,我們來(lái)研究下Yeoman的價(jià)值,或者說(shuō)Yeoman出現(xiàn)的意義是什么?Yeoman的出現(xiàn)解決了什么樣的問(wèn)題?

我們假設(shè)有這樣的開(kāi)發(fā)場(chǎng)景:公司的開(kāi)發(fā)團(tuán)隊(duì),基于某些特定的技術(shù)棧已經(jīng)完成了項(xiàng)目A的開(kāi)發(fā)和上線(xiàn)等工作,項(xiàng)目A的基本情況如下

技術(shù)棧:JavaScript + HTML + CSS + Bootstrap + jQuery
工作流:npm(包管理工具) + bower(下載器) + grunt
版本管理工具:Git

項(xiàng)目整體目錄結(jié)構(gòu)(簡(jiǎn)化后)

.
├── Gruntfile.js
├── bower.json
├── node_modules
│   ├── abbrev
···
│   └── xtend
├── package-lock.json
├── package.json
├── build
│   ├── css
│   │   └── style.min.css
│   └── js
│       ├── index.js
│       └── index.min.js
├── dist
└── src
    ├── css
    │   └── style.css
    ├── index.html
    ├── js
    │   └── index.js
    ├── libs
    │   ├── bootstrap
    │   └── jquery
    └── template

說(shuō)明:上面的目錄中src為代碼的工作目錄,bulid為構(gòu)建后目錄,dist為發(fā)布目錄。

因?yàn)轫?xiàng)目A已經(jīng)上線(xiàn)發(fā)布,現(xiàn)在公司要求著手開(kāi)展新的項(xiàng)目B,經(jīng)過(guò)需求評(píng)審和技術(shù)選型后,新項(xiàng)目B采用的工作流和項(xiàng)目A保持一致,技術(shù)棧在原有的基礎(chǔ)上嘗試使用TypeScript來(lái)處理腳本部分引入Vue框架,其它部分保持不變。我們發(fā)現(xiàn)項(xiàng)目A和項(xiàng)目B它們的結(jié)構(gòu)基本上是一致的(比如項(xiàng)目的目錄就夠,都需要擁有Gruntfile.js和package.json等文件),但是有些部分又不太一樣,比如package.json文件中的項(xiàng)目名稱(chēng)、開(kāi)發(fā)依賴(lài)等。

這個(gè)時(shí)候,我們?cè)趯?duì)項(xiàng)目B進(jìn)行初始化的方式可以嘗試以下操作方式:

  • 方案①從0開(kāi)始創(chuàng)建目錄結(jié)構(gòu),集成工作流配置開(kāi)發(fā)環(huán)境`
  • 方案②從項(xiàng)目A中拷貝目錄結(jié)構(gòu)和固定文件,對(duì)于不同的部分一個(gè)個(gè)修改

如果我們采用方案① 你會(huì)發(fā)現(xiàn)這個(gè)過(guò)程你在初始化項(xiàng)目A的時(shí)候就已經(jīng)做過(guò)了,是重復(fù)性的工作,毫無(wú)技術(shù)含量但是又費(fèi)時(shí)費(fèi)力。
如果我們采用方案② 你會(huì)發(fā)現(xiàn)要修改的文件有些多,每個(gè)文件要改的字段也比較多,而且容易遺漏總是調(diào)不通會(huì)出現(xiàn)各種問(wèn)題,心煩意亂。

如果你會(huì)使用Yeoman腳手架工具的話(huà),那么對(duì)于上面的開(kāi)發(fā)場(chǎng)景你就會(huì)多一個(gè)方案③,在使用方案③來(lái)初始化項(xiàng)目B的時(shí)候,你只需要?jiǎng)觿?dòng)手指在終端中輸入$ yo 生成器名稱(chēng)再使用交互方式簡(jiǎn)單配置某些特定值,初始化的工作就完成了。這就是Yeoman的價(jià)值所在,初始化項(xiàng)目的時(shí)候你不必再把自己沉入到瑣碎重復(fù)無(wú)技術(shù)成長(zhǎng)的費(fèi)力工作中,也不必總是像個(gè)機(jī)器人般進(jìn)入到拷貝-粘貼-修改這樣無(wú)止境的循環(huán)中。腳手架工具是那么的簡(jiǎn)單直接和高效,你甚至可以省出點(diǎn)加班的時(shí)間來(lái)看世界杯了 : )

我知道有一些杠精要出來(lái)噴了。“解決這種初始化問(wèn)題不用搞的這么復(fù)雜,我完全可以把項(xiàng)目結(jié)構(gòu)和固定不變的部分抽取出來(lái)托管到gitHub倉(cāng)庫(kù),要初始化項(xiàng)目的時(shí)候 $ git clone一下不就好了嗎?”

說(shuō)的很有道理,但是clone下來(lái)的倉(cāng)庫(kù)雖然結(jié)構(gòu)和必要文件已經(jīng)準(zhǔn)備好了,但很多文件是不是還得修改?那你會(huì)頂回來(lái)“難道使用Yeoman初始化就不需要修改了嗎?”當(dāng)然也要修改,不過(guò)就算是修改那改起來(lái)也很有趣味還So快!

Yeoman使用交互式的方式來(lái)對(duì)項(xiàng)目文件中需要靈活處理的部分進(jìn)行配置,這部分內(nèi)容我們稱(chēng)為組裝指令,具體再文章的后面會(huì)進(jìn)行講解。

另外,如果新項(xiàng)目的整體結(jié)構(gòu)以及技術(shù)選型和已有的項(xiàng)目很不一樣,那你抽取后交由git管理的倉(cāng)庫(kù)就沒(méi)用了,因?yàn)榘俗植缓习 J褂肶eoman就沒(méi)用這樣的顧慮,在Yeoman-generator列表有好幾千現(xiàn)成的generator供你選擇,總有一款適合你!?。?/p>

我要求太太...太高,實(shí)在誰(shuí)也看不上?沒(méi)關(guān)系,generator這家伙還可以私人訂制,你完全可以根據(jù)自己的需求來(lái)定制需要的generator,你一高興甚至還能把它發(fā)布到社區(qū)造福全人類(lèi)。

Yeoman-generator的內(nèi)部結(jié)構(gòu)

搞清楚 generator的價(jià)值所在和應(yīng)用場(chǎng)景之后,我們就可以開(kāi)始談?wù)揼enerator相關(guān)的話(huà)題了,前面介紹過(guò)Yeoman腳手架工具的作用是幫助我們依據(jù)特定的技術(shù)棧需求來(lái)初始化項(xiàng)目,在安裝了yo工具之后,只需要在終端中使用類(lèi)似$ yo generator--xx的命令先安裝對(duì)應(yīng)的generator然后再$ yo xx搭建即可。至于如何找到匹配當(dāng)前技術(shù)選型的generator,可以去官網(wǎng)的generator列表搜索,這些生成器中有很大一部分來(lái)自于對(duì)應(yīng)框架的作者或者Yeoman官方團(tuán)隊(duì),質(zhì)量有保證且更新很及時(shí)。當(dāng)然,我們也可以創(chuàng)建自己的generator并發(fā)布。關(guān)于如何創(chuàng)建自己的generator,我們放到另一篇文章Yeoman腳手架生成器創(chuàng)建來(lái)解決。

簡(jiǎn)單說(shuō)Yeoman做的工作其實(shí)就是根據(jù)當(dāng)前的生成器(generator)來(lái)復(fù)制固定的項(xiàng)目模板文件到新項(xiàng)目中,而新項(xiàng)目中的某些文件需要配置,這部分工作由安裝時(shí)候的交互式指令來(lái)完成(相當(dāng)于傳遞參數(shù)給模板文件)。

需要注意的是Yeoman的設(shè)計(jì)僅僅只提供了一小部分核心的API,而真正繁重的初始化工作是交給每個(gè)具體的generator來(lái)完成的

generator主要由組裝指令項(xiàng)目模板兩部分組成。

組裝指令

Yeoman generator中的generators/app/index.js文件是整個(gè)生成器的核心部分,該文件用于告知Yeoman該如何來(lái)組織并搭建項(xiàng)目,我們可以在該文件中設(shè)置初始化項(xiàng)目時(shí)必要的安裝提示和選項(xiàng)來(lái)讓用戶(hù)選擇,以及每個(gè)文件應(yīng)該如何復(fù)制和修改,是否需要加載依賴(lài)和Node包等內(nèi)容。

項(xiàng)目模板

項(xiàng)目模板包括初始化項(xiàng)目需要的所有必須文件。這些文件又可以簡(jiǎn)單的劃分為固定文件、靈活文件、可選文件依賴(lài)文件。所謂固定文件就是在每個(gè)初始項(xiàng)目中都一模一樣的文件,譬如index.js、style.css等文件,在具體處理的時(shí)候這些文件只需要簡(jiǎn)單復(fù)制即可。靈活文件指的是那些需要根據(jù)用戶(hù)選擇來(lái)做簡(jiǎn)單修改然后才能復(fù)制的文件,譬如index.html文件(title等信息需根據(jù)用戶(hù)輸入來(lái)指定)。對(duì)于可選文件來(lái)說(shuō),它們并不是必須的,譬如某些基礎(chǔ)框架有的項(xiàng)目中需要,有的項(xiàng)目中也許并不需要,這部分文件的處理方式需要交給用戶(hù)來(lái)決定。

項(xiàng)目模板文件的類(lèi)別

前面已經(jīng)介紹過(guò)了Yeoman生成器的組成部分主要是組裝指令和項(xiàng)目模板。對(duì)于整個(gè)Yeman腳手架工具來(lái)說(shuō),項(xiàng)目模板這部分就相當(dāng)于是搭建腳手架需要用到的原材料,而組裝指令用來(lái)決定和控制所有的具體行動(dòng)是什么。

現(xiàn)在我們開(kāi)始深入的來(lái)討論項(xiàng)目模板這部分內(nèi)容,需要先明白的是“能夠滿(mǎn)足所有需求的萬(wàn)能的項(xiàng)目模板是不存在的”。因?yàn)檫@世界上每個(gè)項(xiàng)目組,每個(gè)產(chǎn)品甚至每個(gè)人的需求(要求)都各有不同。所以,在實(shí)踐中你必須要對(duì)當(dāng)前項(xiàng)目的需求和采用的技術(shù)棧有深入的理解,這樣你才能知道目標(biāo)項(xiàng)目的目錄結(jié)構(gòu)會(huì)是什么樣的? 哪些文件是必不可少的。

如果你的項(xiàng)目和采用的技術(shù)棧比較大眾化,那么搜索一個(gè)合適的generator基本就能滿(mǎn)足需求,拿來(lái)主義即可。如果你的項(xiàng)目不管結(jié)構(gòu)還是所采用的技術(shù)看上去都那么的非凡和特別,那么就多花一點(diǎn)點(diǎn)時(shí)間創(chuàng)建個(gè)自己的generator吧,如果你需要處理多個(gè)這樣的項(xiàng)目,那就更應(yīng)該了。在創(chuàng)建或者理解generator的時(shí)候,我們可以根據(jù)前面對(duì)項(xiàng)目模板文件的劃分情況來(lái)區(qū)別對(duì)待不同的文件。

固定文件

固定文件是在每個(gè)項(xiàng)目中初始內(nèi)容都一樣的必要文件。

比如我們可能總是會(huì)把代碼的結(jié)構(gòu)劃分為srcbuilddist三個(gè)目錄,在src目錄下面擁有js、css和lib文件目錄,index.js和style.css等文件。這些文件都是必要的,剛開(kāi)始的時(shí)候可能是空的或者只有幾行簡(jiǎn)單的代碼。這些文件的特點(diǎn)是,在使用組裝指令操作(通常是復(fù)制-移動(dòng))這些文件的時(shí)候,不需要對(duì)它們進(jìn)行任何的修改。

靈活文件

靈活文件和固定文件差不多,也是初始化項(xiàng)目所必須的,但不同的項(xiàng)目中這些文件的內(nèi)容也會(huì)稍有不同,這些不同之處可能很細(xì)微(比如僅僅是名字、協(xié)議這些),也可能差異巨大。比如,我們常用的構(gòu)建工作流中的bower.jsonpackage.json文件,它們是必不可少的,但是它們都需要當(dāng)前項(xiàng)目的項(xiàng)目名稱(chēng)和協(xié)議等信息才能正常工作。像這樣的靈活文件還有index.html,在這個(gè)文件中的title標(biāo)簽中應(yīng)該使用當(dāng)前項(xiàng)目的名稱(chēng)。

靈活文件中的部分內(nèi)容需要在安裝該生成器的時(shí)候,由用戶(hù)交互式配置輸入的信息來(lái)進(jìn)行設(shè)定。

可選文件

可選文件并不是搭建初始化項(xiàng)目時(shí)所必須的文件,如果沒(méi)有那么沒(méi)關(guān)系,如果有那似乎更好。這些一般在用戶(hù)交互式配置的時(shí)候,以是否題的方式交由用戶(hù)決定,譬如是否使用less 是否安裝Bootstrap等。

依賴(lài)文件

依賴(lài)文件指的是某些常用的框架、插件或者是Node模塊,這些文件并不需要你在項(xiàng)目模板文件中提供,然后通過(guò)組裝指令去一個(gè)個(gè)復(fù)制。因?yàn)榛旧铣墒斓捻?xiàng)目中都會(huì)使用既定的工作流(主要包括依賴(lài)和包的下載、項(xiàng)目的自動(dòng)化構(gòu)建等),所以我們完全只需要在package.json或者bower.json等文件中設(shè)置好依賴(lài)即可,然后在組裝指令的相關(guān)代碼中通過(guò)this.installDependencies()類(lèi)似的代碼來(lái)調(diào)用npm或者是bower執(zhí)行install命令即可。

Yeoman腳手架運(yùn)轉(zhuǎn)的核心機(jī)制

當(dāng)您為項(xiàng)目準(zhǔn)備好(搜索或自己創(chuàng)建)合適的generator之后,就可以用它們來(lái)搭建項(xiàng)目了。generator的執(zhí)行需要在終端中使用yo命令來(lái)操作。yo是Yeoman的核心命令,主要用來(lái)連接生成器和項(xiàng)目結(jié)構(gòu)。我們可以把yo命令理解為generator的執(zhí)行器,它知道怎么找到對(duì)應(yīng)的generator,也知道該如何執(zhí)行它們。

注意:yo基于NodeJS且需要在任何文件目錄中使用,所以在安裝yo命令的時(shí)候應(yīng)該使用-g來(lái)進(jìn)行全局安裝。安裝過(guò)程請(qǐng)參考:Yeoman腳手架使用入門(mén)。

在使用yo命令行工具和生成器來(lái)初始化項(xiàng)目之前,需要先把指定的生成器(generator)下載安裝到本地(如果是自己創(chuàng)建的生成器,那么可以通過(guò)$ npm link命令以軟連接的方式生成一個(gè)全局的npm包,我的是mac OSX系統(tǒng),生成的npm包會(huì)保存在/usr/local/lib/node_modules/路徑,如果使用的是別人發(fā)布的generator,那么請(qǐng)使用$ npm install -g generator-xxx的方式來(lái)安裝)。

這里需要注意的是yo命令行工具主要負(fù)責(zé)前期工作,在使用的時(shí)候它主要檢查當(dāng)前安裝的generator有哪些,指定的generator是否能夠正常工作,如果能,那么它就會(huì)調(diào)用generator的組裝指令,把剩下部分的工作交接給generator來(lái)完成。generator接管項(xiàng)目的組裝流程之后,會(huì)按app/index.js中的要求來(lái)處理文件的復(fù)制等工作。

下面給出腳手架工具初始化項(xiàng)目時(shí)的核心流程。


這里對(duì)yo的主要命令進(jìn)行簡(jiǎn)單說(shuō)明

$ yo 執(zhí)行該命令的時(shí)候,yo會(huì)搜索并列出所有本地可用的生成器
$ yo 生成器名稱(chēng) 比如對(duì)于generator-typescript生成器,那么執(zhí)行的命令就是$ yo typescript。該命令會(huì)先檢查enerator-typescript生成器是否可用。如果可用,那么就接著以 ①交互式配置 ② 寫(xiě)入文件 ③ 下載安裝依賴(lài)的順序來(lái)執(zhí)行組裝指令。

Yeoman的主要組裝流程

組裝指令是用來(lái)讓Yeoman創(chuàng)建項(xiàng)目所需文件的一系列具體的命令(代碼)。典型的組裝流程分為三個(gè)步驟:


① 交互式配置。這個(gè)步驟通過(guò)向用戶(hù)提問(wèn)或直接輸入配置信息來(lái)完成模板傳參。
② 寫(xiě)入文件。把項(xiàng)目模板中的指定文件復(fù)制到新項(xiàng)目的指定目錄中。
③ 安裝依賴(lài)。下載并安裝所有保存在bower.json和package.json文件中的依賴(lài)和Node模塊。

① 交互式配置

Yeoman在執(zhí)行生成器的時(shí)候,首先會(huì)執(zhí)行安裝提示以交互式的方式來(lái)詢(xún)問(wèn)用戶(hù),目的是為了獲取生成器所需要的一些參數(shù),比如項(xiàng)目的名稱(chēng)、作者、使用的開(kāi)原協(xié)議以及是否安裝和使用某些組件等。

這部分功能,需要使用到inquirer包,這個(gè)包的作用是生成選項(xiàng)來(lái)讓用戶(hù)選擇。下面給出代碼示例:

prompting() {
const prompts = [
      {
        type    : 'input',
        name    : 'appName',
        message : '請(qǐng)輸入項(xiàng)目名稱(chēng):',
        default : this.appname        //appname是內(nèi)置對(duì)象,代表工程名,這里就是ys
     },
     {
       type    : 'input',
       name    : 'appAuthor',
       message : '請(qǐng)輸入作者姓名:',
       default : '文頂頂'
    },
    {
        type: 'list',
        name: 'appLicense',
        message: '請(qǐng)選擇使用的license:',
        choices: ['MIT', 'ISC', 'Apache-2.0', 'AGPL-3.0']
      },
      {
        type    : 'confirm',
        name    : 'isIncludeBootstrap',
        message : '是否需要使用bootStrap框架?',
        default : false
     },

    ];
    return this.prompt(prompts).then(props => {
      // To access props later use this.props.someAnswer;
      this.props = props;
    });

我們可以看到在代碼中,這些交互式配置都由prompts來(lái)進(jìn)行維護(hù),prompts是一個(gè)對(duì)象數(shù)組,數(shù)組中的每個(gè)元素對(duì)象就代表著一個(gè)具體的安裝提示,在使用yo命令運(yùn)行該生成器的時(shí)候,它的執(zhí)行情況如下:


     _-----_     ╭──────────────────────────╮
    |       |    │         歡迎使用            │
    |--(o)--|    │      generator-wen!        │
   `---------′   │      Author:文頂頂         │
    ( _′U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |     
   __'.___.'__   
 ′   `  |° ′ Y `

? 請(qǐng)輸入項(xiàng)目名稱(chēng): wendingdingTest
? 請(qǐng)輸入作者姓名: 文頂頂
? 請(qǐng)選擇使用的license: Apache-2.0
? 是否需要使用bootStrap框架? (y/N) yes

prompts中的每個(gè)對(duì)象元素就代表著一個(gè)安裝提示,上面代碼一共提供了四個(gè)安裝提示。每個(gè)對(duì)象中的type屬性用于表明交互的類(lèi)型,其中輸入項(xiàng)目名稱(chēng)和作者姓名是input型的,表示接收用戶(hù)的輸入,相當(dāng)于填空題。選擇使用的license是list型的,它提供了多個(gè)選項(xiàng)供用戶(hù)選擇,您可以認(rèn)為這種類(lèi)型是單選題。是否需要使用bootStrap框架是confirm型的,默認(rèn)為false,如果需要安裝那么需要輸入YES,這相當(dāng)于是非題。

說(shuō)明:交互式配置這部分可以根據(jù)項(xiàng)目的實(shí)際情況來(lái)設(shè)置prompts中的對(duì)象元素,除上面介紹的這些類(lèi)型外,您還可以通過(guò)查看inquirer.js的文檔來(lái)獲取更多內(nèi)容。

交互式配置過(guò)程中用戶(hù)做出的所有選擇和輸入都會(huì)被保存到this.props對(duì)象中,可以通過(guò)訪(fǎng)問(wèn)this.props.isIncludeBootstrap屬性來(lái)確定是否需要安裝Bootstrap。

message屬性保存是每一條安裝提示的提示信息。
name屬性是最重要的屬性之一,它作為key用來(lái)訪(fǎng)問(wèn)用戶(hù)的選擇結(jié)果。
default屬性保存的是默認(rèn)值,即當(dāng)用戶(hù)跳過(guò)當(dāng)前安裝提示的時(shí)候,name對(duì)應(yīng)的value值將使用default中保存的默認(rèn)值來(lái)設(shè)置。

② 寫(xiě)入文件

寫(xiě)入文件這個(gè)過(guò)程會(huì)把項(xiàng)目模板復(fù)制到指定的目錄中,如果是固定文件那么就直接拷貝,如果是靈活文件那么還需要把某些參數(shù)傳遞給指定的模板文件。這個(gè)過(guò)程在代碼中由writing() 函數(shù)體現(xiàn),另外系統(tǒng)還提供了兩個(gè)函數(shù)(fs.copyTpl和fs.copy)用來(lái)執(zhí)行具體的操作。

writing() {
    mkdirp("build");          //創(chuàng)建build文件目錄
    mkdirp("dist");           //創(chuàng)建dist文件目錄
    mkdirp("src/template");   //創(chuàng)建src/template文件目錄

    //傳遞參數(shù)this.props.appName渲染index.html文件
    //把項(xiàng)目模板中的index.html文件復(fù)制到新項(xiàng)目的src路徑下
    this.fs.copyTpl(
      this.templatePath('index.html'),
      this.destinationPath('src/index.html'),
      {appName: this.props.appName}
    );

    //把項(xiàng)目模板中的style.css文件復(fù)制到新項(xiàng)目的src/css路徑下
    this.fs.copy(
      this.templatePath('css/style.css'),
      this.destinationPath('src/css/style.css')
    );

    //......
}

fs.copy方法會(huì)把指定文件復(fù)制到目標(biāo)路徑。
fs.copyTpl方法會(huì)先傳遞參數(shù)給模板文件,經(jīng)過(guò)模板引擎處理后再進(jìn)行復(fù)制。

③ 下載和安裝依賴(lài)

這個(gè)階段做的事情非常簡(jiǎn)單,就是調(diào)用npm或者是bower來(lái)下載并安裝依賴(lài)和相關(guān)的node模塊。Yeoman提供了幾個(gè)對(duì)應(yīng)的方法來(lái)處理這個(gè)過(guò)程。

this.npmInstall()
使用Npm來(lái)安裝package.json中的依賴(lài)和模塊,相當(dāng)于在終端中輸入$ npm install指令。

this.bowerInstall()
使用Bower來(lái)安裝bower.json中的依賴(lài)和模塊,相當(dāng)于在終端中輸入$ bower install指令。

this.installDependencies()
調(diào)用Bower和Npm并且安裝package.json和bower.json中依賴(lài)的所有模塊,相當(dāng)于先后調(diào)用了npmInstall和bowerInstall方法。

最后,為了幫助更好的理解Yeoman組裝流程的三個(gè)階段,給出下面的示意圖。


?著作權(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)容