likes
comments
collection
share

React组件搭建(二)思路分享

作者站长头像
站长
· 阅读数 2

简介

Nx新建自定义Plugin总览

实现目标:

  • 自定义代码生成器:在你的项目,快速生成你的自定义代码片段
  • 自动添加依赖:为你的项目自动安装Taiwindcss
  • 一行命令推送你的代码到NPM

思路步骤:

  1. 通过NX Console快速初始化插件
  2. 参考和略读@nrwl/react部分源码,结合自己的需求快速自定义插件
  3. 发布Npm部分暂时使用社区插件ngx-deploy-npm

笔者现有环境配置说明

  1. win10系统,Nvm-node,pnpm,淘宝镜像源
  2. 编辑器-Vscode

扩展官方插件

新建插件

  • 安装官方插件助手
  • 在lib下新建插件库
  • 在插件库下新建一个生成器

具体命令:

 pnpm add -D @nrwl/nx-plugin
 
 nx generate @nrwl/nx-plugin:plugin nx-plugin-ui --importPath=@taibui/devkit
 
 nx generate @nrwl/nx-plugin:generator library --project=nx-plugin-ui

 >  NX   Running global Nx CLI with PNPM may have issues.
>  NX  Generating @nrwl/nx-plugin:generator

CREATE libs/nx-plugin-ui/src/generators/library/files/src/index.ts__template__
CREATE libs/nx-plugin-ui/src/generators/library/generator.spec.ts
CREATE libs/nx-plugin-ui/src/generators/library/generator.ts
CREATE libs/nx-plugin-ui/src/generators/library/schema.d.ts
CREATE libs/nx-plugin-ui/src/generators/library/schema.json
UPDATE libs/nx-plugin-ui/generators.json
 

定义插件shema

在刚创建的生成器-library下定义schema

具体思路

1.扩展自@newl/react。只需复制官方插件的就好了 2.我们的自定义需求仅需在impl中实现,只需小幅度定义schema

功能拆分和实现

1。基于官方插件的,使用库生成器生成一个库 2. 为了调试和初始化,使用组件生成器生成一个Test组件(第一个生成器也在src根目录生成了组件) 3. 自定义需求:改进官方插件的配置Storybook后的工作流(在storybook配置文件引入css)

自定义需求实现思路

创建一个React库

只需调用官方维护的React插件,传入符合个人需求的参数,案例是限定rollup打包和移除默认生成的组件,强制可发布

import {
  libraryGenerator,
  ...
} from '@nrwl/react';

  const initLibrary = await libraryGenerator(tree, {
    ...options,
    unitTestRunner: 'jest',
    bundler: 'rollup',
    publishable: true,
    buildable: true,
    component: false,
  });

react组件生成器

在项目初始化的时候生成一个Test组件,默认官方是在入口文件同级目录生成一个组件库同名的组件

  const genComponent = await componentGenerator(tree, {
    name: 'Test',
    project: options.name,
    style: 'none',
    //默认导出入口文件
    export: true,
  });

tips:实际使用主要也是以生成文件为主,自定义组件生成器实现很简单,考虑到可以另开一偏转讲,案例只使用官方默认的

可以先大致了解,以下是官方的模版文件

React组件搭建(二)思路分享

React组件搭建(二)思路分享

主要是使用generateFiles,略微改动就能创建自定义组件生成器

storybook配置

限定webpack打包。默认配置文件为ts,不新建测试app

如果配置为ts,js摇摆,可能要做些兼容处理,因为后期引入css文件要修改配置文件

const setStorybookForLib = await storybookConfigurationGenerator(tree, {
    name: options.name,
    configureCypress: false,
    // Automatically generate *.stories.ts files for components declared in this project
    generateStories: true,
    generateCypressSpecs: false,
    bundler: 'webpack',
    tsConfiguration: true,
  });
  
  // 配置文件为ts和js的区别
  
      // 更新 ./storybook/previews.ts 或者 ./.storybook/preview.js
    const storybookConfigPath = joinPathFragments(
      projectRoot,
      options.storyTsConfiguration
        ? '.storybook/preview.ts'
        : '.storybook/preview.js'
    );
    const storybookConfigSource = host.read(storybookConfigPath, 'utf-8');
    if (storybookConfigSource !== null && cssSource !== null) {
      const changes = applyChangesToString(storybookConfigSource, [
        {
          type: ChangeType.Insert,
          index: 0,
          text: `import '../src/styles.css';\n`,
        },
      ]);
      host.write(storybookConfigPath, changes);
    }
  
  

配置tailwindcss

因为是基于官方的插件修改,这里有个坑,插件会检测你的目录是否有css样式文件。而我们配置组件库时,的确没有css文件。使用tailwind,也就是在打包时在入口文件引入。如果不先处理,也不影响本地开发(前提配置好Storybook),仅仅是友好提醒你没有配置好tailwind

 // 在配置tailwindcss之前,先在项目src目录下创建一个styles.css文件
  createFiles(tree, normOptions);

  // 正常为lib配置tailwindcss
  const setTaikwindForLib = await setupTailwindGenerator(tree, {
    project: options.name,
    // The name of the target used to build the project. This option is not needed in most cases
    buildTarget: `${options.name}-build`,
  });

// 官方插件部分源码

