搭建公司前端脚手架

前言

公司的项目使用的都是 react + ts + mobx + antd 的技术栈,每次搞新组件或者重构组件的时候都要重复新建文件夹,比较麻烦,而且每个人的代码风格、习惯也有差异。所以搭建一个快速创建业务组件的脚手架方便开发。

初始化

项目根目录运行命令

js 复制代码
npm init -y

然后安装依赖包

js 复制代码
npm i await-to-js fs-extra inquirer@7.0.4

然后在 package.json 里面配置 添加 bin 节点 txCli 命令,配置脚手架的入口文件为根目录下 index.js

js 复制代码
//package.json
{
  "bin": {
    "txCli": "./index.js"
  },
}

创建项目目录如下

js 复制代码
  |-- 根目录
  |-- template                      //模板
      |--web  //web端模块
           |--组件模板1              
           |--组件模板2
           |--index.js              //web端模块的入口文件
      |--h5   //h5模块
           |--组件模板1
           |--组件模板2
           |--index.js              //h5端模块的入口文件
      |--wx   //微信小程序模块
           |--组件模板1
           |--组件模板2
           |--index.js              //微信小程序的入口文件
  |-- index.js                      //脚手架入口文件
  |-- tool.js                       //工具函数模块
  |-- package.json

组件模板的目录结构如下

js 复制代码
|--组件模板
   |--dom                          //写好的组件模板配置
   |--index.js                     //组件模板配置的入口文件

脚手架编写

以web端组件模块为例子

脚手架入口文件

在根目录下的 index.js 文件,顶部写上 #!/usr/bin/env node

js 复制代码
#!/usr/bin/env node

const args = process.argv.slice(2);
// type 可以拿到脚手架命令后的参数 比如 txCli --webComp 拿到的就是 --webComp
const type = args[0];
const { createWeb } = require("./template/web");

switch (type) {
  case "--webComp":
    {
      createWeb();
    }
    break;
  default: {
    console.log("无效操作参数");
  }
}

当我们运行 txCli --webComp 命令的时候,就会走到 createWeb() 里面

模块入口文件

在 template/web/index.js 中,这是创建 web端组件模块的入口文件

js 复制代码
const inquirer = require('inquirer');
const { to } = require("await-to-js");
const { createModal } = require("./Modal/index");

async function createWeb() {
  const [err, res] = await to(
    inquirer.prompt([
      {
        type: "list",
        name: "type",
        message: "请选择web组件模块",
        choices: [{ value: 1, name: "1-弹窗" }],
        default: 0,
      },
    ])
  );

  switch (res.type) {
    case 1:
      {
        createModal();
      }
      break;
  }
}

module.exports = { createWeb };

因为 web 端会有很多业务组件,这里拿创建一个 Modal 为例子,当我们选择 1-弹窗的时候,就会走到 createModal() 里面

组件模板配置

首先在 template/web/modal/dom 里面,写好组件的模板

比如这儿咱们用的 react + ts + mobx 来搞组件模板

比如 web 端 Modal 组件的编写如下:

tsx 复制代码
import { observer, useSyncProps } from "@quarkunlimit/qu-mobx";
import { Modal } from "antd";
import { forwardRef, useImperativeHandle } from "react";
import { IXXXProps, IXXXRef } from "./interface";
import { Provider, useStore } from "./store/RootStore";

const XXX = observer(
  forwardRef<IXXXRef, IXXXProps>(function XXX_(props, ref) {
    const root = useStore();
    useSyncProps(root, Object.keys(props), props);
    const { logic, PropsStore } = root;

    useImperativeHandle(ref, () => {
      return {
        openModal: logic.openModal,
        closeModal: logic.closeModal,
      };
    });

    return (
      <Modal
        open={logic.open}
        width={800}
        destroyOnClose
        onCancel={logic.closeModal}
        {
          ...PropsStore.props
        }
      >
        XXX
      </Modal>
    );
  })
);

