简化dva的设计流程

2020-04-16  本文已影响0人  你的时间非常值钱

dva 首先是一个基于 reduxredux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-routerfetch,所以也可以理解为一个轻量级的应用框架

上面是dva官网对此框架的简介,使用了2年时间,让我体会到非常不错的用户体验。但我发现项目里有些代码写得太多,就像redux其中的一个缺点,模板代码太多了

用过这框架的小伙伴都知道dva其中一个流程,model里执行副作用需要接受一个service返回的函数做参数,如:

// xxx.model.js
import { urlA } from 'service'
...
effects: {
  *funA({ payload }, { call }) {
    const res = yield call(urlA, payload)
    // 处理...
  }
}

// xxx.service.js
// config是配置接口信息的文件
import { getLangList, getAreaData, getCashVoucherList, getMsgList  } from 'config'
export function getLangList () {
  return request({
    url: findList,
    method: 'get',
  })
}

export function getAreaData (data) {
  return request({
    url: getAreaUrl,
    method: 'post',
    data: data,
  })
}

export function getCashVoucherList (data) {
  return request({
    url: cashVoucherList,
    method: 'post',
    data: data,
  })
}

export function getMsgList (data) {
  return request({
    url: msgList,
    method: 'post',
    data,
  })
}

...

不知大家写service的时候有没感觉总是在ctrl c + ctrl v,然后改url,总是在做重复的动作,总是在写request({url, method, data}),于是我打算将service生成request逻辑封装出来。根据开发经验我们的业务请求主要是两种method,get和post。我先做了一个约定,在书写配置时,带有?结尾的url为get请求,否则为post请求

// config.js是配置url的模块
module.exports = {
 // ...一堆配置
 preUrl: '/api', // 所有接口的共用前缀
 orderCar: {
   getOrderCount: '/limousineOrder/getOrderCount?',  // 约定是get请求
   fetchList: '/limousineOrder/findPage', // post请求
 }, // 命名空间为orderCar
 xxx: {
   xxx1: '...'
 }, // 命名空间为xxx
 // ...
}

/**
* @Descripttion: 
* @name: wjj
* @param {obj, obj || []} module: 模块空间名, opt: 配置
* @return: {obj} 函数对象
*/

// 缓存各命名空间下的service
const serviceMap = {}
// 简单自动生成service
export function createService (module, opt) {
 // 取缓存
 if(serviceMap[module]) return serviceMap[module]
 const urls = config[module]
 const { preUrl } = config
 if (!urls) return
 let funs = {}
 let method = 'post'
 const optString = Object.prototype.toString.call(opt)
 // 根据opt配置相应生成,form下的content-type为form-data,其他暂为json
 if(optString === '[object Object]') {
   let contentType = Object.keys(opt)
   contentType.forEach(ct => {
     for(const url of opt[ct]) {
       funs[url] = (data = {}) => {
         if(ct.toLowerCase() === 'form') {
           const formData = new FormData()
           Object.keys(data).forEach(d => {
             formData.append(d, data[d])
           })
           data = formData 
         }
         return request({
           url: `${preUrl}${urls[url]}`,
           method,
           data,
         })          
       }
     }
   })
 } else if(optString === '[object Array]') {
   // opt为数组时,遍历数组生成,都是content-type为json
   for(const key of opt) {
     let method = 'post'
     let value = urls[key]
     if (value.includes('?')) {
       method = 'get'
       value = value.replace('?', '')
     }
     funs[key] = data => request({
       url: `${preUrl}${value}`,
       method,
       data,
     })
   }
 } else {  
     // 默认配置全部生成一次
     for (let [key, value] of entries(urls)) {
       let method = 'post'
       if (value.includes('?')) {
         method = 'get'
         value = value.replace('?', '')
       }
       funs[key] = function (data) {
         return request({
           url: `${preUrl}${value}`,
           method,
           data,
         })
       }
     }
 }
 return funs
}

使用方式:
1. 命名空间下全部配置生成content-type为json请求,优点简单,缺点是跑多余的生成代码(可以添加缓存)
const urls = createService('equityDining')
2. 可根据两大contentType(form,json)分别生成指定配置,但写得多代码
const { batchListPreview, batchCreate } = createService('equityDining', {
  form: ['batchListPreview', 'batchCreate'],
  json:  ['findProjectCountPage'],
})
3. 若该空间下并指定的接口全部约定content-type是json,可简写成数组
const { sendSms, getRecord, getTempletContent } = createService('orderCar', ['sendSms', 'getRecord', 'getTempletContent'])

同事的方案

在model的副作用call的第一个参数执行一个封装好的函数,每次调用再生成相应service

// uitls.js
export function commonRequest(url, data, header) {
  let method = 'post'
  if (url.includes('?')) {
    method = 'get'
    url = url.replace('?', '')
  }
  return request({
    url: url,
    method: method,
    data,
  }, header)
}
// model
*delCombo ({ proload },{call, put}) {
    const result = yield call(commonRequest.bind(null, delcomboUrl),proload)
    // ...
},

区别

感觉有点像amd和cmd的区别

后续

手写service的生成工作(逐个手动写request)已经删减,之后我在一个新项目是这样设计的
1.将config里的url配置移到services文件下,命名为api.js,不想config承载具体url配置
2.将生成方法createService从utils里写到外围services下,即直属src下面

上一篇下一篇

猜你喜欢

热点阅读