成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

AnthonyHan / 3418人閱讀

引言

下面是一個(gè)使用腳手架來(lái)初始化項(xiàng)目的典型例子。

隨著前端工程化的理念不斷深入,越來(lái)越多的人選擇使用腳手架來(lái)從零到一搭建自己的項(xiàng)目。其中大家最熟悉的就是create-react-appvue-cli,它們可以幫助我們初始化配置、生成項(xiàng)目結(jié)構(gòu)、自動(dòng)安裝依賴,最后我們一行指令即可運(yùn)行項(xiàng)目開始開發(fā),或者進(jìn)行項(xiàng)目構(gòu)建(build)。

這些腳手架提供的都是普遍意義上的最佳實(shí)踐,但是我在開發(fā)中發(fā)現(xiàn),隨著業(yè)務(wù)的不斷發(fā)展,必然會(huì)出現(xiàn)需要針對(duì)業(yè)務(wù)開發(fā)的實(shí)際情況來(lái)進(jìn)行調(diào)整。例如:

通過(guò)調(diào)整插件與配置實(shí)現(xiàn) Webpack 打包性能優(yōu)化后

刪除腳手架構(gòu)建出來(lái)的部分功能

項(xiàng)目架構(gòu)調(diào)整

融合公司開發(fā)工具

……

總而言之,隨著業(yè)務(wù)發(fā)展,我們往往會(huì)沉淀出一套更“個(gè)性化”的業(yè)務(wù)方案。這時(shí)候我們最直接的做法就是開發(fā)出一個(gè)該方案的腳手架來(lái),以便今后能復(fù)用這些最佳實(shí)踐與方案。

1. 腳手架怎么工作?

功能豐富程度不同的腳手架,復(fù)雜程度自然也不太一樣。但是總體來(lái)說(shuō),腳手架的工作大體都會(huì)包含幾個(gè)步驟:

初始化,一般在這個(gè)時(shí)候會(huì)進(jìn)行環(huán)境的初始化,做一些前置的檢查

用戶輸入,例如用 vue-cli 的時(shí)候,它會(huì)“問(wèn)”你很多配置選項(xiàng)

生成配置文件

生成項(xiàng)目結(jié)構(gòu),這是候可能會(huì)使用一個(gè)項(xiàng)目模版

安裝依賴

清理、校驗(yàn)等收尾工作

此外,你還需要處理命令行行為等。往往我們只是想輕量級(jí)、快速得創(chuàng)建一個(gè)特定場(chǎng)景的腳手架(不用想vue-cli那么完備)。而對(duì)于想要快速創(chuàng)建一個(gè)腳手架,其實(shí)我們不用完全從零開始。Yeoman 就是一個(gè)可以幫我們快速創(chuàng)建腳手架的工具。

可能很多同學(xué)都不太了解,那么先簡(jiǎn)單介紹一下 Yeoman 是什么,又是如何幫我們來(lái)簡(jiǎn)化腳手架搭建的。

首先,Yeoman 可以簡(jiǎn)單理解為是一個(gè)腳手架的運(yùn)行框架,它定義了一個(gè)腳手架在運(yùn)行過(guò)程中所要經(jīng)歷的各個(gè)階段(例如我們上面說(shuō)的,可能會(huì)先讀取用戶輸入,然后生成項(xiàng)目文件,最后安裝依賴),我們所需要的就是在生命周期的對(duì)應(yīng)階段,填充對(duì)應(yīng)的操作代碼即可。而我們填充代碼的地方,在 Yeoman 中叫做 generator,物如其名,Yeoman 通過(guò)調(diào)用某個(gè) generator 即可生成(generate)對(duì)應(yīng)的項(xiàng)目。

如果你還不是特別清楚它們之間的關(guān)系,那么可以舉個(gè)小例子:

將腳手架開發(fā)類比為前端組件開發(fā),Yeoman 的角色就像是 React,是一個(gè)框架,尤其是定義了組件的生命周期函數(shù);而 generator 類似于你寫的一個(gè) React 業(yè)務(wù)組件,根據(jù) React 的規(guī)則在各個(gè)生命周期中填代碼即可。

