[ JavaScript ] 项目开发中的函数抽离和复用

我心飞翔 分类:javascript

项目开发中的函数抽离和复用

在实际的项目开发中,尽可能要做到让开发者易于理解和后期维护,那么,其中一个最重要的就是必须将重复使用的相同代码块或者是差异不明显的代码块抽离出来。在需要使用的地方传参调用执行。

这样做的好处其中一个是,尽可能减少代码模块的改动,当项目中有需要拓展或者修改的地方,那就不需要在一个大的函数体里面去修改,而是该对应的模块,只要确定好输入的参数和返回值就可以。另一个好处是易于项目的拓展,将通用逻辑抽离出来之后,如果有新增的方法,直接新命名一个新的函数体实现新的逻辑,旧的函数体也可以保留,保证目前线上代码的兼容性。

下面是一个简单的例子:
这是一段数据上报相关的代码,在上报之前合并所需参数,之后判断当前环境再执行上报事件。

// index.vue
methods : {
  pageReport(ags) {
    let baseParams = {
      'event_id': this.eventId,
      'event_name': this.eventName,
      'page_id': '123',
    };
    return this.report({ ...baseParams, ...args });
  },
  report(args) {
    if (this.isApp) {
      web.Connect.Report({
        "data": args
      });
    } else {
      web.report(args);
    }
  },
}
 

流程图为:

c1-0.png

这一段代码本身是没有什么问题,但是后来想了一下,如果这个时候做拓展增加上报的场景,比如微信情况下,qq情况下,或者在上报的时候增加了一些其他逻辑,那么,report 这个函数的代码就会很臃肿,而且也不好理解。所以,可以先把客户端上报和web上报的函数单独给抽离出来。

所以,可以代码修改成这样:

// index.vue
methods : {
  pageReport(args) {
    let baseParams = {
      'event_id': this.eventId,
      'event_name': this.eventName,
      'page_id': '123',
    };
    return this.reportDistribute({ ...baseParams, ...args });
  },
  reportDistribute(args) {
    if (!this.isApp) return this.webReport(args);
    this.appReport(args);
  },
  appReport(args) {
    web.Connect.Report({
      "data": args
    });
  },
  webReport(ars) {
    web.report(args)
  },
}
 

流程图为:

c1-1.png

由中间的一个函数做判断处理,这样至少可以先保证客户端上报和web上报这两个地方不论在逻辑上怎么去改,只要是不涉及到上报本身的逻辑修改,那么就只要修改 reportDistribute 这个函数就可以,可以理解为 appReport 和 webReport 这两个函数是底层执行代码,而 reportDistribute 这个就是逻辑层代码,负责判断处理调用。

这样抽离之后底层负责执行的模块就会和逻辑层分开,需要拓展的之后只需要增加对应的模块和定义好需要的数据,使用的时候再去调用既可以了,不会再改到逻辑层上的代码,同时,任意一个模块需要修改,那么只要修改对应模块就可以。

上面的代码目前只有两种情况,客户端内和Web情况下上报 。虽然是将底层代码抽离了出来,但如果这个地方如果增加了其他场景的上报比如微信下的,或者是qq下的又或者是微博下的,按照目前的写法,就是增加 if else,但是这样并不好理解,代码的圈复杂度也高,这个时候可以写一个配置表进行映射 ,代码如下:

// index.vue
methods : {
  appReport(ars) {
    // do something
  },
  webReport(ars) {
    // do something
  },
  weixinReport(ars) {
    // do something
  },

  getReportParams() {
    let isApp = isApp ? '2' : '1';
    if (+isApp === 1 && isWeixin === 1) {
      isApp = 3
    }
    return isApp
  },

  reportDistribute(args, type) {
    const config = {
      1: this.webReport,
      2: this.appReport,
      3: this.weixinReport
    }
    config[type](args);
  },

  reportInit(args) {
    return this.reportDistribute(args, this.getReportParams());
  },

  pageReport(args) {
    let baseParams = {
      'event_id': this.eventId,
      'event_name': this.eventName,
      'page_id': '123',
    };
    return this.reportInit({ ...baseParams, ...args });
  },
}
 

流程图为:

c1-1-1.png

这样不管再新增多少情况的上报,只要在 reportDistribute 里面增加一个配置,在 getReportParams 中多增加一个返回值,就可以完成配置,整一段代码清晰可维护。

