🛠怎样疾速开辟一个本身的项目脚手架?

弁言

下面是一个运用脚手架来初始化项目的典范例子。

《🛠怎样疾速开辟一个本身的项目脚手架?》

跟着前端工程化的理念不断深入,越来越多的人挑选运用脚手架来从零到一搭建本身的项目。个中人人最熟习的就是create-react-appvue-cli,它们能够协助我们初始化设置、天生项目构造、自动装置依靠,末了我们一行指令即可运转项目最先开辟,或许举行项目构建(build)。

这些脚手架供应的都是广泛意义上的最好实践,然则我在开辟中发明,跟着营业的不断生长,必然会涌现须要针对营业开辟的实际状况来举行调解。比方:

  • 经由过程调解插件与设置完成 Webpack 打包机能优化后
  • 删除脚手架构建出来的部份功用
  • 项目架构调解
  • 融会公司开辟东西
  • ……

总而言之,跟着营业生长,我们每每会沉淀出一套更“个性化”的营业计划。这时刻我们最直接的做法就是开辟出一个该计划的脚手架来,以便以后能复用这些最好实践与计划。

1. 脚手架怎样事情?

功用雄厚水平差别的脚手架,庞杂水平天然也不太一样。然则整体来讲,脚手架的事情大致都邑包含几个步骤:

  • 初始化,平常在这个时刻会举行环境的初始化,做一些前置的搜检
  • 用户输入,比方用 vue-cli 的时刻,它会“问”你许多设置选项
  • 天生设置文件
  • 天生项目构造,这是候能够会运用一个项目模版
  • 装置依靠
  • 清算、校验等扫尾事情

另外,你还须要处置惩罚敕令行行动等。每每我们只是想轻量级、疾速得建立一个特定场景的脚手架(没必要想vue-cli那末完全)。而关于想要疾速建立一个脚手架,实在我们没必要完全从零最先。Yeoman 就是一个能够帮我们疾速建立脚手架的东西。

《🛠怎样疾速开辟一个本身的项目脚手架?》

能够许多同砚都不太相识,那末先简朴引见一下 Yeoman 是什么,又是怎样帮我们来简化脚手架搭建的。

起首,Yeoman 能够简朴理解为是一个脚手架的运转框架,它定义了一个脚手架在运转过程当中所要阅历的各个阶段(比方我们上面说的,能够会先读取用户输入,然后天生项目文件,末了装置依靠),我们所须要的就是在性命周期的对应阶段,添补对应的操纵代码即可。而我们添补代码的处所,在 Yeoman 中叫做 generator,物如其名,Yeoman 经由过程挪用某个 generator 即可天生(generate)对应的项目。

假如你还不是迥殊清晰它们之间的关联,那末能够举个小例子:

将脚手架开辟类比为前端组件开辟,Yeoman 的角色就像是 React,是一个框架,尤其是定义了组件的性命周期函数;而 generator 类似于你写的一个 React 营业组件,根据 React 的划定规矩在各个性命周期中填代码即可。

Yeoman 内置的“性命周期”要领实行递次以下:

  1. initializing
  2. prompting
  3. default
  4. writing
  5. conflicts
  6. install
  7. end

个中 default 阶段会实行你自定义地种种要领。

同时,Yeoman 还集成了脚手架开辟中经常使用的各种东西,像是文件操纵、模版添补、终端上的用户交互功用,敕令行等,而且封装成了简朴易用的要领。

经由过程这两点,Yeoman 能够帮我们大大范例与简化脚手架的开辟。

2. 开辟一个本身的脚手架

相识了一些脚手架的事情体式格局与 Yeoman 的基本概念,我们就能够来建立一个属于本身的脚手架。作为例子,这个脚手架的功用很简朴,它会为我们建立一个最简版的基于 Webpack 的前端项目。终究脚手架运用结果以下:

《🛠怎样疾速开辟一个本身的项目脚手架?》

