项目配置
编写
hzero-front 使用生成的方式编写 utils/config.js 文件
配置说明
配置是固定字符串
高级配置
route:init返回的值 可以 被routeMap这个环境变量中对应值覆盖noChange: 不能在运行中被changeRoute更改deps&get: 该配置依赖于其他配置deps, 生成方法是get
config/apiConfig.js 文件的编写
固定配置
  const config ={
    VERSION: 'v1',
  };
后端服务路由
  const config ={
    HZERO_HXXX: {
      init: () => {
         return "'/hxxx'";
      },
      route: true,
    },
  };
不可在运行时更改
  const config = {
    CLIENT_ID: {
      init: () => {
        return '`${process.env.CLIENT_ID}`';
      },
      noChange: true,
    },
  };
依赖其他变量
  const config = {
    AUTH_LOGOUT_URL: {
      deps: ['AUTH_HOST'],
      get: (AUTH_HOST) => {
        return `${AUTH_HOST}/logout`;
      },
    },
  };
实际文件
- 除了 scripts/genConfig.js 外 其他文件都略有删减
 - 其他项目如果也需要使用 apiConfig, 需要将 scripts/genConfig.js中的 changeRoute 方法 改名为 {projectName}ChangeRoute
 
config/apiConfig.js 编写文件
/**
 * 配置 utils/config 下的常量
 */
/**
 * deps 和 get 必须同时出现
 * get 和 init 是互斥的
 * 如果有 route, 那么必须有init
 *
 * @typedef {Object|string} CONFIG
 * @property {?Function} init - 返回返回字符串包裹的字符串模板; 参照 CLIENT_ID
 * @property {?boolean} noChange - 是否能改变 false/undefined => 能改变, true => 不能改变; 参照 CLIENT_ID
 * @property {?boolean} route - 是否能被环境变量routeMap改变 false/undefined => 可以改变, true => 不能改变; 参照 HZERO_PLATFORM
 * @property {?string[]} deps - 依赖的CONFIG, 当 依赖的CONFIG 改变时 会同时改变; 参照 AUTH_SELF_URL 的配置
 * @property {?Function} get - 依赖改变生成自己的方法; (...deps: string[]) => string; 参照 AUTH_SELF_URL 的配置
 */
/**
 * @type {{[configId]: CONFIG}}
 */
const config = {
  CLIENT_ID: {
    init: () => {
      return '`${process.env.CLIENT_ID}`';
    },
    noChange: true,
  },
  WEBSOCKET_URL: {
    init: () => {
      return '`${process.env.WEBSOCKET_HOST}`';
    },
  },
  HZERO_PLATFORM: {
    init: () => {
      return "'/hpfm'";
    },
    route: true,
  },
  HZERO_FILE: {
    deps: ['API_HOST', 'HZERO_HFLE'],
    get: (API_HOST, HZERO_HFLE) => {
      return `${API_HOST}${HZERO_HFLE}`;
    },
  },
  VERSION_IS_OP: {
    deps: ['PLATFORM_VERSION'],
    get: (PLATFORM_VERSION) => {
      return `${PLATFORM_VERSION}` === 'OP';  //  OP版
    },
    init: () => {
      return '`${process.env.PLATFORM_VERSION}`  ===  \'OP\'';  //  OP版
    },
  },
  // ...
};
const prefix = `/**
 * 不要直接修改这个文件, 请修改 config/apiConfig 文件
 * Config - 全局统一配置
 * @date: 2018-6-20
 * @author: niujiaqing <njq.niu@hand-china.com>
 * @version: 0.0.1
 * @copyright Copyright (c) 2018, Hand
 */
`;
const suffix = '';
module.exports = {
  config,
  prefix,
  suffix,
};
生成方法文件 scripts/genConfig.js
/**
 * TODO: 已经可以直接通过 changeRoute('/iam', '/iam-xxxxx') 来在运行时改变配置了, 需要将 .route 相关的代码注释取消 和注释之下的代码, 需要注意单引号问题, 已知问题, 不能有重复的 配置名
 * 通过 ../config/apiConfig 生成 utils/config 文件
 * @author WY <yang.wang06@hand-china.com>
 * @date 2019-05-22
 * @copyright ® HAND
 */
