跨域解决方案之Node中间层

6,029

还愿

在上一篇《Vue-cli 3.x + axios 跨域方案踩坑指北》中说到通过webpack配置proxy代理来实现的跨域只是开发环境下的便利,真正上线时源代码被打包为静态资源是没有devServer帮你转发请求的,那上文文末也立了个flag,说下次在谈到这个跨域问题尝试找找答案。最近在实习,通过和导师交流和自己理解,发现Node中间层是一个目前前后端分离开发模式下比较普遍的跨域解决方案,其实原理与proxy差不多,无非是proxy是devServer提拱好的,而Node中间层则需要我们自己实现一个proxy,当然Node中间层的作用远远不止代理转发请求这么简单。在文末也会概述Node中间层为前端开发带来了哪些变化和机遇,本文大部分讲解如何使用Node中间层转发请求实现资源跨域访问。

抽象的理解

要明白Node层为什么能实现跨域,首先要明白一个原理:跨域问题是浏览器的同源策略的安全机制引起的,服务器之间是不存在跨域问题的,这也不是说服务器之间没有安全机制,只是服务器之间的调用无论是通过http访问还是通过rpc调用都是协议层面的机制,并没有限制必须同源。这也就是Node层跨域的实质,我们把静态文件和Node中间层放在同一个域下,这样前端资源和Node层的通信不会受同源策略影响,然后我们通过Node中间层把前端的资源请求转发到真实的请求地址,在通过中间层把请求返回的数据传给前端,这样就实现了此域跨彼域的串门操作。

具体操作

按理说,是可以使用一些中间件来做这个代理转发的,不过目前还没有研究,以后研究到再写文章记录。我们这次的Demo,使用的是Node的原始API(http.get API做get请求访问真实资源地址) + express中间件(做服务器使用),前端异步调用还秉承上篇文章的axios。

假设,我们前端这样异步访问一个地址:

    this.axios.get('/api/index').then(res=>{...})

express服务可以这样写:

app.get('/api/index',(req,res)=>{
    //请求远程地址的API
    getHttp().then(data=>{
        console.log(data);
        res.send(data);
    }).catch(err=>{
        res.send(err);
    })
    //返回数据给前端
})

其中getHttp()是封装的一个promise,这个promise的作用就是转发请求并在成功后以data(请求到的数据)回调resolve函数。然后,在成功请求后,在then的第一个回调参数中将数据在传递给前端

const getHttp = ()=>{
    return new Promise((resolve,reject)=>{
        http.get('http://localhost:8080/New/Index', (res) => {
            const { statusCode } = res;
            const contentType = res.headers['content-type'];
          
            let error;
            if (statusCode !== 200) {
              error = new Error('请求失败\n' +
                                `状态码: ${statusCode}`);
            } 
            if (error) {
              console.error(error.message);
              // 消费响应数据来释放内存。
              res.resume();
              return;
            }
          
            res.setEncoding('utf8');
            let rawData = '';
            res.on('data', (chunk) => { rawData += chunk; });
            res.on('end', () => {
              try {
                const parsedData = JSON.parse(rawData);
                resolve(parsedData);
                console.log(parsedData);
              } catch (e) {
                console.error(e.message);
              }
            });
          }).on('error', (e) => {
            console.error(`出现错误: ${e.message}`);
          });
          
    })
}

这样就实现了一个简单的跨域demo的框架结构。

最后,谈谈Node层对前端的影响

Node让给我们前端er的能力开始向服务端扩张,当然要说对抗老后端Java,Node还是略显单薄。但是各有各的好处,大体上来说,业界普遍认识是Node一般用来做中小型Web应用开发或者在大型应用开发中充当中间层的作用。那我们就来谈谈这个中间层有什么意义?

  • BFF架构 (backend for frontend) 在RESTFul接口的架构中,后端接口众多,规范不统一,而且后端API返回的数据大部分需要做二次处理,引入Node层后就可以为前端提供统一规范的接口,而且可以将数据二次处理放到Node层处理,前端则专注于UI逻辑的构建 ,我们习惯称这个叫BFF
  • 预渲染,前端渲染有一个最大的问题就是首屏白屏加载时间会比较长,影响用户体验。这个问题出现的本质是JS客户端渲染的性能问题,遇到较大的计算量较大的JS处理,会阻塞UI绘制导致JS不执行完无法加载DOM的问题,一个很好的解决方案就是预渲染。预渲染可以在Node层做模板渲染(相当于中间层的服务器渲染),这样可以有效解决白屏问题。那为什么不直接用服务器端渲染,是因为考虑到前后端分离开发的便利。
  • 控制路由,进一步分离UI与逻辑,前端渲染中路由是前端控制的,Node层的引入可以把路由控制权转让给Node,这样前端更加专注于UI,复杂的事情交给Node去做,即高效有关注分离,提高维护性。

希望,我的一点探索可以对你有用,这也是我分享的初衷。感谢支持!