Yeoman 內(nèi)置的“生命周期”方法執(zhí)行順序如下:

initializing

prompting

default

writing

conflicts

install

end

其中 default 階段會(huì)執(zhí)行你自定義地各種方法。

同時(shí),Yeoman 還集成了腳手架開發(fā)中常用的各類工具,像是文件操作、模版填充、終端上的用戶交互功能,命令行等,并且封裝成了簡(jiǎn)單易用的方法。

通過(guò)這兩點(diǎn),Yeoman 可以幫我們大大規(guī)范與簡(jiǎn)化腳手架的開發(fā)。

2. 開發(fā)一個(gè)自己的腳手架

了解了一些腳手架的工作方式與 Yeoman 的基本概念,咱們就可以來(lái)創(chuàng)建一個(gè)屬于自己的腳手架。作為例子,這個(gè)腳手架的功能很簡(jiǎn)單,它會(huì)為我們創(chuàng)建一個(gè)最簡(jiǎn)版的基于 Webpack 的前端項(xiàng)目。最終腳手架使用效果如下:

2.1. 準(zhǔn)備一個(gè)項(xiàng)目模版

腳手架是幫助我們快速生成一套既定的項(xiàng)目架構(gòu)、文件、配置,而最常見的做法的就是先寫好一套項(xiàng)目框架模版,等到腳手架要生成項(xiàng)目時(shí),則將這套模版拷貝到目標(biāo)目錄下。這里其實(shí)會(huì)有兩個(gè)小點(diǎn)需要關(guān)注。

第一個(gè)是模版內(nèi)變量的填充。

在模版中的某些文件內(nèi)容可能會(huì)需要生成時(shí)動(dòng)態(tài)替換,例如根據(jù)用戶在終端中輸入的內(nèi)容,動(dòng)態(tài)填充package.json中的name值。而 Yeoman 內(nèi)置了 ejs 作為模版引擎,可以直接使用。

第二個(gè)就是模版的放置位置。

一種是直接放在本地,也就是直接放到 generator 中,跟隨 generator 一起下載,每次安裝都是本地拷貝,速度很快,但是項(xiàng)目模版自身的更新升級(jí)比較困難,需要提示用戶升級(jí) generator。

另一種則是將模版文件放到某個(gè)服務(wù)器上,每次使用腳手架初始化時(shí)通過(guò)某個(gè)地址動(dòng)態(tài)下載,想要更新升級(jí)模版會(huì)很方便,通常會(huì)選擇托管在 github 上。

關(guān)于第二個(gè)模版放置究竟是選擇在本地好,還是遠(yuǎn)端好,其實(shí)還是依據(jù)你個(gè)人的業(yè)務(wù)場(chǎng)景而定,在不同的場(chǎng)景的限制的需求不同,我之前既寫過(guò)模版放在本地的腳手架(即和腳手架一起通過(guò) npm 安裝),也寫過(guò)托管在 git 倉(cāng)庫(kù)上的這種方式。

回到我們「創(chuàng)建一個(gè)最簡(jiǎn)版的基于 Webpack 的前端項(xiàng)目」的目標(biāo),我準(zhǔn)備了一個(gè)項(xiàng)目模版,之后就會(huì)用它來(lái)作為腳手架生成的項(xiàng)目?jī)?nèi)容。

2.2. 創(chuàng)建 generator(yeoman-generator)

創(chuàng)建 Yeoman 的 generator 需要遵循它的規(guī)則。

首先是 generator 命名規(guī)則。需要以generator打頭,橫線連接。例如你想創(chuàng)建一個(gè)名為 webpack-kickoff 的 generator,包名需要取成 generator-webpack-kickoff。

這樣,當(dāng)你通過(guò)

npm i -g yo

安裝完 Yeoman 的 CLI 后,就可以通過(guò)yo命令來(lái)使用 generator 來(lái)啟動(dòng)腳手架:

yo webpack-kickoff

這里的 webpack-kickoff 就是包名里generator-后面的內(nèi)容,Yeoman 會(huì)按這個(gè)規(guī)則去全局找相匹配的包。

