你是如何开始一个我的项目呢?是基于以后技术栈提供的脚手架还是从 npm init 开始呢?

以前我没得选,必须面向搜索引擎。基于 webpack 或 rollup 来一步步构建我的项目,在开发过程中还有可能产生很多谬误。但当初我只想专一于以后业务,筛选适合的脚手架之后迅速构建本人的我的项目,这样的话,就能够把大量维护性的工作交给开源作者。

当然,出名的脚手架工具(Vue CLI,Umi,Vite 等)自不必说,这里我举荐几个棘手的工具。

  • microbundle-crl 专一于 React 组件的构建
  • tsdx 专一于 TypeScript 库的构建
  • crateApp 依据以后选项配置生成我的项目包 (多个根底构建工具 Webpack,Parcel,Snowpack)

但无论是哪一个样板库或者脚手架,都不会完全符合以后业务的需要,开发者须要基于以后的样板进行批改。比如说须要在我的项目中要增加开源协定,批改项目名称,以及为我的项目增加不同的依赖。

从构建来说,目前有两个问题:

  • 大量重复性操作

如果生成我的项目的工作频率很高的话,例如一周写一个业务性组件。尽管每次在我的项目中要增加开源协定,批改项目名称,增加特定依赖都是一些小活,但频率高起来也是一件麻烦的事件。

  • 底层依赖无奈间接降级

如果开发者批改了以后样板,那么脚手架呈现破坏性更新时候就无奈间接降级(这种问题当然也比拟少)。尽管开发过程中会记录一些批改。但随着工夫的偏移,开发者不会确切晓得须要编辑或删除哪些文件能力使降级后的我的项目失常工作。

话不多说,咱们来看一看工具 Preset 是如何解决这一系列的问题的。

应用 Preset

首先建设一个我的项目,以 vite 为例子,package.json 如下所示

{  "name": "vite-preset",  "version": "0.0.1",  "author": "jump-jump",  "license": "MIT",  "preset": "preset.ts",  "prettier": {    "printWidth": 80,    "tabWidth": 2,    "trailingComma": "all",    "singleQuote": true,    "arrowParens": "always",    "useTabs": false,    "semi": true  },  "devDependencies": {    "apply": "^0.2.15"  }}

执行上面的操作,咱们会的到 my-vue-app 文件。

# npm 6.xnpm init @vitejs/app my-vue-app --template vue

拿到了以后命令生成的后果之后咱们把以后生成文件拷贝到 vite-preset 根目录下的 templates 中(即 templates/vite ) 文件夹下。

