Storybook 按需加载指定包(从命令行中获取包名)

起因

在使用storybook @6.1.21时,可能只想关注其中一个或多个UI组件,但storybook将所有组件都给打包了;

导致的问题

  1. 展示的组件太多;
  2. 启动时间变慢;
  3. 如果其它包有问题,会导致启动失败;

    需求

    在启动时,只启动指定UI组件,其它组件即不打包,也不渲染;

配置文件

1. 文件目录

20210705164628242.png

2. config.js

import { configure } from '@storybook/react';

const req = require.context('../packages', true, /\.(stories|story)\.(tsx|jsx|js)$/);
function loadStories() {
  req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

3. webpack.config.js

module.exports = ({ config }) => {
  /** other config **/
  return config;
};

4. 版本信息

storybook: 6.1.21

解决方案:

方案1:webpack.DefinePlugin

原理

利用webpack.DefinePlugin定义全局变量,并替换config.js中的require上下文路径

config.js

import { configure } from '@storybook/react';

/** 变化点:将真实路径正则用 占位字符串 REPLACE_COMPONENT_DIR_REG_STR 代替 **/
const req = require.context('../packages', true, REPLACE_COMPONENT_DIR_REG_STR);
/** end **/
function loadStories() {
  req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

webpack.config.js

const webpack = require('webpack');

/** 变化点:从命令行参数中获取组件名 **/
let componentName;
try {
  const [cmd1, cmd2] = JSON.parse(process.env.npm_config_argv).original.slice(-2);
  if (cmd1 === 'start' && cmd2 && cmd2.trim()) {
    componentName = cmd2
      .split(',')
      .map(item => item && item.trim())
      .filter(Boolean)
      .join('|');
  }
}catch(e) {console.error(e)}
/** end **/

module.exports = ({ config }) => {
  /** other config **/
  /** 变化点:使用 webpack.DefinePlugin 插件在打包的时候将 占位字符串 替换掉 **/
  config.plugins.push(new webpack.DefinePlugin({
    REPLACE_COMPONENT_DIR_REG_STR: `/\\/(${componentName})\\/.*\\.(stories|story)\\.(tsx|jsx|js)$/`
  }));
  /** end **/
  return config;
};

方案2:自定义loader

原理

自定义一个webpack的loader,在打包config.js文件时替换 require.context 的正则表达式

config.js

import { configure } from '@storybook/react';

/** 变化点:在正则表达式中使用 占位字符串 %REPLACE_COMPONENT_DIR_NAME% **/
const req = require.context('../packages', true, /%REPLACE_COMPONENT_DIR_NAME%\.(stories|story)\.(tsx|jsx|js)$/);
/** end **/
function loadStories() {
  req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

webpack.config.js

/** 变化点 **/
const path = require('path');
/** end **/

module.exports = ({ config }) => {
  /** other config **/
  /** 变化点:为 .storybook/config.js 文件定制一个loader **/
  config.module.rules.push({
    test: /\.storybook\/config\.js$/,
    use: path.resolve(__dirname, './cus-set-package-loader.js'),
  });
  /** end **/
  return config;
};

.storybook/cus-set-package-loader.js

module.exports = res => {
  // 从命令行参数中获取组件名
  let componentName
  try {
    const [cmd1, cmd2] = JSON.parse(process.env.npm_config_argv).original.slice(-2)
    if (cmd1 === 'start' && cmd2 && cmd2.trim()) componentName = cmd2
  }catch(e) {console.error(e)}
  console.log('\n\x1b[33m%s\x1b[0m', `package names: ${componentName || 'all packages'}`)
  // 处理 多组件 和 组件名中的空格
  const componentDirName = componentName
    ? `\\/(${componentName.split(',')
      .map(item => item && item.trim())
      .filter(Boolean)
      .join('|')})\\/.*`
    : ''
  // 替换配置文件中的 占位符
  return res.replace('%REPLACE_COMPONENT_DIR_NAME%', componentDirName)
}

启动

npm start componentName1, componentName2

你可能感兴趣的:(Storybook 按需加载指定包(从命令行中获取包名))