其次,依據(jù) Yeoman 的規(guī)范,默認(rèn)情況下你需要在項(xiàng)目(即 generator)的generators/app/目錄下創(chuàng)建index.js,在其中寫入你的腳手架工作流程。當(dāng)然,也可以通過(guò)修改配置來(lái)擴(kuò)展或改變這個(gè)規(guī)則。

此外,你創(chuàng)建的 generator 類需要繼承 yeoman-generator。所以我們會(huì)在generators/app/index.js中寫如下代碼:

const Generator = require("yeoman-generator");
class WebpackKickoffGenerator extends Generator {
    constructor(params, opts) {
        super(params, opts);
    }
}
module.exports = WebpackKickoffGenerator;

還記得之前提到的“生命周期”方法么?包括 initializing、prompting、default、writing、conflicts、install 和 end。除了default,其他都代表了 Generator 中的一個(gè)同名方法,你需要的就是在子類中重寫后所需的對(duì)應(yīng)方法。default階段則會(huì)執(zhí)行用戶定義的類方法。

例如,你想在初始化時(shí)打印下版本信息,可以這么做:

const Generator = require("yeoman-generator");
class WebpackKickoffGenerator extends Generator {
    constructor(params, opts) {
        super(params, opts);
    }
    
    initializing() {
        const version = require("../../package.json").version;
        this.log(version);
    }
}
module.exports = WebpackKickoffGenerator;

可見,剩下的工作就是在 WebpackKickoffGenerator 類中填充各種方法的實(shí)現(xiàn)細(xì)節(jié)了。

2.3. 處理用戶交互

腳手架工作中一般都會(huì)有一些用戶自定義的內(nèi)容,例如創(chuàng)建的項(xiàng)目目錄名,或者是否啟用某個(gè)配置等。這些交互一般都是通過(guò)交互式的終端來(lái)實(shí)現(xiàn)的,例如下面這個(gè)功能。

可以使用 Inquirer.js 來(lái)實(shí)現(xiàn)。而 Yeoman 已經(jīng)幫我們集成好了,直接在 generator 里調(diào)用 this.prompt 即可。

在用戶交互部分的需求也比較簡(jiǎn)單,只需要詢問(wèn)用戶所需創(chuàng)建的項(xiàng)目目錄名即可,隨后也會(huì)作為項(xiàng)目名。按照 Yeoman 的流程規(guī)范,我們將該部分代碼寫在 prompting 方法中:

class WebpackKickoffGenerator extends Generator {
    // ……
    prompting() {
        const done = this.async();

        const opts = [{
            type: "input",
            name: "dirName",
            message: "Please enter the directory name for your project:",
            default: "webpack-app",
            validate: dirName => {
                if (dirName.length < 1) {
                    return "??  directory name must not be null!";
                }
                return true;
            }
        }];

        return this.prompt(opts).then(({dirName}) => {
            this.dirName = dirName;
            done();
        });
    }
    // ……
}

注意,由于用戶交互是一個(gè)“異步”的行為,為了讓后續(xù)生命周期方法在“異步”完成后再繼續(xù)執(zhí)行,需要調(diào)用this.async()方法來(lái)通知方法為異步方法,避免順序執(zhí)行完同步代碼后直接調(diào)用下一階段的生命周期方法。調(diào)用后會(huì)返回一個(gè)函數(shù),執(zhí)行函數(shù)表明該階段完成。

2.4. 下載模版

正如2.1.中所述,我們選擇將模版托管在 github 上,因此在生成具體項(xiàng)目代碼前,需要將相應(yīng)的文件下載下來(lái)??梢允褂?download-git-repo 來(lái)快速實(shí)現(xiàn)。

class WebpackKickoffGenerator extends Generator {
    // ……
    _downloadTemplate() {
        return new Promise((resolve, reject) => {
            const dirPath = this.destinationPath(this.dirName, ".tmp");
            download("alienzhou/webpack-kickoff-template", dirPath, err => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve();
            });
        });
    }
    // ……
}

這里我們使用了this.destinationPath()方法,該方法主要用于獲取路徑。不傳參時(shí)返回當(dāng)前命令行運(yùn)行的目錄;如果收到多個(gè)參數(shù),則會(huì)進(jìn)行路徑的拼接。