接着,这一段代码负责底层逻辑的一些映射相关的配置,这些一般改动的是比较少的,那么可以在把这一些抽离出来单独放在一个 config 文件里面,而不是全都挤在一个 vue 页面的 methods 中。
首先,将映射配置相关和负责底层执行的函数抽离出来,放在一个page-config.js 文件中,再将接口export 出去。 代码如下:

第一个文件 page-config.js ,定义基本配置和映射表。

// page-config.js 
function appReport(ars) {
  alert(JSON.stringify(ars));
}
function webReport(ars) {
  alert(JSON.stringify(ars));
}
function weixinReport(ars) {
  alert(JSON.stringify(ars));
}
const baseParams = {
  eventId: '1',
  eventName: 'test-page',
  pageId: 'index',
};
const pageConfig = {
  appReport: appReport,
  webReport: webReport,
  weixinReport: weixinReport,
};
export { pageConfig, baseParams };
 

之后如果需要做拓展,直接在这个配置文件里面加内容即可。
再是第二个文件 index.vue 文件

// index.vue 
import { pageConfig, baseParams } from './page-config';
export default {
  methods: {
    getReportParams() {
      let isApp = this.isApp() ? 'appReport' : 'webReport';
      if (+isApp === 'webReport' && this.isWeixin === true) {
        isApp = 'weixinReport'
      }
      return isApp
    },
    // add function
    pageGo(args) {
      return this.eventDistribute('link', 'https://www.google.com');
    },
    eventDistribute(type, args) {
      return pageConfig[type](args);
    },
    pageReport(args) {
      return this.eventDistribute(this.getReportParams(), { ...baseParams, ...args });
    },
  }
}
 

函数抽离之后,这里以后的拓展需要改到的只有 getReportParams 函数,这里面可能会返回新的数值。
这里注意一点是,之前的 reportDistribute 函数改成了 eventDistribute,这样做的目的是更加的通用,因为一个项目不但单单是只有上报事件,肯定还有其他功能逻辑,例如这时候需要再加一个网页跳转的事件,那么就可以向上面的代码一样 ,增加一个 pageGo调用 eventDistribute 。
eventDistribute 函数通用第一个参数区分执行的函数类型和第二个参数进行传参执行对应函数。

整理的流程图改为:

c1-2.png

接下来,也可以再对 page-config 做一点优化,因为,上面举例的事件类型也只有两大类一个是页面跳转,另一个是上报事件。那么,要考虑再多其他类型的情况,也不太好记的请。所以,可以对底层的映射配置再做一点优化,比如,通过参数的名称进行对应的函数执行,代码如下:

methods : {
  getReportParams() {
    let isApp = this.isApp ? "report.appReport" : "report.webReport";
    if (isApp === "report.webReport" && this.isWeixin) {
      isApp = "report.weixinReport";
    }
    return isApp;
  },
  // add function
  pageGo(args) {
    return this.eventDistribute("go.link", "https://www.google.com");
  },
  eventDistribute(type, args) {
    return pageConfig(type, args);
  },
  pageReport(args) {
    return this.eventDistribute(this.getReportParams(), {
      ...baseParams,
      ...args,
    });
  },
},
 

把事件类型分类,再使用小数点隔开 report.appReport


// page-config.js 
function appReport(ars) {
  alert(JSON.stringify(ars));
}
function webReport(ars) {
  alert(JSON.stringify(ars));
}
function weixinReport(ars) {
  alert(JSON.stringify(ars));
}
function link(url) {
  window.location.href = url;
}
const baseParams = {
  eventId: '1',
  eventName: 'test-page',
  pageId: 'index',
};
const pageEvent = (name, args) => {
  eval(name + "(" + JSON.stringify(args) + ")");
};
const pageFunConfig = {
  'report': pageEvent,
  'go': pageEvent
};
const pageConfig = (type, args) => {
  const eventType = type.split('.')[0];
  const eventName = type.split('.')[1];
  pageFunConfig[eventType](eventName, args);
}
export { pageConfig, baseParams };
 

切割出函数名称之后执行函数。流程图如下:

c1-3.png

上面的代码只是利用了上报这个行为做了一个例子,并不是说一定就是要这样写,更多的是一种将代码抽离达到多次服用和容易维护的目的。

如果小伙伴有更好的方法和建议,麻烦请在我的公众号留言,谢谢!


利宝阁确实有点东西。

回复

我来回复
  • 暂无回复内容