const knownLocations = [
  // Plain React
  'src/styles.css',
  'src/styles.scss',
  'src/styles.styl',
  'src/styles.less',

  // Next.js
  'pages/styles.css',
  'pages/styles.scss',
  'pages/styles.styl',
  'pages/styles.less',
];


  if (stylesPath) {
    const content = tree.read(stylesPath).toString();
    tree.write(
      stylesPath,
      `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n${content}`
    );
  } else {
    logger.warn(
      stripIndents`
        Could not find stylesheet to update. Add the following imports to your stylesheet (e.g. styles.css):
        
          @tailwind base;
          @tailwind components;
          @tailwind utilities;

更新文件

应用配置tailwindcss理论上插件会帮你引入tailwind样式,但是15.8的nx插件还是不能直接配置taiwind

主要思路,Nx修改更新json文件是很容易的(提供助手函数updateJson),但是修改ts,js文件,基本要靠自己实现,稍微复杂的,比如添加一个路由组件,自动更新路由配置,估计就要上AST了。

案例只是修改入口文件,基本上只是在第一行 或者最后一行插入字符,

// 更新文件
  addExportsToBarrel(tree, normOptions);
  
  
export default function addExportsToBarrel(
  host: Tree,
  options: NormalizedSchema
) {
  if (!tsModule) {
    tsModule = ensureTypescript();
  }

  const project = getProjects(host).get(options.name);
  const {
    sourceRoot: projectSourceRoot,
    projectType,
    root: projectRoot,
  } = project;


  const isApp = projectType === 'application';
  const indexFilePath = joinPathFragments(projectSourceRoot, 'index.ts');
  const indexSource = host.read(indexFilePath, 'utf-8');
  if (!isApp) {
    const cssFilePath = joinPathFragments(projectSourceRoot, 'styles.css');
    const cssSource = host.read(cssFilePath, 'utf-8');

    if (indexSource !== null && cssSource !== null) {
      const changes = applyChangesToString(indexSource, [
        {
          type: ChangeType.Insert,
          index: 0,
          text: `import './styles.css';\n`,
        },
      ]);
      host.write(indexFilePath, changes);
    }

新建Rollup和更新project文件

通过generateFiles新建文件,要注意的是,要更新Nx程式级 | 库级的project.json

案例修改的是执行器的配置

你的Nx命令都注册在该配置文件上

tips 旧版本的nx会在根目录有个workspace.json,新版本现在已经废弃了,用project.json取代它的功能

 // 新建 custom-rollup.config.js
  generateFiles(
    tree,
    joinPathFragments(__dirname, 'files/rollup/'),
    joinPathFragments(normOptions.projectRoot),
    {
      template: '',
    }
  );

  // 更新project.json
  updateProject(tree, normOptions);

export default function updateProject(tree: Tree, options: NormalizedSchema) {
  const project = readProjectConfiguration(tree, options.name);

  project.targets = project.targets || {};
  project.targets.build = {
    ...project.targets.build,
    options: {
      ...project.targets.build.options,
      rollupConfig: `${options.projectRoot}/custom-rollup.config.js`,
    },
  };

  updateProjectConfiguration(tree, options.name, project);
}

tailwind其它配置

tailwind的prettier插件

安装插件依赖 等同于执行 yarn add prettier-plugin-tailwindcss

案例选择的是删掉原有配置文件,因为修改js | ts 略复杂,初始化项目也没有什么配置,上AST不如新建


  const addPrettierTailwindPlugin = addDependenciesToPackageJson(
    tree,
    {},
    {
      'prettier-plugin-tailwindcss': '^0.2.7',
    }
  );

  // 删除原有的tailwindcss配置文件
  try {
    tree.exists(`${normOptions.projectRoot}/tailwind.config.js`) &&
      tree.delete(`${normOptions.projectRoot}/tailwind.config.js`);
  } catch (e) {
    throw new Error('删除原有的tailwindcss配置文件失败');
  }

新建两个tailwind配置文件

工作区级和程式级的配置文件


  // 新建tailwind.config.js
  generateFiles(
    tree,
    joinPathFragments(__dirname, 'files/taiwind/'),
    joinPathFragments(normOptions.projectRoot),
    {
      template: '',
      configRelativeUrl: offsetFromRoot(normOptions.projectRoot),
    }
  );

  // 检查工作区是否存在配置文件taiwind-worksapce-preset.config.js,否则创建
  if (
    !tree.exists(
      joinPathFragments(tree.root, 'taiwind-worksapce-preset.config.js')
    )
  ) {
    generateFiles(
      tree,
      joinPathFragments(__dirname, 'files/twpreset/'),
      joinPathFragments(workspaceRoot),
      {
        template: '',
      }
    );
    logger.warn("Don't forget to check the preset to your tailwind.config.js");
  }

这里借助offsetFromRoot(normOptions.projectRoot)获取两个文件的相对位置,这样程式级配置文件才能引用对

module.exports = {
  presets: [require('<%= configRelativeUrl %>taiwind-worksapce-preset.config.js')],
  content: [
    ...
  ],
    ...
  plugins: [],
};

开发经验

通过--dry-run 不断调试,总能完成自定义插件

pnpm exec nx g @taibui/devkit --dry-run

总结

Nx暴露的许多助手函数,能让我们十分容易的在工作区管理文件,社区活跃。目前底层是TS,但是有计划迁移其它方案,目前.net 和 Go也能脱离NPM使用Nx,接个人私活,算是一个不错的工具。