zoukankan      html  css  js  c++  java
  • node前后端同构的踩坑经历

    项目背景

    nodejs项目,webpack打包,用axios请求,Promise封装,nunjucks模板引擎;

    之前已将nunjucks模板通过webpack打包策略,做成前后端共用;

    目前需要将网络请求以及数据处理封装成service模块;

    目录划分:

    如上图所示:

    将公共代码放到service中,整合两端共同的一些网络请求以及数据处理(node首屏,客户端再次请求数据更新等操作)

    这里碰到的两个问题:

    1. node模块使用module.exports,而webpack我们使用的是import/export,两者共用会报错;

    2.我们使用了Promise做了两层封装(service封装、service中的fetch封装:抹平node和客户端的环境差异)

    第一个问题,其实webpack也提供了module.exports的方法,所以两端的模块是可以共用的。

    而第二个问题,我们使用了Promise,也在webpack全局引入了babel-polyfill,但是会报错:

    Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

    导致前面的排查思路一直以为是module.exports出了问题;

    解决方法

    我们刚开始是通过引入es6-promise来解决的:

    service/fetch.js:

    var axios = require('axios');
    var Promise = require('es6-promise').Promise;
    
    module.exports = function(opts, request) {
      return new Promise(function(resolve, reject) {
        axios(opts).then(function(res) {
          res = res.data
          if (res.success) {
            resolve(res)
          } else {
            reject({ ___req: opts, ___res: res })
          }
        }).catch(function(err) {
          reject({ ___req: opts, ___res: err.data || err.stack || err })
        })
      })
    }

    service/wawa.js

    var fetch = require('./fetch');
    var Promise = require('es6-promise').Promise;
    
    var getGamelist = function(params, req) {
      return new Promise(function(resolve, reject) {
        fetch({
          url: '/api/appeal/appealJoinOrderPage',
          type: 'get',
          params: params
        }).then(function(res) {
          resolve(res.data)
        }).catch(function(err) {
          reject(err)
        })
      })
    }
    
    module.exports = {
      getGamelist
    }

    并且我们也尝试在全局引入es6-promise,仍然报错;

    这样我们暂时得出结论,是原生Promise语法,直接与module.exports冲突报错。目前只能通过在当前js中引入es6-promise来规避。

    所幸的是,每个js中重复引入的es6-promise,在最终webpack打包的时候会去重,也避免了打包体积变大的问题。

    至此,node前后端代码共用的方案暂时通过。并且后面还可以写除了service以外的共用代码,提升了复用性和可维护性。

    再次踩到坑

    我们在迁移另一个公用service的时候,又碰到原来的问题,精简后的代码如下:

    /**
     * 获取红包列表
     */
    var getRedList = function(params, req) {
      return JSON.stringify({a:1})
    }
    
    module.exports = {
      getRedList
    }

    纳尼?又报错

    Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

    经过排查定位,发现是JSON.stringify不支持。。。但平时我们正常使用export/import从未碰到此问题。

    所以猜测是module.exports出去的模块,在webpack中默认是不会给全局方法加上window的。

    最终解决:兼容global和window(其实还不是最终)

    那解决方法就容易了,给全局方法手动加上全局对象,兼容处理global和window就可以了:

    (function(global) {
      let isBrowser = global.toString() === '[object Window]';
      /**
       * 获取红包列表
       */
      var getRedList = function(params, req) {
        return JSON.stringify({a:1})
      }
    
      module.exports = {
        getRedList
      }
    })(typeof exports === 'undefined' ? global = window : global);

    以上,得出在node和浏览器webpack共用模块化代码的解决方案:

    1. 使用 module.exports / require 做模块化

    2. 兼容处理global和window

    打死不改终极奥义最终版

    上周末线上突然报错飙升,发现集中在安卓4.3以下,报错:Promise is not defined

    经过不断试错排查,发现在module.exports出去后,里边的 'es6-promise'兼容包,在外面是不生效的

    最后决定在外边定义一个全局的Promise:

    window.Promise = require('es6-promise').Promise;

    此外,在低版本安卓中,判断window环境还有一个坑:

    需要这么写

    (function(global) {
      module.exports = function(opts, request) {
        var isBrowser = global.toString() === '[object Window]' || global.toString() === '[object DOMWindow]';
    
        return new global.Promise(function(resolve, reject) {
          if (isBrowser) {
            axios(opts).then(function(res) {
    
            }).catch(function(err) {
            })
          } else {
            axios(opts).then(function(res) {
              
            }).catch(function(err) {
            })
          }
        })
      }
    })(typeof exports === 'undefined' ? global = window : global);

    标红的那段是重点,划下来!!!

    End

    写到这里,已经做好了node项目代码复用的基础,那么整个接口的数据流程是怎么样的呢,又会碰到什么样的问题?

    比如我们需要在两端调用service的时候必须获得同样的数据格式,而浏览器的请求实际是经过一次node接口转发,总共两次fetch流程产生的。

    而且fetch模块,又需要支持浏览器和node的直接调用。所以我们整理出下面的接口请求流程图:

    具体项目架构会在下一期文章给出。

  • 相关阅读:
    2015北京网络赛 Couple Trees 倍增算法
    POJ 1330 Nearest Common Ancestors 倍增算法的LCA
    2015北京网络赛 G Boxes BFS+打表
    Codeforces Round#320 Div2 解题报告
    HDU 5446 Unknown Treasure Lucas+中国剩余定理+按位乘
    codeforces #329 div 2 B. Anton and Lines(几何)
    codeforces #329 div 2 A. 2Char (暴力)
    hdu 1394 Minimum Inversion Number (树状数组 逆序对)
    hdu 1754 I Hate It (线段树)
    codeforces 589 G
  • 原文地址:https://www.cnblogs.com/woodk/p/8353573.html
Copyright © 2011-2022 走看看