你是如何開始一個項目呢?是基於當前技術棧提供的腳手架仍是從 npm init 開始呢?php
之前我沒得選,必須面向搜索引擎。基於 webpack 或 rollup 來一步步構建項目,在開發過程當中還有可能發生不少錯誤。但如今我只想專一於當前業務,挑選合適的腳手架以後迅速構建本身的項目,這樣的話,就能夠把大量維護性的工做交給開源做者。css
固然,知名的腳手架工具(Vue CLI,Umi,Vite 等)自沒必要說,這裏我推薦幾個順手的工具。前端
但不管是哪個樣板庫或者腳手架,都不會徹底符合當前業務的需求,開發者須要基於當前的樣板進行修改。好比說須要在項目中要添加開源協議,修改項目名稱,以及爲項目添加不一樣的依賴。vue
從構建來講,目前有兩個問題:node
若是生成項目的工做頻率很高的話,例如一週寫一個業務性組件。雖然每次在項目中要添加開源協議,修改項目名稱,添加特定依賴都是一些小活,但頻率高起來也是一件麻煩的事情。webpack
若是開發者修改了當前樣板,那麼腳手架出現破壞性更新時候就沒法直接升級(這種問題固然也比較少)。雖然開發過程當中會記錄一些修改。但隨着時間的偏移,開發者不會確切知道須要編輯或刪除哪些文件才能使升級後的項目正常工做。git
話很少說,咱們來看一看工具 Preset 是如何解決這一系列的問題的。github
首先創建一個項目,以 vite 爲例子,package.json 以下所示web
{ "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 文件。sql
# npm 6.x npm 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,移除依賴 sass Preset.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('jump-jump preset')
此操做會修改提取根路徑,不使用則默認選項爲 templates。
// 文件提取根路徑被改成了 stubs 而不是 templates Preset.setTemplateDirectory('stubs');
此操做容許將文件從預設的樣板目錄提取到目標目錄。在大多數狀況下,這個命令已經能夠解決絕大部分問題。
// 當前會提取整個根樣板 即 templates 或者 stubs Preset.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 => .gitignore Preset.extract().withDots();
使用 editJson 能夠覆蓋和刪除 JSON 文件中的內容。
// 編輯 package.json 深度拷貝數據 Preset.editJson('package.json') .merge({ devDependencies: { tailwindcss: '^2.0' } }); // 編輯 package.json 刪除 開發依賴中的 bootstrap 和 sass-loader Preset.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 完成。
// 安裝依賴,默認爲 node,也支持 PHP Preset.installDependencies(); // 詢問用戶是否安裝 Preset.installDependencies('php') .ifUserApproves();
該命令能夠添加標語來一步步引導用戶進行下一步操做,還能夠添加各類顏色。
import { Preset, color } from `apply`; Preset.instruct([ `Run ${color.magenta('yarn dev')} to start development.`, ]).withHeading("What's next?");
開發者想要添加多個樣板,是否須要開發多個項目呢?答案是否認的,咱們經過 options 獲取參數便可。
npx apply C:\re-search\vite-preset vite-demo --useEsbuild
當前數據會被設置到 Preset.options 中。
// 默認設置 useEsbuild 爲 true Preset.option('useEsbuild', true); // 默認設置 use 爲字符串 esbuild Preset.option('use', 'esbuild'); // 若是配置項 useEsbuild 爲 ture 解壓 templates/esbuild // 也有 ifNotOption 取反 Preset.extract('esbuld').ifOption('useEsbuild'); // use 嚴格相等於 esbuild 解壓 templates/esbuild Preset.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' } |
Preset 設置配置項很棒。但就用戶體驗來講,經過交互設置則更好。這樣咱們無需記憶各個配置項。經過人機交互來輸入數據,當前數據會被添加到 Preset.prompt 中。
// 第一個參數將傳入 Preset.prompt Preset.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
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', }));
若是以前的命令都不能知足你,那隻能執行 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 項目過程其實也就是修改樣板增量。
咱們應該進一步在開發中使用增量思想,在這裏「增量」這個概念的對立面是「全量」。增量會根據比對當前與過去之間的差別,只關注差別性所帶來的影響。
增量有不少實際的意義,咱們能夠看到:
隨着前端框架帶來了數據驅動,JQuery 逐漸退出歷史舞臺(Bootstrap 5 去除了 JQuery)。ES 不斷升級也給與用戶大量幫助,用戶無需自行構建對象進行鏈式調用了。但這並不意味鏈式調用不重要。
由於鏈式調用能夠優雅的記錄時序,開發者能夠依賴當前調用來進行分析。
大多數工具都會提供不一樣的配置項。此時咱們能夠直接傳入配置項來使用工具。
若是當前操做有時序性(前後順序決定最終結果),構建對象進行鏈式調用則更有效。固然你能夠說咱們添加一個數組配置來決定順序。但面對複雜的順序,優秀的函數命名可讓用戶更簡單的理解代碼。
又若是,咱們在面對複雜的圖形結構時,構建對象來進行節點的選擇與操做必定會更加簡單。若是有需求,咱們甚至須要根據鏈式調用來生成 sql 語句。
若是你以爲這篇文章不錯,但願能夠給與我一些鼓勵,在個人 github 博客下幫忙 star 一下。