项目背景

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的直接调用。所以我们整理出下面的接口请求流程图:

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

记node前后端代码共用的一次坑的更多相关文章

  1. 基于数据库的代码自动生成工具,生成JavaBean、生成数据库文档、生成前后端代码等(v6.0.0版)

    TableGo v6.0.0 版震撼发布,此次版本更新如下: 1.UI界面大改版,组件大调整,提升界面功能的可扩展性. 2.新增BeautyEye主题,界面更加清新美观,也可以通过配置切换到原生Jav ...

  2. 实战:一键生成前后端代码,Mybatis-Plus代码生成器让我舒服了

    实战:一键生成前后端代码,Mybatis-Plus代码生成器让我舒服了 前言 在日常的软件开发中,程序员往往需要花费大量的时间写CRUD,不仅枯燥效率低,而且每个人的代码风格不统一.MyBatis-P ...

  3. [Java 开源项目]一款无需写任何代码,即可一键生成前后端代码的工具

    作者:HelloGitHub-小鱼干 JeecgBoot 是一款基于代码生成器的低代码开发平台,零代码开发.JeecgBoot 采用开发模式:Online Coding 模式-> 代码生成器模式 ...

  4. Node前后端分离基本概括

    首先从一个重要的概念“模板”说起. 广义上来说,web中的模板就是填充数据后可以生成文件的页面. 严格意义上来说,应该是模板引擎利用特定格式的文件和所提供的数据编译生成页面.模板大致分为前端模板(如e ...

  5. layui上传文件组件(前后端代码实现)

    我个人博客系统上传特色图片功能就是用layui上传文件组件做的.另外采用某个生态框架,尽量都统一用该生态框架对应的解决方案,因为这样一来,有这么几个好处?1.统一而不杂糅,有利于制定相应的编码规范,方 ...

  6. SpringCloud微服务实战——搭建企业级开发框架(三十一):自定义MybatisPlus代码生成器实现前后端代码自动生成

      理想的情况下,代码生成可以节省很多重复且没有技术含量的工作量,并且代码生成可以按照统一的代码规范和格式来生成代码,给日常的代码开发提供很大的帮助.但是,代码生成也有其局限性,当牵涉到复杂的业务逻辑 ...

  7. easyui中权限分配和添加 前后端代码

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  8. ajax请求, 前后端, 代码示例

    [博客园cnblogs笔者m-yb原创,转载请加本文博客链接,笔者github: https://github.com/mayangbo666,公众号aandb7,QQ群927113708] http ...

  9. vue.js异步上传文件前后端代码

    上传文件前端代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type&q ...

随机推荐

  1. Cleaner, more elegant, and harder to recognize (msdn blog)

    It appears that some people interpreted the title of one of my rants from many months ago, "Cle ...

  2. centos 7 部署 open-falcon 0.2.0

    =============================================== 2017/12/06_第2次修改                       ccb_warlock 更 ...

  3. 记录Mac下安装pyenv时所遇到的问题

    http://blog.csdn.net/foryouslgme/article/details/51683654  

  4. Ajax异步提交登录(2)--登录使用

    http://cjp1989.iteye.com/blog/1740964 1.Ajax的原理: Ajax的原理就是:通过javascript的方式,将前台数据通过xmlhttp对象传递到后台,后台在 ...

  5. Python学习_01_对象

    之前关于python的知识比较零散,这一个系列的随笔将python重新学习整理一遍.学习书籍<Python核心编程>第二版. Python对象基础 python并不是一个单纯面向对象的语言 ...

  6. MyBatis单个多个参数传递

    一.传入单个参数: public List<XXBean> getXXBeanList(String xxCode); <select id="getXXXBeanList ...

  7. OpenCascade Ruled Surface

    OpenCascade Ruled Surface eryar@163.com Abstract. A ruled surface is formed by moving a line connect ...

  8. Lambda表达式基础

    1.委托的典型用法 1.1定义一个委托: public delegate int AddDelegate(int i, int j); 1.2 定义一个MyClass类,放置一个满足 AddDeleg ...

  9. Spring框架入门之基于xml文件配置bean详解

    关于Spring中基于xml文件配置bean的详细总结(spring 4.1.0) 一.Spring中的依赖注入方式介绍 依赖注入有三种方式 属性注入 构造方法注入 工厂方法注入(很少使用,不推荐,本 ...

  10. 开源API测试工具 Hitchhiker v0.8 - 自动化测试结果统计

    Hitchhiker 是一款开源的支持多人协作的 Restful Api 测试工具,支持自动化测试, 数据对比,压力测试,支持脚本定制请求,可以轻松部署到本地,和你的team成员一起协作测试Api. ...