const {config, prefix, suffix} = require('../config/apiConfig');
const helpFuncMap = {};
const initLang = [];
const changeRouteSwitch = new Map(); // 存储 case 代码
const configChangeFunc = [];
const exportConfig = [];
// 不需要从 'HZERO_PLATFORM' -> '/hpfm' 的映射
// '/hpfm' -> 'HZERO_PLATFORM' 的映射
// const routeMapVar = {};
Object.entries(config).forEach(([key, value]) => {
  // #region exportConfig
  exportConfig.push(key);
  // #endregion
  // #region initConfig
  let varType = 'let';
  if (value.noChange) {
    varType = 'const';
  }
  //  init  lang
  if (value.init) {
    if (value.route) {
      // 路由 不能直接被环境变量更改, 所有 route: true 的 init 是直接返回可用的变量
      const routePath = value.init();
      // if (routeMapVar[routePath]) {
      //   throw new Error(formatString('route define has two config %12s, %12s has same value %8s', routeMapVar[routePath], key, routePath));
      // } else {
      //   routeMapVar[routePath] = key;
      // }
      initLang.push(formatString('%8s %16s = %s;', varType, key, ` routeMap[${routePath}] || ${routePath}`));
    } else {
      // not route
      initLang.push(formatString('%8s %16s = %s;', varType, key, value.init()));
    }
  } else if (value.get) {
    //  get  config  return  body;
    // TODO: 初始化时也调用方法
    // const retBody = value.get.toString().match(/return ([\S\s]+);/);
    // initLang.push(`${varType} ${key} = ${(retBody && retBody[1] || '')};`);
    initLang.push(formatString('%8s %16s = %s;', varType, key, `change${key}(${value.deps.join(', ')})`));
  } else {
    initLang.push(formatString('%8s %16s = %s;', varType, key, `'${value}'`));
  }
  // #endregion
  if (value.noChange) {
    // noChange 不能在运行时更改
    return;
  }
  // #region help  func
  // const helpKey = value.route ? value.init() : key;
  const helpKey = key;
  const curHelp = helpFuncMap[helpKey] || {changeConfig: [], depBy: []};
  helpFuncMap[helpKey] = curHelp;
  if (value.deps) {
    value.deps.forEach((depKey) => {
      // const depHelpKey = (config[depKey] && config[depKey].route) ? config[depKey].init() : depKey;
      const depHelpKey = depKey;
      const depHelp = helpFuncMap[depHelpKey] || {changeConfig: [], depBy: []};
      helpFuncMap[depHelpKey] = depHelp;
      depHelp.changeConfig.push(helpKey);
      curHelp.depBy.push(helpKey);
    });
  }
  curHelp.changeConfig.push(helpKey);
  // #endregion
  // #region change  route  func
  // const caseKey = value.route ? value.init() : key;
  const caseKey = key;
  const curCase = changeRouteSwitch.get(caseKey) || [];
  changeRouteSwitch.set(caseKey, curCase);
  if (value.deps) {
    //  if  have  deps,  must  have  get
    value.deps.forEach((depKey, depIndex) => {
      const depCase = changeRouteSwitch.get(depKey) || [];
      changeRouteSwitch.set(depKey, depCase);
      const newDep = [...value.deps];
      newDep[depIndex] = 'value';
      depCase.push(`${key} = change${key}(${newDep.join(', ')});`);
    });
    const changeFuncLines = value.get.toString().split('\n');
    changeFuncLines[0] = `function change${key} (${value.deps.join(', ')}) {`;
    const lastLineNo = changeFuncLines.length - 1;
    configChangeFunc.push(
      changeFuncLines.map((line, lineNo) => {
        if (lineNo === 0 || lineNo === lastLineNo) {
          return line.trim();
        } else {
          return `  ${line.trim()}`;
        }
      }).join('\n'));
  }
  // every config can change it self
  curCase.push(`${key} = value;`);
  // #endregion
});
function renderChangeRouteSwitch() {
  const strs = [
    'window.changeRoute = function changeRoute(key, value)  {',
    '  if(key && value)  {',
    '    switch(key)  {',
  ];
  for ([key, value] of changeRouteSwitch.entries()) {
    if (value.noChange) {
      return;
    }
    const curCase = (value || []);
    strs.push(`      case '${key}':`,
      curCase.map(str => {
        return `        ${str}`;
      }).join('\n'),
      '        break;',
    );
  }
  strs.push('      default:');
  strs.push('        console.error(`${key} is not exists`);');
  strs.push('        helpMethod();');
  strs.push('        break;');
  strs.push('',
    '    }',
    '  } else {',
    '    helpMethod(key);',
    '  }',
    '};',
  );
  return strs.join('\n');
}
function renderHelpMethod() {
  const strs = [];
  strs.push(`const helpMethodAssist = ${JSON.stringify(helpFuncMap)};`,
    'function helpMethod(key) {',
    '  if(key && helpMethodAssist[key]) {',
    `     console.error(\`\${key} 会更改: [\${helpMethodAssist[key].changeConfig.join(', ')}], 被级连更改: [\${helpMethodAssist[key].depBy.join(', ')}]\`)`,
    '  } else {',
    `    console.error('使用 changeRoute() 查看可以更改的参数');`,
    `    console.error('使用 changeRoute("参数") 查看具体改变');`,
    `    console.error('使用 changeRoute("参数", "参数值") 更改参数');`,
    `    console.error(\`可以更改的配置: [\${Object.keys(helpMethodAssist).join(', ')}]\`);`,
    '  }',
    '}');
  return strs.join('\n');
}
function renderExportsConfig() {
  return ['export {', exportConfig.join(',\n  '), '};'].join('\n');
}
const configData = [
  '/* eslint-disable no-shadow,camelcase,import/no-mutable-exports,no-console */',
  '// TODO: 自动生成的 src/utils/config 禁用了部分 eslint, 请查看 scripts/genConfig.js',
  prefix,
  'const routeMap = JSON.parse(process.env.routeMap || \'{}\');',
  // `const routeMapVar = ${JSON.stringify(routeMapVar)};`,
  '// #region initConfig',
  initLang.join('\n'),
  '// #endregion',
  '',
  '// #region changeConfig Funcs',
  configChangeFunc.join('\n'),
  '// #endregion',
  '',
  '// #region changeRoute',
  renderChangeRouteSwitch(),
  '// #endregion',
  '',
  '// #region helpMethod',
  renderHelpMethod(),
  '// #endregion',
  '',
  '// #regioin exportsConfig',
  renderExportsConfig(),
  '// #endregion',
  suffix,
];
require('fs').writeFileSync(
  require('path').resolve(__dirname, '../src/utils/config.js'),
  configData.join('\n'),
);
function formatString(format, ...args) {
  let argIndex = 0;
  return format.replace(/%(\d*)s/g, (testStr, match/* , start, all */) => {
    const padNum = +match;
    let ret;
    if (!padNum) {
      ret = args[argIndex]
    } else {
      ret = ("" + args[argIndex]).padEnd(padNum, ' ');
    }
    argIndex++;
    return ret;
  });
}
生成的 src/config.js 文件
/* eslint-disable no-shadow,camelcase,import/no-mutable-exports,no-console */
// TODO: 自动生成的 src/utils/config 禁用了部分 eslint, 请查看 scripts/genConfig.js
/**
 * 不要直接修改这个文件, 请修改 config/apiConfig 文件
 * Config - 全局统一配置
 * @date: 2018-6-20
 * @author: niujiaqing <njq.niu@hand-china.com>
 * @version: 0.0.1
 * @copyright Copyright (c) 2018, Hand
 */