此外,如果你細(xì)心的話,會(huì)發(fā)現(xiàn)_downloadTemplate()方法帶了一個(gè)下劃線前綴。這是 Yeoman 中的一個(gè)約定:Yeoman 執(zhí)行順序中有個(gè)default階段,該階段包含了所有用戶自定義的類方法。但是,如果某些方法你不希望被 Yeoman 的腳手架流程直接調(diào)用,而是作為工具方法提供給其他類方法,則可以添加一個(gè)下劃線前綴。對(duì)于這種命名的方法,則會(huì)在default階段被忽略。

2.5. 模版文件拷貝

項(xiàng)目模版下載完畢后,下面就可以將相關(guān)的目錄、文件拷貝到目標(biāo)文件夾中。這些都可以在writing階段操作。此時(shí)需要遍歷模版中的所有目錄,將所有文件進(jìn)行模版填充與拷貝。遍歷方式如下:

class WebpackKickoffGenerator extends Generator {
    // ……
    _walk(filePath, templateRoot) {
        if (fs.statSync(filePath).isDirectory()) {
            fs.readdirSync(filePath).forEach(name => {
                this._walk(path.resolve(filePath, name), templateRoot);
            });
            return;
        }

        const relativePath = path.relative(templateRoot, filePath);
        const destination = this.destinationPath(this.dirName, relativePath);
        this.fs.copyTpl(filePath, destination, {
            dirName: this.dirName
        });
    }
    // ……
}

這里使用了this.fs.copyTpl()方法,它支持文件拷貝,同時(shí)還可以指定相應(yīng)的模版參數(shù),此外,如果出現(xiàn)重名覆蓋情況會(huì)在控制臺(tái)自動(dòng)輸出相應(yīng)信息。

最后,把下載與拷貝整合起來(lái)即可完成writing階段。

class WebpackKickoffGenerator extends Generator {
    // ……
    writing() {
        const done = this.async();
        this._downloadTemplate()
            .then(() => {
                const templateRoot = this.destinationPath(this.dirName, ".tmp");
                this._walk(templateRoot, templateRoot);
                fs.removeSync(templateRoot);
                done();
            })
            .catch(err => {
                this.env.error(err);
            });
    }
    // ……
}
2.6. 依賴安裝

到目前,腳手架已經(jīng)可以幫我們把項(xiàng)目開發(fā)所需的配置、目錄結(jié)構(gòu)、依賴清單都準(zhǔn)備好了。這時(shí)候可以進(jìn)一步幫開發(fā)人員將依賴安裝完畢,這樣腳手架創(chuàng)建項(xiàng)目完成后,開發(fā)人員就可以直接開發(fā)了。

Yeoman 也提供了this.npmInstall()來(lái)方法來(lái)實(shí)現(xiàn) npm 包的安裝:

class WebpackKickoffGenerator extends Generator {
    // ……
    install() {
        this.npmInstall("", {}, {
            cwd: this.destinationPath(this.dirName)
        });
    }
    // ……
}

到這里,腳手架的核心功能就完成了。已經(jīng)可以使用咱們的這個(gè) generator 來(lái)快速創(chuàng)建項(xiàng)目了。很簡(jiǎn)單吧~

完整的代碼可以參考 generator-webpack-kickoff。
3. 使用腳手架

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109606.html