而后咱们通过 preset.ts(对应 package.json 中的 preset": "preset.ts" ) 编写 Preset 命令。

import {Preset, color} from 'apply'// 以后编写我的项目的名称,会在控制台中展现Preset.setName('jump-jump vite preset')// 从 templates/vite 中提取所有文件,并携带以 . 结尾的文件 如 .gitignore 等    Preset.extract('vite')  .withDots()// 更新以后 package.json 文件,增加依赖 tailwindcss,移除依赖 sassPreset.editNodePackages()  .add('tailwindcss', '^2.0')  .remove('sass')// 装置所有依赖Preset.installDependencies()// 运行提醒Preset.instruct([  `Run ${color.magenta('yarn dev')} to start development.`,]).withHeading("What's next?");

实现了!

咱们能够来试试成果,我寻找一个适合的文件夹,而后运行指令:

// 解析 vite-preset 我的项目npx apply C:\re-search\vite-preset

之前保留的 vite 样板文件夹被解压到以后文件夹下,此时依赖也被替换掉了,当然,咱们也能够指定文件夹下装置,如

npx apply C:\re-search\vite-preset vite-demo

vite 样板板被解压到以后文件夹下的 vite-demo 文件夹中去了。

咱们岂但能够应用本地门路,当然,咱们也能够应用 github 门路。如:

npx apply git@github.com:useName/projectName.git// 等同于npx apply username/projectName

目前来看,成果勉强还能够,实际上咱们可能操作的远不止上述展现的,那么我开始一一解读一下 Preset 的各个命令。

玩转 Preset

setName 工程名设置

正如下面图片展现的那样,该命令设置胜利后会显示在控制台中。

Preset.setName('jump-jump preset')

setTemplateDirectory 样板目录设置

此操作会批改提取根门路,不应用则默认选项为 templates。

// 文件提取根门路被改为了 stubs 而不是 templatesPreset.setTemplateDirectory('stubs');

extract 文件夹提取

此操作容许将文件从预设的样板目录提取到目标目录。在大多数状况下,这个命令曾经能够解决绝大部分问题。

// 以后会提取整个根样板 即 templates 或者 stubsPreset.extract();// 以后会提取 templates/vite 文件夹到根目录Preset.extract('vite'); // 先提取 templates/presonal,而后提取  templates/presonal 文件夹Preset.extract('vite'); Preset.extract('presonal'); // 等同于 Preset.extract('vite')Preset.extract().from('vite'); // 提取到根门路下的 config 文件夹Preset.extract().to('config');// 遇到文件已存在的场景 [ask 询问, override 笼罩, skip 跳过]// 留神:如果询问后回绝,将会停止以后进度Preset.extract().whenConflict('ask');// 在业务中,咱们往往这样应用,是否以后式交互模式?// 是则询问,否则笼罩Preset.extract().whenConflict(Preset.isInteractive() ? 'ask' : 'override')// 如果没有此选项,以 .结尾的文件(如 .gitignore .vscode) 文件将被疏忽。// 留神:倡议在样板中应用 .dotfile 结尾。 // 如: gitignore.dotfile => .gitignorePreset.extract().withDots();

editJson 编辑 JSON 文件

应用 editJson 能够笼罩和删除 JSON 文件中的内容。

// 编辑 package.json 深度拷贝数据Preset.editJson('package.json')  .merge({    devDependencies: {      tailwindcss: '^2.0'    }  });// 编辑 package.json 删除 开发依赖中的 bootstrap 和 sass-loaderPreset.editJson('package.json')  .delete([    'devDependencies.bootstrap',    'devDependencies.sass-loader'  ]);

当然,Preset 为 node 我的项目提供了简略的管制项 editNodePackages 。

Preset.editNodePackages()  // 会删除 bootstrap   // 无论是 dependencies, devDependencies and peerDependencies  .remove('bootstrap')  // 增加 dependencies    .add('xxx', '^2.3.0')  // 增加 devDependencies  .addDev('xxx', '^2.3.0')  // 增加 peerDependencies  .addPeer('xxx', '^2.3.0')  // 设置键值对           .set('license', 'MIT')  .set('author.name', 'jump-jump')

installDependencies 装置依赖

在搭建我的项目的同时咱们须要装置依赖,这里通过 installDependencies 实现。

// 装置依赖,默认为 node,也反对 PHPPreset.installDependencies();// 询问用户是否装置Preset.installDependencies('php')  .ifUserApproves();

instruct 疏导

该命令能够增加标语来一步步疏导用户进行下一步操作,还能够增加各种色彩。

import { Preset, color } from `apply`;Preset.instruct([  `Run ${color.magenta('yarn dev')} to start development.`,]).withHeading("What's next?");

options 设置配置

开发者想要增加多个样板,是否须要开发多个我的项目呢?答案是否定的,咱们通过 options 获取参数即可。

npx apply C:\re-search\vite-preset vite-demo --useEsbuild

以后数据会被设置到 Preset.options 中。

// 默认设置 useEsbuild 为 truePreset.option('useEsbuild', true);// 默认设置 use 为字符串 esbuildPreset.option('use', 'esbuild');// 如果配置项 useEsbuild 为 ture 解压 templates/esbuild// 也有 ifNotOption 取反Preset.extract('esbuld').ifOption('useEsbuild');// use 严格相等于 esbuild 解压 templates/esbuildPreset.extract('esbuld').ifOptionEquals('use','esbuild');Preset.extract((preset) => {  // 如果配置项 useEsbuild 为 ture 解压 templates/esbuild  if (preset.options.useEsbuild) {    return 'esbuild';  }  return 'vite';});

咱们能够在执行 npx 是增加配置项,如下所示

标记价值观
--auth{ auth: true }
--no-auth{ auth: false }
--mode auth{ mode: 'auth' }

input confirm 交互设置

Preset 设置配置项很棒。但就用户体验来说,通过交互设置则更好。这样咱们无需记忆各个配置项。通过人机交互来输出数据,以后数据会被增加到 Preset.prompt 中。

// 第一个参数将传入 Preset.promptPreset.input('projectName', 'What is your project name?');// 第三个是可选的上下文字符串,用于定义提醒的默认值。// 如果预设是在非交互模式下启动的,它将被应用。Preset.input('projectName', 'What is your project name?', 'jump project');// 编辑脚本Preset.editNodePackages()    .set('name', Preset.prompt.projectName)    .set('license', 'MIT')    .set('author.name', 'jump-jump')// 第一个参数将传入 Preset.prompt// 第三个是可选的上下文布尔值,用于定义提醒的默认值。// 如果预设是在非交互模式下启动的,它将被应用。Preset.confirm('useEsLint', 'Install ESLint?', true);

delete edit 批改文件

删除生成文件夹中的文件间接应用 delete

Preset.delete('resources/sass');

编辑文件

// 替换文本字符串Preset.edit('config/app.php').update((content) => {    return content.replace('en_US', 'fr_FR');});// 替换 README.md 文件中的字符串 {{ projectName }}// {{prejectName}} => prompts.name ?? 'Preset'Preset.edit('README.md').replaceVariables(({ prompts }) => ({    projectName: prompts.name ?? 'Preset',}));

execute 执行 bash 命令

如果之前的命令都不能满足你,那只能执行 bash 命令了吧!Preset 也提供了这个性能,联合 hooks 可增加各种参数。

// 利用钩子将数据存储到 context 中Preset.hook(({ context, args, options }) => {    const allowedOptions = ['auth', 'extra'];    context.presetName = args[2];    context.options = Object.keys(options)        .filter((option) => allowedOptions.includes(option))        .map((option) => `--${option}`);});// 第一个参数是程序或者命令名称,前面是参数,从 context 中读取Preset.execute('php')    .withArguments(({ context }) => [        'artisan',         'ui',         context.presetName,         ...context.options    ])    // 批改以后题目,以后执行时会在控制台打印如下字符串,而不是默认字符串    .withTitle(({ context }) => `Applying ${context.presetName}`);

进一步思考

通过对 Preset 库的学习,咱们能够看到 Preset 具备十分不错的设计格调与弱小的性能。Preset 没有从底层构建我的项目,反而是帮忙开发者通过一些命令衍生出本人的工具,同时还能够记录开发者绝大部分对于我的项目的批改。

增量思维

在应用 Preset 构建样板的过程中,开发者没有对本来的样板进行批改,这样使得开发者降级原始样本变得非常简单。在构建 Preset 我的项目过程其实也就是批改样板增量。

咱们应该进一步在开发中应用增量思维,在这里「增量」这个概念的对立面是「全量」。增量会依据比对以后与过来之间的差别,只关注差异性所带来的影响。

增量有很多理论的意义,咱们能够看到:

  • 前后端交互时候前端只提交变动的数据
  • rsync 增量同步文件
  • 网盘增量上传文件
  • 数据库增量备份
  • 增量代码查看、构建、打包

链式调用

随着前端框架带来了数据驱动,JQuery 逐步退出历史舞台(Bootstrap 5 去除了 JQuery)。ES 一直降级也给与用户大量帮忙,用户无需自行构建对象进行链式调用了。但这并不象征链式调用不重要。

因为链式调用能够优雅的记录时序,开发者能够依赖以后调用来进行剖析。

大多数工具都会提供不同的配置项。此时咱们能够间接传入配置项来应用工具。

如果以后操作有时序性(先后顺序决定最终后果),构建对象进行链式调用则更无效。当然你能够说咱们增加一个数组配置来决定程序。但面对简单的程序,优良的函数命名能够让用户更简略的了解代码。

又如果,咱们在面对简单的图形构造时,构建对象来进行节点的抉择与操作肯定会更加简略。如果有需要,咱们甚至须要依据链式调用来生成 sql 语句。

激励一下

如果你感觉这篇文章不错,心愿能够给与我一些激励,在我的 github 博客下帮忙 star 一下。

博客地址

参考资料

Preset

microbundle-crl

tsdx

crateApp