export default observer(
  forwardRef<IXXXRef, IXXXProps>(function XXXPage(props, ref) {
    return (
      <Provider>
        <XXX {...props} ref={ref} />
      </Provider>
    );
  })
);

在 template/web/Modal/index.js 中,就是 web端 Modal 组件的入口创建文件了

js 复制代码
const { to } = require("await-to-js");
const { existsSync } = require("fs");
const fse = require("fs-extra");
const inquirer = require("inquirer");
const path = require("path");
const { replaceFileInfo } = require("../../../tool");

async function createModal() {
  const [err, res] = await to(
    inquirer.prompt([
      {
        type: "input",
        name: "page",
        message: "请输入页面文件夹名(首字母大写e.g:EditModal)",
      },
    ])
  );

  const Page = res.page?.trim?.();
  if (!Page) {
    console.log("请输入正确的页面名");
    return;
  }
  const workPath = process.cwd();
  const dirPath = path.join(workPath, Page);
  if (existsSync(dirPath)) {
    console.log("该页面已存在");
    return;
  }
  console.log("组件路径:", workPath);
  console.log("组件名称:", Page);
  try {
    await fse.copy(path.resolve(__dirname, "./dom"), dirPath);
  } catch (error) {
    console.log("发生了异常:");
    console.log(error);
    return;
  }
  // 替换页面名称
  replaceFileInfo(path.join(dirPath, "index.tsx"), "XXX", Page);
  // 替换interface名称
  replaceFileInfo(path.join(dirPath, "interface.ts"), "XXX", Page);
  // 替换store相关内容
  replaceFileInfo(path.join(dirPath, "store/RootStore/index.ts"), "XXX", Page);
  replaceFileInfo(
    path.join(dirPath, "store/RootStore/interface.ts"),
    "XXX",
    Page
  );
  console.log("web组件创建完成");
}

module.exports = { createModal };

上面做的事情就是

  • 用户输入这个组件的名称
  • 判断当前路径下有没有重复组件
  • 复制模板到指定路径下
  • 然后把模板里面的 XXX 替换成输入的组件名称

这里的 replaceFileInfo 就是一个文件内容替换的方法,在根目录下的 tool.js 中

js 复制代码
// tool.js
const fs = require("fs");

const fileConfig = {
  encoding: "utf-8",
};
const replaceFileInfo = (filePath, oldInfo, newInfo) => {
  //读取指定目录的文件内容
  let content = fs.readFileSync(filePath, fileConfig);
  //替换内容
  content = content.replaceAll(oldInfo, newInfo);
  //覆盖旧内容
  fs.writeFileSync(filePath, content, fileConfig);
};

module.exports = {
  replaceFileInfo,
};

脚手架实践

写好脚手架后,把代码推到 gitlab,然后本地执行 npm link

然后在项目里面执行命令即可

这样就可以去配置不同端的各种业务组件了

相关推荐
ai小鬼头2 小时前
百度秒搭发布:无代码编程如何让普通人轻松打造AI应用?
前端·后端·github
漂流瓶jz2 小时前
清除浮动/避开margin折叠:前端CSS中BFC的特点与限制
前端·css·面试
前端 贾公子2 小时前
在移动端使用 Tailwind CSS (uniapp)
前端·uni-app
散步去海边2 小时前
Cursor 进阶使用教程
前端·ai编程·cursor
Codebee2 小时前
实战AI增强注解驱动:OneCode语义工程的智能升级
架构
清幽竹客2 小时前
vue-30(理解 Nuxt.js 目录结构)
前端·javascript·vue.js
weiweiweb8882 小时前
cesium加载Draco几何压缩数据
前端·javascript·vue.js
幼儿园技术家2 小时前
微信小店与微信小程序简单集成指南
前端
我不吃饼干9 天前
鸽了六年的某大厂面试题:你会手写一个模板引擎吗?
前端·javascript·面试
涵信9 天前
第一节 布局与盒模型-Flex与Grid布局对比
前端·css