相關(guān)文章

  • vue中如何實(shí)現(xiàn)的自定義按鈕

    摘要:在實(shí)際開發(fā)項(xiàng)目中,有時(shí)我們會(huì)用到自定義按鈕因?yàn)橐粋€(gè)項(xiàng)目中,眾多的頁(yè)面,為了統(tǒng)一風(fēng)格,我們會(huì)重復(fù)用到很多相同或相似的按鈕,這時(shí)候,自定義按鈕組件就派上了大用場(chǎng),我們把定義好的按鈕組件導(dǎo)出,在全局引用,就可以在其他組件隨意使用啦,這樣可以大幅度 在實(shí)際開發(fā)項(xiàng)目中,有時(shí)我們會(huì)用到自定義按鈕;因?yàn)橐粋€(gè)項(xiàng)目中,眾多的頁(yè)面,為了統(tǒng)一風(fēng)格,我們會(huì)重復(fù)用到很多相同或相似的按鈕,這時(shí)候,自定義按鈕組件就...

    biaoxiaoduan 評(píng)論0 收藏0
  • JavaScript代碼整潔之道

    摘要:代碼整潔之道整潔的代碼不僅僅是讓人看起來(lái)舒服,更重要的是遵循一些規(guī)范能夠讓你的代碼更容易維護(hù),同時(shí)降低幾率。另外這不是強(qiáng)制的代碼規(guī)范,就像原文中說(shuō)的,。里式替換原則父類和子類應(yīng)該可以被交換使用而不會(huì)出錯(cuò)。注釋好的代碼是自解釋的。 JavaScript代碼整潔之道 整潔的代碼不僅僅是讓人看起來(lái)舒服,更重要的是遵循一些規(guī)范能夠讓你的代碼更容易維護(hù),同時(shí)降低bug幾率。 原文clean-c...

    liaorio 評(píng)論0 收藏0
  • 前端經(jīng)典面試題總結(jié)

    摘要:接著我之前寫的一篇有關(guān)前端面試題的總結(jié),分享幾道比較經(jīng)典的題目第一題考點(diǎn)作用域,運(yùn)算符栗子都會(huì)進(jìn)行運(yùn)算,但是最后之后輸出最后一個(gè)也就是那么其實(shí)就是而且是個(gè)匿名函數(shù),也就是屬于,就輸出第二和第三個(gè)都是類似的,而且作用域是都是輸出最后一個(gè)其實(shí)就 接著我之前寫的一篇有關(guān)前端面試題的總結(jié),分享幾道比較經(jīng)典的題目: 第一題: showImg(https://segmentfault.com/im...

    BlackMass 評(píng)論0 收藏0
  • 私有云那家好-六大私有云廠商詳細(xì)對(duì)比!

    對(duì)比內(nèi)容UCloudStackZStackVMwareQingCloud騰訊TStack華為云Stack優(yōu)勢(shì)總結(jié)?基于公有云自主可控?公有云架構(gòu)私有化部署?輕量化/輕運(yùn)維/易用性好?政府行業(yè)可復(fù)制案例輕量化 IaaS 虛擬化平臺(tái)?輕量化、產(chǎn)品成熟度高?業(yè)內(nèi)好評(píng)度高?功能豐富、交付部署快?中小企業(yè)案例多全套虛擬產(chǎn)品及云平臺(tái)產(chǎn)品?完整生態(tài)鏈、技術(shù)成熟?比較全面且健全的渠道?產(chǎn)品成熟度被市場(chǎng)認(rèn)可,市場(chǎng)占...

    ernest.wang 評(píng)論0 收藏0
  • cross-env使用記錄

    摘要:能跨平臺(tái)地設(shè)置及使用環(huán)境變量讓這一切變得簡(jiǎn)單,不同平臺(tái)使用唯一指令,無(wú)需擔(dān)心跨平臺(tái)問(wèn)題安裝方式改寫使用了環(huán)境變量的常見如在腳本多是里這么配置運(yùn)行,這樣便設(shè)置成功,無(wú)需擔(dān)心跨平臺(tái)問(wèn)題關(guān)于跨平臺(tái)兼容,有幾點(diǎn)注意 cross-env能跨平臺(tái)地設(shè)置及使用環(huán)境變量, cross-env讓這一切變得簡(jiǎn)單,不同平臺(tái)使用唯一指令,無(wú)需擔(dān)心跨平臺(tái)問(wèn)題 1、npm安裝方式 npm i --save-de...

    Michael_Ding 評(píng)論0 收藏0
  • webpack打包插件

    摘要:引入的模塊引入的使用將打包打包的拆分將一部分抽離出來(lái)物理地址拼接優(yōu)化打包速度壓縮代碼,這里使用的是,同樣在的里面添加 const path = require(path); //引入node的path模塊const webpack = require(webpack); //引入的webpack,使用lodashconst HtmlWebpackPlugin = require(ht...

    ChanceWong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<