2.1. 预备一个项目模版

脚手架是协助我们疾速天生一套既定的项目架构、文件、设置,而最常见的做法的就是先写好一套项目框架模版,比及脚手架要天生项目时,则将这套模版拷贝到目的目次下。这里实在会有两个小点须要关注。

第一个是模版内变量的添补。

在模版中的某些文件内容能够会须要天生时动态替代,比方根据用户在终端中输入的内容,动态添补package.json中的name值。而 Yeoman 内置了 ejs 作为模版引擎,能够直接运用。

第二个就是模版的安排位置。

一种是直接放在当地,也就是直接放到 generator 中,追随 generator 一同下载,每次装置都是当地拷贝,速率很快,然则项目模版本身的更新晋级比较难题,须要提醒用户晋级 generator。

另一种则是将模版文件放到某个服务器上,每次运用脚手架初始化时经由过程某个地点动态下载,想要更新晋级模版会很轻易,通常会挑选托管在 github 上。

关于第二个模版安排究竟是挑选在当地好,照样远端好,实在照样根据你个人的营业场景而定,在差别的场景的限定的需求差别,我之前既写过模版放在当地的脚手架(即和脚手架一同经由过程 npm 装置),也写过托管在 git 堆栈上的这类体式格局。

回到我们「建立一个最简版的基于 Webpack 的前端项目」的目的,我预备了一个项目模版,以后就会用它来作为脚手架天生的项目内容。

2.2. 建立 generator(yeoman-generator)

建立 Yeoman 的 generator 须要遵照它的划定规矩。

起首是 generator 定名划定规矩。须要以generator打头,横线衔接。比方你想建立一个名为 webpack-kickoff 的 generator,包名须要取成 generator-webpack-kickoff

如许,当你经由过程

npm i -g yo

装置完 Yeoman 的 CLI 后,就能够经由过程yo敕令来运用 generator 来启动脚手架:

yo webpack-kickoff

这里的 webpack-kickoff 就是包名里generator-背面的内容,Yeoman 会按这个划定规矩去全局找相匹配的包。

其次,根据 Yeoman 的范例,默许状况下你须要在项目(即 generator)的generators/app/目次下建立index.js,在个中写入你的脚手架事情流程。固然,也能够经由过程修正设置来扩大或转变这个划定规矩

另外,你建立的 generator 类须要继承 yeoman-generator。所以我们会在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 中的一个同名要领,你须要的就是在子类中重写后所需的对应要领。default阶段则会实行用户定义的类要领。

比方,你想在初始化时打印下版本信息,能够这么做:

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 类中添补种种要领的完成细节了。

2.3. 处置惩罚用户交互

脚手架事情中平常都邑有一些用户自定义的内容,比方建立的项目目次名,或许是不是启用某个设置等。这些交互平常都是经由过程交互式的终端来完成的,比方下面这个功用。

《🛠怎样疾速开辟一个本身的项目脚手架?》

能够运用 Inquirer.js 来完成。而 Yeoman 已帮我们集成好了,直接在 generator 里挪用 this.prompt 即可。

在用户交互部份的需求也比较简朴,只须要讯问用户所需建立的项目目次名即可,随后也会作为项目名。根据 Yeoman 的流程范例,我们将该部份代码写在 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();
        });
    }
    // ……
}

注重,因为用户交互是一个“异步”的行动,为了让后续性命周期要领在“异步”完成后再继承实行,须要挪用this.async()要领来关照要领为异步要领,防止递次实行完同步代码后直接挪用下一阶段的性命周期要领。挪用后会返回一个函数,实行函数表明该阶段完成。

2.4. 下载模版

正如2.1.中所述,我们挑选将模版托管在 github 上,因此在天生详细项目代码前,须要将响应的文件下载下来。能够运用 download-git-repo 来疾速完成。

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()要领,该要领重要用于猎取途径。不传参时返回当前敕令行运转的目次;假如收到多个参数,则会举行途径的拼接。

