美文网首页
简化dva的设计流程

简化dva的设计流程

作者: 你的时间非常值钱 | 来源:发表于2020-04-16 18:54 被阅读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下面

    相关文章

      网友评论

          本文标题:简化dva的设计流程

          本文链接:https://www.haomeiwen.com/subject/abjdmhtx.html