从零开始搭建一个属于自己的react脚手架

1. 准备

首先,需要确保你已经安装了 node,然后我们来初始化一个项目

mkdir mycli-demo & cd mycli-demo
npm init -y

2. 添加一个简单的命令

在 package.json 中添加 bin 配置,mycli是我们 cli 工具提供的命令名称,对应的值是文件入口,我们这里指向bin/cli.js文件

{
  "name": "mycli-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
+ "bin":{
+   "mycli": "./bin/cli.js"
+ },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

在项目中创建bin/cli.js,并打印一段文字

#!/usr/bin/env node

console.log("i'm a cli");

第一行很重要,用来指明运行环境

此时我们可以再控制台运行mycli

你可能会有错误:控制台找不到我们的cli工具,这是因为我们在使用一个工具的时候必须安装它,例如你没有安装npm的时候去使用也会遇到同样的错误。

由于我们的mycli并没有发布,因此可以借助npm link或者yarn link选择本地安装,执行:

npm link

yarn link

如果你觉得 npm link 和 yarn link 比较繁琐,你也可以使用yalc 进行本地调试

然后再执行mycli,就可以看到控制台输出了bin/cli.js中打印的内容

至此,我们已经了解cli工具是如何作用的,下面我们在此基础上做一些改进,让他可以处理参数,彩色打印,显示加载中等等功能

3. 处理参数

很多时候我们需要在运行cli工具时携带参数,那么如何获取到这个参数呢?

node程序中,通过process.argv可获取到命令的参数,以数组返回

可以看到其实我们已经拿到各个参数,但是这样的获取方式不太直观,所以我们引入一个第三方 npm 包帮我们处理这部分功能:commander,参考文档:commander 文档

npm i commander -S

我们在bin/cli.js添加如下代码

#!/usr/bin/env node
+ const program = require('commander')

console.log("i'm a cli")

// 打印参数
console.log(process.argv)

+ program
+  .command('create <projectName>')
+  .description('create a new project')
+  .alias('c')
+  .option('-u, --umi', 'umi react template')
+  .option('-v, --vite', 'vite react template')
+  .option('-w, --webpack', 'webpack react template')
+  .action((projectName, options) => {
+    console.log(projectName, options)
+ })
+ program.version('1.0.0').parse(process.argv)

到目前为止,我们可以直观的获取到命令的参数,你可以在控制台尝试一下

4. 交互式命令

有的时候我们可能需要在命令行工具中融入一些交互,根据用户的输入或者选择生成一些东西或者做相应的操作。我们可以引入一个npm包来帮我们实现:inquirer

npm i inquirer -S

我们在bin/cli.js添加如下代码

#!/usr/bin/env node
const program = require('commander')
+ const inquirer = require('inquirer')

console.log("i'm a cli")

// 打印参数
console.log(process.argv)

program
  .command('create <projectName>')
  .description('create a new project')
  .alias('c')
  .option('-u, --umi', 'umi react template')
  .option('-v, --vite', 'vite react template')
  .option('-w, --webpack', 'webpack react template')
  .action((projectName, options) => {
    console.log(projectName, options)
+   inquirer
+     .prompt([
+       {
+         type: 'list',
+         name: 'frameTemplate',
+         message: '请选择框架类型',
+         choices: ['vite react', 'umi react', 'webpack react']
+       }
+     ])
+     .then((answer) => {
+       console.log(answer)
+     })
  })
program.version('1.0.0').parse(process.argv)

我们在控制台运行

mycli create test -r

至此,我们完成了交互式命令,下面我将完成一个模板的下载,一起动手吧!

5. 完成一个模板下载

在前面的步骤中,我们发现我们的日志打印不是很友好,我们可以通过log-symblos chalk ora 帮我们做一些提示信息的优化

npm 包名称作用官网备注
chalk修改控制台中字符串的样式chalk字体样式、颜色、背景颜色
log-symbols各种日志级别的彩色符号log-symbols从 5 版本开始使用 ESMRelease v5.0.0
ora终端加载效果ora

由于 log-symbols 从 5 版本开始使用 ESM,所以我们这里使用 4 版本

npm i chalk log-symbols@4 ora -S

要下载一个 github 仓库的代码,我们需要引入download-git-repo, download-git-repo - npm

npm i download-git-repo -S

我们来实现一个简单的下载功能

#!/usr/bin/env node
const program = require('commander')
const inquirer = require('inquirer')
+ const ora = require('ora')
+ const download = require('download-git-repo')
+ const { errLog, successLog } = require('../src/utils/log.js')

console.log("i'm a cli")

// 打印参数
console.log(process.argv)

program
  .command('create <projectName>')
  .description('create a new project')
  .alias('c')
  .option('-r, --react', 'react template')
  .option('-v, --vue', 'vue template')
  .option('-v2, --vue2', 'vue2 template')
  .option('-v3, --vue3', 'vue3 template')
  .action((projectName, options) => {
    console.log(projectName, options)
    inquirer
      .prompt([
        {
          type: 'list',
          name: 'frameTemplate',
          message: '请选择框架类型',
          choices: ['Vue3', 'Vue2', 'React']
        }
      ])
      .then((answer) => {
        console.log(answer)
+       const spinner = ora()
+       spinner.text = '正在下载模板...'
+       spinner.start()
+       download(
+         '',
+         projectName,
+         { clone: true },
+         function (err) {
+           if (err) {
+             spinner.fail('模板下载失败')
+             errLog(err)
+           } else {
+             spinner.succeed('模板下载成功')
+             successLog('项目初始化完成')
+           }
+         }
+       )
      })
  })
program.version('1.0.0').parse(process.argv)

在控制台输入

 mycli create test -r

可以看到模板下载成功

如果下载不成功,请详细查看download-git-repo的文档:download-git-repo - npm

至此,我们已经实现了一个简单的模板仓库下载功能;其中项目的下载地址可以去设置为一个或多个git仓库,更多的功能大家可以自行尝试。

6. 项目优化

如果我们有多个命令,那么我们就需要写多个

program
  .command("")
  .description("")
  .alias("")
  .option("")
  .action(() => {
    // 命令处理
  });
  1. 因为这部分代码被重复使用,我们自然而然想到了遍历,首先声明一个 list 变量用来维护我们的命令配置,在 src 新建 command-config.js 文件,该文件导出配置
const COMMAND_LIST = [
  {
    command: "create <projectName>",
    description: "create a new project",
    alias: "c",
    options:  ['-u, --umi', 'umi react template'],
      ['-v, --vite', 'vite react template'],
      ['-w, --webpack', 'webpack react template'],,
    action: require("./commandHandler/create"),
    examples: ['-u', '--umi', '-v', '--vite', '-w, --webpack'].map((v) => `create projectName ${v}`),
  },
];

module.exports = COMMAND_LIST;

修改bin/cli.js

/**
 * 注册option
 * @param {Object} commander commander实例
 * @param {Object} option 每个命令配置对象
 * @returns commander
 */
const registerOption = (commander, option) => {
  return option && option.length ? commander.option(...option) : commander;
};
/**
 * 注册action
 * @param {Object} commander commander实例
 * @param {Object} commandEle 每个命令配置对象
 * @returns commander
 */
const registerAction = (commander, commandEle) => {
  const { command, description, alias, options, action } = commandEle;
  const c = commander
    .command(command) // 命令的名称
    .description(description) // 命令的描述
    .alias(alias);
  // 循环options
  options && options.reduce(registerOption, c);
  c.action(action);
  return commander;
};

// 循环创建命令
COMMAND_LIST.reduce(registerAction, program);
  1. 由于命令处理部分代码量较大,所以我们考虑把命令处理的函数提取在一个文件夹下,我们在 src 下新建 commandHandler 目录,并新建一个 create.js,把 create 命令的处理代码放进 create.js

  2. 为了更方便使用,我们改写 mycli --help 命令

// help命令 把example显示出去
const help = () => {
  console.log("\n");
  console.log(chalk.green("如何使用:"));
  COMMAND_LIST.forEach((command, index) => {
    console.log(
      "  ",
      chalk.keyword("orange")(index + 1),
      `${command.command}命令`
    );
    command.examples.forEach((example) => {
      console.log(`     - mycli ${example}`);
    });
  });
};

program.on("-h", help);
program.on("--help", help);
  1. 为了更方便使用,我们加入对 npx 的支持
{
  "name": "mycli-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin":{
     "mycli": "./bin/cli.js"
  },
  "scripts": {
+   "start": "./bin/cli.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

5.优化我们的下载函数

/**
 * 下载函数,用于下载模板并安装依赖
 * @param {string} answer - 用户输入答案
 * @param {string} projectName - 项目名称
 * @returns {Promise<void>} 无返回值
 */
const downloadFn = async (answer, projectName) => {
  const url = SOURCESURL[answer?.frameTemplate];
  const spinner = ora()
  spinner.text = '正在下载模板...'
  spinner.start()
  try {
    await new Promise((resolve, reject) => {
      download(`direct:${url}`, projectName, { clone: true }, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });

    spinner.text = '模板下载成功,正在安装依赖'

    // 进入目录
    process.chdir(projectName);

    // 执行npm install命令
    const { stdout, stderr } = await exec('npm install');
    console.log(stdout);
    spinner.succeed('依赖完成')
    successLog('项目初始化完成')
    return
  } catch (err) {
    spinner.fail('模板下载失败')
    console.error(err);
    errLog(err)
  }
}

至此我们完成了一个简易版 cli 工具的开发,后续优化版本将会直接发布至 npm。

另附:

github地址:https://github.com/Shen-zhihao/my-react-cli.git

gitee地址:https://gitee.com/laborer996/my-react-cli.git

npm项目地址:https://www.npmjs.com/package/create-my-react-project

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
搭建 React + TypeScript 项目的步骤如下: 1. 创建一个新的项目文件夹 ``` mkdir my-react-ts-app cd my-react-ts-app ``` 2. 初始化项目 ``` npm init -y ``` 3. 安装必要的依赖 ``` npm install --save react react-dom npm install --save-dev typescript @types/react @types/react-dom webpack webpack-cli webpack-dev-server awesome-typescript-loader html-webpack-plugin ``` 4. 创建一个 TypeScript 配置文件 ``` touch tsconfig.json ``` 5. 将以下内容添加到 tsconfig.json 文件中 ``` { "compilerOptions": { "outDir": "./dist/", "module": "es6", "target": "es5", "lib": ["es6", "dom"], "sourceMap": true, "jsx": "react", "moduleResolution": "node", "esModuleInterop": true }, "include": [ "./src/**/*" ] } ``` 6. 添加一个 index.html 文件到项目根目录 7. 添加以下内容到 index.html 文件中 ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>My React TypeScript App</title> </head> <body> <div id="root"></div> </body> </html> ``` 8. 创建一个 src 目录 ``` mkdir src ``` 9. 在 src 目录中创建一个 App.tsx 文件 ``` touch src/App.tsx ``` 10. 将以下内容添加到 App.tsx 文件中 ```tsx import React from 'react'; const App = () => { return ( <div> <h1>Hello, World!</h1> </div> ); }; export default App; ``` 11. 在 src 目录中创建一个 index.tsx 文件 ``` touch src/index.tsx ``` 12. 将以下内容添加到 index.tsx 文件中 ```tsx import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root')); ``` 13. 在项目根目录中创建一个 webpack.config.js 文件 ``` touch webpack.config.js ``` 14. 将以下内容添加到 webpack.config.js 文件中 ```js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.tsx', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], }, module: { rules: [ { test: /\.tsx?$/, loader: 'awesome-typescript-loader', }, ], }, plugins: [ new HtmlWebpackPlugin({ template: 'index.html', }), ], devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, port: 3000, }, }; ``` 15. 运行 npm start 启动项目 ``` npm start ``` 以上就是从零搭建一个 React + TypeScript 的完整项目的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值