metabase 前端架构解析

   metabase前端采用的是基于react的开发框架,并未用到前端应用开发框架如dva,其主要结合react-router、redux-actions、react-router-redux、reselect等主要组件结合而成,个人感觉写法上还是比较的官方,可读性不是很强,需要对上述的插件具有良好理解才能读懂前端源码,另外metabase前端也未采用组件库如antd,完全自己开发的组件。

一:前端store构建过程

  (一)redux-actions

  此组件主要用来创建action,其中主要API为createAction(s)​、handleAction(s)​,其中createAction负责创建标准action的payload,handleAction主要为包装reducer,负责处理createAction生成的payload,最后进行reducer处理,生成state。示例如下:

actions.js

export const CREATE_PUBLIC_LINK = "metabase/card/CREATE_PUBLIC_LINK";
export const createPublicLink = createAction(CREATE_PUBLIC_LINK, ({ id }) =>
  CardApi.createPublicLink({ id }),
);

reducers.js

// the card that is actively being worked on
export const card = handleActions(
  {
    [RESET_QB]: { next: (state, { payload }) => null },
    [CREATE_PUBLIC_LINK]: {
      next: (state, { payload }) => ({ ...state, public_uuid: payload.uuid }),
    }
  },
  null,
);

其中createAction和handleAction通过唯一表示type(即示例中的CREATE_PUBLIC_LINK)进行关联。handleActions的payload参数则为createAction的返回值,handleActions进行reducer处理,最后生成state变量card。

此外metabase封装了createThunkAction方法,它可接受redux-thunk style thunk,在thunk中可调用dispatch 和getState方法,createAction方法体中也可以进行异步。

// similar to createAction but accepts a (redux-thunk style) thunk and dispatches based on whether
// the promise returned from the thunk resolves or rejects, similar to redux-promise
export function createThunkAction(actionType, actionThunkCreator) {
  function fn(...actionArgs) {
    var thunk = actionThunkCreator(...actionArgs);
    return async function(dispatch, getState) {
      try {
        let payload = await thunk(dispatch, getState);
        let dispatchValue = { type: actionType, payload };
        dispatch(dispatchValue);

        return dispatchValue;
      } catch (error) {
        dispatch({ type: actionType, payload: error, error: true });
        throw error;
      }
    };
  }
  fn.toString = () => actionType;
  return fn;
}

createThunkCreator主要返回了一个异步方法,在其中调用了异步的方法,然后dispatch相应的actionType,其中参数dispatch和getState则通过mapDispatchToProps传入。

这样通过redux-action组件中的createAction和handleAction以及自定义的createThunkAction,就建立起整个前台的state状态树。

另外action的写法除了createAction和createThunkAction之外,还可直接编写方法如frontend/metabase/quey_builder/actions.js中的initializeQB方法,其代码如下:

export const initializeQB = (location, params) => {
  return async (dispatch, getState) => {
    // do this immediately to ensure old state is cleared before the user sees it
    dispatch(resetQB());
    dispatch(cancelQuery());

    const { currentUser } = getState();

    let card, databasesList, originalCard;
    let uiControls: UiControls = {
      isEditing: false,
      isShowingTemplateTagsEditor: false,
    };

     ........................................
    // if we have loaded up a card that we can run then lets kick that off as well
    if (question) {
      if (question.canRun()) {
        // NOTE: timeout to allow Parameters widget to set parameterValues
        setTimeout(
          () =>
            // TODO Atte Keinänen 5/31/17: Check if it is dangerous to create a question object without metadata
            dispatch(runQuestionQuery({ shouldUpdateUrl: false })),
          0,
        );
      }

      // clean up the url and make sure it reflects our card state
      const originalQuestion =
        originalCard && new Question(getMetadata(getState()), originalCard);
      dispatch(
        updateUrl(card, {
          dirty:
            !originalQuestion ||
            (originalQuestion && question.isDirtyComparedTo(originalQuestion)),
          replaceState: true,
          preserveParameters,
        }),
      );
    }
  };
};

  如果通过dispatch调用相关的action,方法如下:

(1)dispatch(resetQB());  其中resetQB为creatAction方法。

(2)dispatch.action(SET_CARD_AND_RUN, { card, originalCard });直接调用reducers中的handleAction.

(二)reselect组件

    示例如下:

export const getIsDirty = createSelector(
  [getCard, getOriginalCard],
  (card, originalCard) => {
    return isCardDirty(card, originalCard);
  },
);

    reselect组件具有缓存功能,其首先会调用参数一集合中的方法,各返回结果作为参数二方法的集合,如果返回结果较上一次调用没有变化,则不会调用参数二方法进行计算。

    结合合react-redux组件中的mapStateToProps方法,通过selector计算state状态,最后作为props传递给react组件。

二:TypeScript

   metabase前端代码中还采用了TypeScript方法,并对前端用的概念实体进行了type定义,路径为:/frontend/metabase/meta目录之下。

猜你喜欢

转载自blog.csdn.net/xiaobai51509660/article/details/80901665