如何通过命令行生成React组件代码

2,860 阅读3分钟
img
img

业务开发中添加新的组件久而久之变成了一件很重复繁杂的任务,特别是当你正在编写测试并使用类似storybook的东西时,单个组件的文件结构可能看起来像这样

img
img

你可以通过编写一个node.js脚本来获取所需的组件模板并生成这些文件,而不是每次都手工编写它们,从而节省大量时间。

目前有一些npm包可供你下载并试用,但是这种方法的额外好处是你可以根据一个项目调整组件模板以适应您和您的团队的需要。

你会需要什么?

  1. 文件模板功能
  2. 用于创建文件和填充文件的node脚本
  3. 脚本中的逻辑来更新components / index.ts文件(可选)
  4. 运行node脚本的npm脚本(可选)

让我们首先在项目的根文件夹中创建一个目录,用于存放模板函数和逻辑。

take .generate_component
touch component_templates.js
touch index.js

组件模板

我的团队使用的基本组件的结构示例如下所示:

import React from 'react';

import './Navbar.scss';

export interface INavbarProps {}

const Navbar = ({}: INavbarProps) => {
  return <div>Hello 👋, I am a Navbar component.</div>;
};

export default Navbar;

components/Navbar/Navbar.tsx

在我们的component_templates.js文件中,我们可以像这样添加一个函数来生成组件模板。

exports.component = name => `
  import React from 'react';
  import './${name}.scss';
  export interface I${name}Props {}
  const ${name} = ({}: ${name}Props) => {
    return <div>Hello 👋, I am a ${name} component.</div>;
  };
  export default ${name};
`;

.generate_component/component_templates.js

该函数将在运行node脚本时接收name参数。

根据你希望包含文件夹结构的文件,你可以添加额外的组件模板。在我的例子中,我想添加以下模板函数:

  • component
  • story
  • test

该文件夹结构的完整文件如下所示:

// component.tsx
exports.component = name => `
  import React from 'react';
  import './${name}.scss';
  export interface I${name}Props {}
  const ${name} = ({}: ${name}Props) => {
    return <div>Hello 👋, I am a ${name} component.</div>;
  };
  export default ${name};
`;

// component.stories.jsx
exports.story = name => `
  import React from 'react';
  import ${name} from './${name}.tsx';
  export default {
    title: '${name}',
    component: ${name},
  };
  export const Default = () => <${name} />;
`;

// component.test.tsx
exports.test = name => `
  import React from 'react';
  import { render } from '@testing-library/react';
  import ${name} from './${name}';
  describe('${name} Component', () => {
    test('it should match the snapshot', () => {
      const { asFragment } = render(<${name} />);
      expect(asFragment()).toMatchSnapshot();
    });
  });
`;

// index.ts
exports.barrel = name => `
  import ${name} from './${name}';
  export default ${name};
`;

.generate_component/component_templates.js

Node脚本

由node脚本完成的任务如下:

  1. 在运行脚本时,从终端接受一个参数(用于组件名称)
  2. 为新组件创建一个文件夹
  3. 向文件夹中添加所需的文件
  4. 从components / index.ts文件中读取并将新组件插入其中(可选)

这是假设你使用一个index.js导出components文件夹中的所有组件。下面是我们用来参考的一个index.js示例

import Carousel from './Carousel';
import GenreCard from './GenreCard';
import HeroBanner from './HeroBanner';
import Layout from './Layout';
import Navbar from './Navbar';
import NetworkCard from './NetworkCard';
import Poster from './Poster';
import Swimlane from './Swimlane';

export {
  Carousel,
  GenreCard,
  HeroBanner,
  Layout,
  Navbar,
  NetworkCard,
  Poster,
  Swimlane,
};

components/index.js

如果你不遵循将新生成的组件包含在components/index.ts中的模式,那么我建议在下面的示例中,在标有“ Optional”的注释之后删除该代码块。

const fs = require('fs');
const { component, story, test, barrel } = require('./component_templates.js');

// grab component name from terminal argument
const [name] = process.argv.slice(2);
if (!name) throw new Error('You must include a component name.');

const dir = `./src/components/${name}/`;

// throw an error if the file already exists
if (fs.existsSync(dir)) throw new Error('A component with that name already exists.'); 

// create the folder
fs.mkdirSync(dir, { recursive: true });

function writeFileErrorHandler(err) {
  if (err) throw err;
}

// component.tsx
fs.writeFile(`${dir}/${name}.tsx`, component(name), writeFileErrorHandler);
// component.scss
fs.writeFile(`${dir}/${name}.scss`, '', writeFileErrorHandler);
// storybook.jsx
fs.writeFile(`${dir}/${name}.stories.jsx`, story(name), writeFileErrorHandler);
// test.tsx
fs.writeFile(`${dir}/${name}.test.tsx`, test(name), writeFileErrorHandler);
// index.tsx
fs.writeFile(`${dir}/index.ts`, barrel(name), writeFileErrorHandler);

////////////////
/// Optional ///
////////////////

// insert new component into 'components/index.ts file
fs.readFile('./src/components/index.ts', 'utf8', function(err, data) {
  if (err) throw err;

  // grab all components and combine them with new component
  const currentComponents = data.match(/(?<=import )(.*?)(?= from)/g);
  const newComponents = [name, ...currentComponents].sort();

  // create the import and export statements
  const importStatements = newComponents
    .map(importName => `import ${importName} from './${importName}';\n`)
    .join('');
  const exportStatements = `export {\n${newComponents
    .map(component => `  ${component},\n`)
    .join('')}};\n`;

  const fileContent = `${importStatements}\n${exportStatements}`;

  fs.writeFile(`./src/components/index.ts`, fileContent, callback);
});

.generate_component/index.js

现在脚本和模板函数已经就位,您可以通过在终端中输入以下命令来运行脚本

node ./generate_component ComponentName

另外,如果您不想直接运行节点脚本,您可以编写一个npm脚本来在包中运行该命令package.json文件。例如:

"scripts": {
  "generate-component": "node .generate_component $1"
}

上面脚本中的$1将是运行npm脚本时传递给它的参数名称。

现在已经编写了npm脚本,可以在终端中运行以下命令了

npm run generate-component ComponentName

如果正确执行了所有操作,则新文件夹应该出现在components文件夹中,并将其添加到components / index.ts文件中。

最后希望你可以将此模式合并到您的工作流程中,从而节省大量的时间!

本文首发于公众号全栈工具人欢迎大家关注,原文:👉如何通过控制台生成React组件代码