const routeMap = JSON.parse(process.env.routeMap || '{}');
// #region initConfig
const CLIENT_ID = `${process.env.CLIENT_ID}`;
let WEBSOCKET_URL = `${process.env.WEBSOCKET_HOST}`;
let HZERO_PLATFORM = routeMap['/hpfm'] || '/hpfm';
let HZERO_HFLE = routeMap['/hfle'] || '/hfle';
let HZERO_FILE = changeHZERO_FILE(API_HOST, HZERO_HFLE);
let VERSION_IS_OP = `${process.env.PLATFORM_VERSION}` === 'OP';
// #endregion
// #region changeConfig Funcs
function changeHZERO_FILE(API_HOST, HZERO_HFLE) {
  return `${API_HOST}${HZERO_HFLE}`;
}
function changeVERSION_IS_OP(PLATFORM_VERSION) {
  return `${PLATFORM_VERSION}` === 'OP'; //  OP版
}
// #endregion
// #region changeRoute
window.changeRoute = function changeRoute(key, value) {
  if (key && value) {
    switch (key) {
      case 'WEBSOCKET_URL':
        WEBSOCKET_URL = value;
        break;
      case 'HZERO_PLATFORM':
        HZERO_PLATFORM = value;
        break;
      case 'HZERO_HFLE':
        HZERO_HFLE = value;
        HZERO_FILE = changeHZERO_FILE(API_HOST, value);
        break;
      case 'HZERO_FILE':
        HZERO_FILE = value;
        break;
      case 'VERSION_IS_OP':
        VERSION_IS_OP = value;
        break;
      case 'PLATFORM_VERSION':
        VERSION_IS_OP = changeVERSION_IS_OP(value);
        break;
      default:
        console.error(`${key} is not exists`);
        helpMethod();
        break;
    }
  } else {
    helpMethod(key);
  }
};
// #endregion
// #region helpMethod
const helpMethodAssist = {
  WEBSOCKET_URL: { changeConfig: ['WEBSOCKET_URL'], depBy: [] },
  HZERO_PLATFORM: { changeConfig: ['HZERO_PLATFORM'], depBy: [] },
  HZERO_HFLE: { changeConfig: ['HZERO_HFLE', 'HZERO_FILE'], depBy: [] },
  HZERO_FILE: { changeConfig: ['HZERO_FILE'], depBy: ['HZERO_FILE', 'HZERO_FILE'] },
  CLIENT_ID: { changeConfig: ['AUTH_URL'], depBy: [] },
  VERSION_IS_OP: { changeConfig: ['VERSION_IS_OP'], depBy: ['VERSION_IS_OP'] },
  PLATFORM_VERSION: { changeConfig: ['VERSION_IS_OP'], depBy: [] },
};
function helpMethod(key) {
  if (key && helpMethodAssist[key]) {
    console.error(
      `${key} 会更改: [${helpMethodAssist[key].changeConfig.join(
        ', '
      )}], 被级连更改: [${helpMethodAssist[key].depBy.join(', ')}]`
    );
  } else {
    console.error('使用 changeRoute() 查看可以更改的参数');
    console.error('使用 changeRoute("参数") 查看具体改变');
    console.error('使用 changeRoute("参数", "参数值") 更改参数');
    console.error(`可以更改的配置: [${Object.keys(helpMethodAssist).join(', ')}]`);
  }
}
// #endregion
// #regioin exportsConfig
export {
  CLIENT_ID,
  WEBSOCKET_URL,
  HZERO_PLATFORM,
  HZERO_HFLE,
  HZERO_FILE,
  VERSION_IS_OP,
};
// #endregion