<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手把手實(shí)現(xiàn)一個(gè)web代碼模板快速生成CLI工具

          共 8953字,需瀏覽 18分鐘

           ·

          2021-10-19 20:01

          前言

          在上一篇文章中,我們實(shí)現(xiàn)了一個(gè)web工程通用腳手架工具,目的是快速搭建項(xiàng)目。在實(shí)際開發(fā)中,特別是后臺(tái)管理系統(tǒng),有很多相似的代碼實(shí)現(xiàn)。于是乎,我們可以繼續(xù)實(shí)現(xiàn)一個(gè)快速生成web代碼模板的工具,告別復(fù)制/粘貼。

          基本流程

          基本思路其實(shí)很簡單,就是通過命令調(diào)取定義好的模板,然后生成代碼文件:

          創(chuàng)建文件流程.png

          項(xiàng)目結(jié)構(gòu)

          xman-tcli
          ├─?bin
          │??└─?xmant.js
          ├─?command
          │??├─?createFile.js
          │??└─?createManyFiles.js
          ├─?config
          │??└─?fileList.js
          ├─?templates
          │??├─?index.js
          │??├─?js
          │??│??├─?reactClassJSX.js
          │??│??└─?reactFuncJSX.js
          │??└─?ts
          │?????├─?reactClassTSX.js
          │?????├─?reactFuncTS.js
          │?????└─?reactFuncTSX.js
          ├─?utils
          │??└─?index.js
          ├─?.gitignore
          ├─?LICENSE
          ├─?package.json
          └─?README.md

          具體實(shí)現(xiàn)

          很多依賴的用處上一篇文章已經(jīng)提及,所以這篇文章就不會(huì)過多的介紹。

          初始化項(xiàng)目

          可以用 npm init 進(jìn)行創(chuàng)建,也可以根據(jù)下面列出的 package.json 進(jìn)行修改。

          {
          ??"name":?"xman-tcli",
          ??"version":?"1.0.0",
          ??"description":?"web-cli工具,可以快速創(chuàng)建template",
          ??"bin":?{
          ????"xmant":?"bin/xmant.js"
          ??},
          ??"scripts":?{
          ????"test":?"echo?\"Error:?no?test?specified\"?&&?exit?1"
          ??},
          ??"repository":?{
          ????"type":?"git",
          ????"url":?"https://github.com/XmanLin/xman-tcli.git"
          ??},
          ??"keywords":?[
          ????"cli"
          ??],
          ??"author":?"xmanlin",
          ??"license":?"MIT",
          ??"dependencies":?{
          ????"chalk":?"^4.1.2",
          ????"clui":?"^0.3.6",
          ????"commander":?"^8.2.0",
          ????"figlet":?"^1.5.2",
          ????"handlebars":?"^4.7.7",
          ????"inquirer":?"^8.1.5",
          ????"update-notifier":?"^5.1.0"
          ??}
          }

          編寫bin/xman.js

          #!/usr/bin/env?node

          const?{?program?}?=?require('commander');

          program
          ?.version(require('../package').version,?'-v,?--version');
          ?
          program.parse(process.argv);?//?這里是必要的

          if?(!program.args.length)?{
          ?program.help();
          }

          在當(dāng)前xmant-cli目錄下,執(zhí)行 npm link 后,就可以在本地對腳手架工具進(jìn)行調(diào)試了。

          然后在當(dāng)前目錄下執(zhí)行:

          xmant?-v
          版本顯示.png

          說明工程初步搭建成功。

          通過命令快速創(chuàng)建單個(gè)代碼模板

          編寫模板

          代碼模板可以根據(jù)實(shí)際項(xiàng)目進(jìn)行抽離,這里利用幾個(gè)簡單的模板作為例子。

          templates/js/reactClassJSX.js

          ????return?`
          import?*?as?React?from?'react';

          export?class?${className}?extends?React.Component{
          ????constructor(props){
          ????????super(props);

          ????????this.state?=?{}
          ????}

          ????componentDidMount(){

          ????}

          ????render()?{
          ????????return?(
          ????????????

          ????????)
          ????}
          }
          ????`
          ?
          }

          templates/js/reactFuncJSX.js

          module.exports?=?function?(funcName)?{
          ????return?`
          import?React,?{useEffect,?useState}?from?'react';

          const?${funcName}?=?(props)?=>?{
          ????
          ????return?(
          ????????

          ????)
          }

          export?default?${funcName};
          ????`
          ?
          }

          templates/ts/reactClassTSX.js

          module.exports?=?function?(className)?{
          ????return?`
          import?*?as?React?from?'react';

          interface?Props?{}
          interface?State?{}

          export?class?${className}?extends?React.Component{
          ????constructor(props:?Props){
          ????????super(props);

          ????????this.state?=?{}
          ????}

          ????componentDidMount(){

          ????}

          ????render()?{
          ????????return?(
          ????????????

          ????????)
          ????}
          }
          ????`
          ?
          }

          templates/ts/reactFuncTS.js

          module.exports?=?function?(funcName)?{
          ????return?`
          export?const?${funcName}?=?()?=>?{
          ????
          }
          ????`
          ?
          }

          templates/ts/reactFuncTSX.js

          module.exports?=?function?(funcName)?{
          ????return?`
          import?React,?{useEffect,?useState}?from?'react';

          const?${funcName}?=?(props:?any)?=>?{
          ????
          ????useEffect(()?=>?{
          ????????
          ????},[])
          ????
          ????return?(
          ????????

          ????)
          }

          export?default?${funcName};
          ????`
          ?
          }

          模板定義好之后,通過 index.js 統(tǒng)一導(dǎo)出。

          templates/index.js

          const?reactClassJSX?=?require('./js/reactClassJSX');
          const?reactFuncJSX?=?require('./js/reactFuncJSX');
          const?reactClassTSX?=?require('./ts/reactClassTSX');
          const?reactFuncTSX?=?require('./ts/reactFuncTSX');
          const?reactFuncTS?=?require('./ts/reactFuncTS');
          //?命名規(guī)范:name由“-”鏈接,前面為模板名,后面為創(chuàng)建后文件的后綴

          module.exports?=?[
          ????{
          ????????name:?'reactClass-jsx',?src:?reactClassJSX
          ????},
          ????{
          ????????name:?'reactFunc-jsx',?src:?reactFuncJSX
          ????},
          ????{
          ????????name:?'reactClass-tsx',?src:?reactClassTSX
          ????},
          ????{
          ????????name:?'reactFunc-tsx',?src:?reactFuncTSX
          ????},
          ????{
          ????????name:?'reactFunc-ts',?src:?reactFuncTS
          ????}
          ]

          這里的“命名規(guī)范”,目的是為了后面創(chuàng)建文件時(shí)得到相應(yīng)的后綴。

          創(chuàng)建工具函數(shù) utils/index.js:

          module.exports?=?{
          ????getFileSuffix:?(name)?=>?{
          ????????if(typeof?name?===?'string')?{
          ????????????return?name.split('-')[1]
          ????????}
          ????}
          }

          編寫創(chuàng)建文件邏輯

          準(zhǔn)備工作就緒,接下來就是文件創(chuàng)建的邏輯 command/createFile.js:

          //?創(chuàng)建單個(gè)文件
          const?templates?=?require('../templates/index');
          const?chalk?=?require('chalk');
          const?inquirer?=?require('inquirer');
          const?fs?=?require("fs");
          const?utils?=?require('../utils/index');


          module.exports?=?()?=>?{
          ????inquirer.prompt([
          ????????{
          ????????????name:?'templateName',
          ????????????type:'list',
          ????????????message:?'請選擇你想要生成的代碼模板:',
          ????????????choices:?templates
          ????????},
          ????????{
          ????????????name:?'filename',
          ????????????type:'input',
          ????????????message:?'請輸入代碼文件中類名或方法名:',
          ????????????validate:?function?(value)?{
          ????????????????if?(value.length)?{
          ????????????????????return?true;
          ????????????????}?else?{
          ????????????????????return?'請輸入代碼文件中類名或方法名';
          ????????????????}
          ????????????},
          ????????}
          ????])
          ????.then(answers?=>?{
          ????????const?templateName?=?answers.templateName;
          ????????const?filename?=?answers.filename;
          ????????templates.forEach((item)?=>?{
          ????????????if(item.name?===?templateName)?{
          ????????????????const?suffix?=?utils.getFileSuffix(item.name)
          ????????????????const?file?=?`./index.${suffix}`
          ????????????????//?檢驗(yàn)當(dāng)前文件夾下是否有同名文件
          ????????????????fs.access(file,?function(err)?{
          ????????????????????if(!err)?{
          ????????????????????????console.log('創(chuàng)建失?。?,?chalk.yellow('文件已存在'))
          ????????????????????}?else?{
          ????????????????????????fs.writeFile(file,?item.src(filename),?function(err)?{
          ????????????????????????????if(err)?{
          ????????????????????????????????console.log('創(chuàng)建失?。?,?chalk.red(err))
          ????????????????????????????}?else?{
          ????????????????????????????????console.log(chalk.green(`創(chuàng)建文件成功!${file}`));
          ????????????????????????????}
          ????????????????????????})
          ????????????????????}
          ????????????????})
          ????????????}
          ????????})
          ????})
          }

          這里需要注意的是:如果不在文件創(chuàng)建之前檢查當(dāng)前文件夾下是否有同名文件的話,原有的同名文件將被覆蓋。

          編寫命令

          最后就是命令的編寫 bin/xman.js:

          #!/usr/bin/env?node

          const?{?program?}?=?require('commander');

          ...

          program
          ?.command('create')
          ?.description("Create?a?file")
          ?.alias('c')
          ?.action(()?=>?{
          ??require('../command/createFile')()
          ?});
          ?
          ...

          調(diào)試

          在當(dāng)前項(xiàng)目文件夾下執(zhí)行 npm link --force , 然后隨便找個(gè)文件下執(zhí)行 xmant c:

          創(chuàng)建單個(gè)文件.gif

          打開我們新創(chuàng)建的文件看看:

          生成的單個(gè)文件.png

          也可以選擇其他模板創(chuàng)建試試。

          通過命令快速批量創(chuàng)建代碼模板

          如果我們想一次性創(chuàng)建大量的代碼模板呢?當(dāng)然還是通過命令批量的創(chuàng)建文件。

          這里的思路:通過讀取配置文件,然后進(jìn)行批量創(chuàng)建。

          編寫配置文件

          //?說明:?
          //?folder:?文件夾名,可以嵌套,用?“/”分隔
          //?fileName:?文件名
          //?funcName:?類名或函數(shù)名
          // template:?用到的文件模板

          module.exports?=?[
          ????{
          ????????folder:?'./home',
          ????????fileName:?'index',
          ????????funcName:?'Home',
          ????????template:?'reactFunc-tsx'
          ????},
          ????{
          ????????folder:?'./home/compnent',
          ????????fileName:?'index',
          ????????funcName:?'Compnent',
          ????????template:?'reactFunc-tsx'
          ????},
          ????{
          ????????folder:?'./home/service',
          ????????fileName:?'index',
          ????????funcName:?'service',
          ????????template:?'reactFunc-ts'
          ????},
          ????{
          ????????folder:?'./news',
          ????????fileName:?'index',
          ????????funcName:?'News',
          ????????template:?'reactFunc-tsx'
          ????},
          ????{
          ????????folder:?'./news/service',
          ????????fileName:?'index',
          ????????funcName:?'service',
          ????????template:?'reactFunc-ts'
          ????}
          ]

          這里用到的文件模板就是我們之前編寫好的模板。

          編寫批量創(chuàng)建文件邏輯

          根據(jù)配置文件進(jìn)行文件夾和文件的批量創(chuàng)建 command/createManyFiles.js:

          //?批量創(chuàng)建文件

          const?chalk?=?require('chalk');
          const?inquirer?=?require('inquirer');
          const?fs?=?require('fs');
          const?path?=?require('path');
          const?utils?=?require('../utils/index');
          const?fileList?=?require('../config/fileList');
          const?templates?=?require('../templates/index');
          const?clui?=?require('clui');
          const?Spinner?=?clui.Spinner;
          const?status?=?new?Spinner('正在創(chuàng)建...');

          //?遞歸創(chuàng)建目錄?同步方法
          function?mkdirsSync(dirname)?{
          ????if?(fs.existsSync(dirname))?{
          ????????return?true;
          ????}?else?{
          ????????if?(mkdirsSync(path.dirname(dirname)))?{
          ????????????fs.mkdirSync(dirname);
          ????????????console.log(chalk.green(`創(chuàng)建目錄成功-${dirname}`));
          ????????}
          ????}???
          }

          module.exports?=?()?=>?{
          ????inquirer.prompt([
          ????????{
          ????????????name:?'choices',
          ????????????type:'list',
          ????????????message:?'請確認(rèn)配置好模板批量生成列表',
          ????????????choices:?['yes',?'no']
          ????????}
          ????])
          ????.then(answers?=>?{
          ????????const?choices?=?answers.choices
          ????????if(choices?===?'yes')?{
          ????????????//?批量創(chuàng)建目錄
          ????????????fileList.forEach(item?=>?{
          ????????????????if(item.folder)?{
          ????????????????????mkdirsSync(`${item.folder}`)
          ????????????????}
          ????????????})
          ????????????//?批量創(chuàng)建文件
          ????????????fileList.forEach(item?=>?{
          ????????????????templates.forEach(tpl?=>?{
          ????????????????????if(item.template?===?tpl.name)?{
          ????????????????????????const?suffix?=?utils.getFileSuffix(item.template)
          ????????????????????????const?fileName?=?`${item.fileName}.${suffix}`
          ????????????????????????fs.writeFile(`${item.folder}/${fileName}`,?tpl.src(item.funcName),?function(err)?{
          ????????????????????????????if(err)?{
          ????????????????????????????????console.log('創(chuàng)建失?。?,?chalk.red(err))
          ????????????????????????????}?else{
          ????????????????????????????????console.log(chalk.green(`創(chuàng)建文件成功!${fileName}`));
          ????????????????????????????}
          ????????????????????????})
          ????????????????????}
          ????????????????})
          ????????????})
          ????????}
          ????})
          }

          編寫命令

          最后編寫 bin/xman.js:

          #!/usr/bin/env?node

          const?{?program?}?=?require('commander');

          ...

          program
          ?.command('create-many')
          ?.description("Create?many?folders?and?files")
          ?.alias('cm')
          ?.action(()?=>?{
          ??require('../command/createManyFiles')()
          ?});
          ?
          ...

          調(diào)試

          在當(dāng)前項(xiàng)目文件夾下執(zhí)行 npm link --force , 然后隨便找個(gè)文件下執(zhí)行 xmant cm:

          創(chuàng)建多個(gè)文件.gif

          看一下我們批量創(chuàng)建的文件和文件夾:

          生成的多個(gè)文件.png

          總結(jié)

          關(guān)于快速創(chuàng)建代碼模板的方法有很多,有VSCode插件,也有CLI工具,更有做成 lowcode/nocode 平臺(tái)的方式等等。本文這種方式的好處在于足夠靈活,我們可以根據(jù)具體的需求進(jìn)行靈活的改造,使得更適用。


          瀏覽 28
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人妻精品导航 | 亚洲精品无码在线观看 | 黑人大鸡| 在线观看欧美日韩视频 | 国产美女久久久久久 |