另外,假如你仔细的话,会发明_downloadTemplate()要领带了一个下划线前缀。这是 Yeoman 中的一个商定:Yeoman 实行递次中有个default阶段,该阶段包含了一切用户自定义的类要领。然则,假如某些要领你不愿望被 Yeoman 的脚手架流程直接挪用,而是作为东西要领供应给其他类要领,则能够增加一个下划线前缀。关于这类定名的要领,则会在default阶段被疏忽。

2.5. 模版文件拷贝

项目模版下载终了后,下面就能够将相干的目次、文件拷贝到目的文件夹中。这些都能够在writing阶段操纵。此时须要遍历模版中的一切目次,将一切文件举行模版添补与拷贝。遍历体式格局以下:

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()要领,它支撑文件拷贝,同时还能够指定响应的模版参数,另外,假如涌现重名掩盖状况会在控制台自动输出响应信息。

末了,把下载与拷贝整合起来即可完成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. 依靠装置

到现在,脚手架已能够帮我们把项目开辟所需的设置、目次构造、依靠清单都预备好了。这时刻能够进一步帮开辟人员将依靠装置终了,如许脚手架建立项目完成后,开辟人员就能够直接开辟了。

Yeoman 也供应了this.npmInstall()来要领来完成 npm 包的装置:

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

到这里,脚手架的中心功用就完成了。已能够运用我们的这个 generator 来疾速建立项目了。很简朴吧~

完全的代码能够参考
generator-webpack-kickoff

3. 运用脚手架 🚀

运用该脚手架会同时须要 Yeoman 与上述我们刚建立的 yeoman-generator。固然,有一个条件,Yeoman 与这个 generator 都须要全局装置。全局装置 Yeoman 没啥有题目(npm install -g yo),处置惩罚 generator-webpack-kickoff 的话能够有几种体式格局:

  1. 直接宣布到 npm,然后一般全局装置
  2. 直接手动拷贝到全局 node_modules
  3. 运用npm link将某个目次链接到全局

根据2.2.节的内容,我们的 generator 名称为 generator-webpack-kickoff。因为我的包已发到 npm 上了,所以要运用该脚手架能够运转以下指令:

# 装置一次即可
npm i -g yo
npm i -g generator-webpack-kickoff

# 启动脚手架
yo webpack-kickoff

4. 优化

从上文这个例子能够看出,完成一个脚手架异常简朴。例子虽小,但也包含了脚手架开辟的重要部份。固然,这篇文章为了简化,省略了一些“优化”功用。比方

  • 项目目次的重名检测,天生项目时,搜检是不是目次已存在,并提醒正告
  • 项目模版的缓存。虽然我们运用 github 托管体式格局,但也能够斟酌没必要每次都从新下载,能够放一份当地缓存,然后天天或每周更新;
  • CLI 的优化。完全版里还会包含一些更雄厚的 CLI 运用,比方我们在动图中看到的 loading 结果、头尾显现的信息面板等。这些东西包含

    • ora,用于建立 spinner,也就是上面所说的 loading 结果
    • chalk,用于打印彩色的信息
    • update-notifier,用于搜检包的线上版本与当地版本
    • beeper,能够“哔”一下你,比方失足的时刻
    • boxen,建立头尾的谁人小“面板”
  • 版本搜检。上面提到能够用 update-notifier 来搜检版本。所以能够在 initializing 阶段举行版本搜检,提醒用户更新脚手架。

末了

本文经由过程一个简朴的例子来通知人人怎样运用 Yeoman 疾速建立脚手架。要相识更多 yeoman-generator 的开辟与运用,能够参考社区里人人写的各种 generator。现在在 npm 上有凌驾 8000 个 yeoman-generator,或许就会有你的菜。

文中完成的代码请检察
generator-webpack-kickoff

    原文作者:alienzhou
    原文地址: https://segmentfault.com/a/1190000019219648
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