搭建公司前端脚手架

前言

公司的项目使用的都是 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

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

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

相关推荐
全栈前端老曹3 分钟前
【前端路由】React Router 权限路由控制 - 登录验证、私有路由封装、高阶组件实现路由守卫
前端·javascript·react.js·前端框架·react-router·前端路由·权限路由
zhuà!24 分钟前
uv-picker在页面初始化时,设置初始值无效
前端·javascript·uv
Amumu1213825 分钟前
React应用
前端·react.js·前端框架
tle_sammy25 分钟前
【架构的本质 07】数据架构:在 AI 时代,数据是流动的资产,不是静态的表格
人工智能·架构
没有bug.的程序员25 分钟前
Serverless 架构深度解析:FaaS/BaaS、冷启动困境与场景适配指南
云原生·架构·serverless·架构设计·冷启动·baas·faas
摸鱼的春哥28 分钟前
实战:在 Docker (Windows) 中构建集成 yt-dlp 的“满血版” n8n 自动化工作流
前端·javascript·后端
超级种码30 分钟前
Kafka四部曲之二:核心架构与设计深度解析
分布式·架构·kafka
小酒星小杜34 分钟前
在AI时代,技术人应该每天都要花两小时来构建一个自身的构建系统
前端·vue.js·架构
测试游记35 分钟前
基于 FastGPT 的 LangChain.js + RAG 系统实现
开发语言·前端·javascript·langchain·ecmascript
阿奇__35 分钟前
elementUI table 多列排序并保持状态样式显示正确(无需修改源码)
前端·vue.js·elementui