学习elpis-core阶段性总结

142 阅读2分钟

elpis-core

注:抖音“哲玄前端”,《全栈实践课》

elpis-core 是什么?

该项目是一个基于koa二次封装的一个项目,主要是通过实现loader加载文件,并挂载到app对象上,app文件下的配置也挂载到app对象上。

loader目录

image.png

在elpis-core入口文件引入loader

const Koa = require('koa');
const path = require('path');
const { sep } = path; // 兼容不同操作系统上的斜杠

const env = require('./env');

const middlewareLoader = require('./loader/middleware');
const routerSchemaLoader = require('./loader/router-schema');
const routerLoader = require('./loader/router');
const controllerLoader = require('./loader/controller');
const serviceLoader = require('./loader/service');
const configLoader = require('./loader/config');
const extendLoader = require('./loader/extend');

const DEFAULT_PORT = 8080;
const DEFAULT_HOST = '0.0.0.0';

module.exports = {
  /**
   * @description 启动项目
   * @param {object} options
   * options = {
   *  name: string, // 项目名称
   *  homePath: string, // 项目首页
   * }
   */
  async start (options = {}) {
    // Koa 实例
    const app = new Koa();

    // 挂载应用配置
    app.options = options;

    // 基础路径
    app.baseDir = process.cwd();

    // 业务文件路径
    app.businessPath = path.resolve(app.baseDir, `.${sep}app`);

    // 初始化环境变量
    app.env = env();
    console.log(`-- [start] env: ${app.env.getEnv()} --`);

    // 加载所有模块
    module.exports.loadModules(app);


    // 注册全局中间件
    try {
      require(`${app.businessPath}${sep}middleware.js`)(app);
      console.log(`-- [start] load global middleware done --`);
    } catch (error) {
      console.log('[exception] there is no global middleware file.');
    }

    //单独加载routerLoader,防止routerLoader中间件被覆盖
    routerLoader(app);

    // 启动服务
    try {
      const port = process.env.PORT || DEFAULT_PORT;
      const host = process.env.IP || DEFAULT_HOST;
      app.listen(port, host);
      console.log(`Server running on port: ${port}`);
    } catch (error) {
      console.log('error', error);
    }
  },

  /**
   * @description 加载所有模块
   * @param {Koa} app
   */
  loadModules (app) {
    const loaders = [
      { loader: middlewareLoader, name: 'middleware' },
      { loader: routerSchemaLoader, name: 'routerSchema' },
      { loader: controllerLoader, name: 'controller' },
      { loader: serviceLoader, name: 'service' },
      { loader: configLoader, name: 'config' },
      { loader: extendLoader, name: 'extend' },
      // { loader: routerLoader, name: 'router' }
    ];
    loaders.forEach(({ loader, name }) => {
      loader(app);
      console.log(`-- [start] load ${name} --`);
    });
  }
};

controller.js

const glob = require('glob');
const path = require('path');
const { sep } = path; // 兼容不同操作系统上的斜杠

/**
 * @description controller loader
 * @param {object}  app Koa 实例
 * @todo 加载所有 controller,可通过 'app.controller.${目录}.${文件名}' 访问
 * @example
  app/controller
    |
    | --custom-module
            |
            | --custom-controller.js
  =>app.controller.customModule.customController
 *
 */
module.exports = (app) => {
  // 读取 app/controller 目录下的所有文件
  const controllerPath = path.resolve(app.businessPath, `.${sep}controller`);
  const fileList = glob.sync(path.resolve(controllerPath, `.${sep}**${sep}**.js`));

  // 遍历文件,挂载到 app.controller 下
  const controller = {};
  fileList.forEach((file) => {
    // 获取文件名
    let name = path.relative(controllerPath, file).slice(0, -3); // 去掉 .js 后缀

    // 截取路径 app/controller/custom-module/custom-controller.js => custom-module/custom-controller
    name = name.replace(/[_-][a-z]/ig, (s) => s.slice(1).toUpperCase());
    // return;
    // 挂载 controller 到内存 app 对象中
    const names = name.split(sep); // ['customModule(目录)', 'customController(文件)']
    let tempController = controller;
    names.forEach((part, index) => {
      // return;
      if (index === names.length - 1) { // 文件
        const ControllerModule = require(path.resolve(file))(app);
        tempController[part] = new ControllerModule();
      } else { // 目录
        tempController[part] = tempController[part] || {};
        tempController = tempController[part];
      }
    });
  });
  app.controller = controller;
}

全局middleware

另一个觉得比较重要的就是全局middleware,负责配置静态根目录、模板渲染引擎、引入ctx.body解析中间件、引入异常捕获中间件、引入签名合法性校验中间件、引入API参数合法性校验中间件。

const path = require('path');

module.exports = (app) => {
  // 配置静态根目录
  const koaStatic = require('koa-static');
  app.use(koaStatic(path.resolve(process.cwd(), './app/public')));

  // 模板渲染引擎
  const koaNunjucks = require('koa-nunjucks-2');
  app.use(koaNunjucks({
    ext: 'tpl',
    path: path.resolve(process.cwd(), './app/public'),
    nunjucksConfig: {
      noCache: true,
      trimBlocks: true
    }
  }));

  // 引入ctx.body解析中间件
  const bodyParser = require('koa-bodyparser');
  app.use(bodyParser({
    formLimit: '1mb',
    enableTypes: ['json', 'form', 'text']
  }));

  // 引入异常捕获中间件
  app.use(app.middlewares.errorHandler);

  // 引入签名合法性校验中间件
  app.use(app.middlewares.apiSignVerify);

  // 引入API参数合法性校验中间件
  app.use(app.middlewares.apiParamsVerify);
}

最后感觉重要的一点是要理解好洋葱圈模型。