ReactJS单页应用程序的服务器端渲染

8

我已经做了什么

  • I have a frontend entirely built with React.JS.
  • All the business logic is handled by Django and exposed by Django REST Framework via an API.
  • I'm able to build for different mobile environnements (Android and iOS via Cordova)
  • The web application is accessible via my Django project (the same that exposes the api), the frontend is sill the same ReactJS code bundled via webpack.
  • The App has a single entry point, main.js which is a bundled version of my react.js components and dependencies, so my index.html typically looks like this :

    <body>
        <script type="text/javascript" src="/static/bundles/main-3997ad3476694c3c91cf.js"></script>
    </body>
    

我想要做什么

  • 我想提供一个服务器端渲染我的Web应用程序,让Web爬虫正确地索引我的应用程序(我不需要为移动构建进行服务器端渲染)。

考虑到我的应用程序是单页应用程序,我该如何处理?我不想重复造轮子或复制代码。为了实现这种自动服务器端呈现,我必须编写哪种node.js服务器?是否有任何方法可以直接在Django中提供服务器端呈现(通过一些工具读取和解释客户端显示的页面的最终结果,并返回此原始HTML)?


通常情况下,您会设置一个Node.js后端,并使用React.renderToString。但是,由于您正在使用Django,您可能需要使用类似于https://github.com/markfinger/python-react这样的东西。 - Joseph Furlott
我已经查看了这个软件包,但是我找不到使其工作的方法...我正在努力解决服务器不接受“POST”参数的问题,而且我不知道如何配置他所说的“反向代理”。 - Pierre Criulanscy
这个库怎么样?https://github.com/defrex/django-react很遗憾,我不熟悉Django,无法提供实际帮助,但我认为您只需要在Django的服务器渲染部分获取React的renderToString方法。您是否可以在Django中运行服务器端JS呢? - Joseph Furlott
1
看起来很有前途!我没有看到这个 Django 分支,谢谢 :) - Pierre Criulanscy
看看 reactstarterkit.com - 它是一个伟大的起始项目,用于建立一个同构的 JavaScript 应用程序。我正在运行一个 django rest 框架后端和一个 nodejs 前端,它的效果非常好。 - Henrik Andersson
1
这看起来很有趣,但我不想在Flux中重复我的业务逻辑,我只想让ReactJS成为“纯粹”的前端,没有任何业务逻辑。你如何处理这个问题? - Pierre Criulanscy
1个回答

3
您可能已经解决了您的问题,但我想分享一下我的解决方案。我有一个非常相似的设置,并且有一些看起来工作得很好的东西。我基本上有一个带有DRF后端api和同构React/Flux JavaScript应用程序的django。我还在Python后端服务器旁边运行一个Node服务器,该服务器仅充当“模板渲染”服务。实质上是替换django中的“render”函数。因此,我简单地用一个特殊的"IsoView"替换了django的"View",它通过http调用节点服务器并返回呈现的html。
from rest_framework.renderers import JSONRenderer
import requests

class IsoView(View):

    def user_json(self):
        if self.request.user.is_anonymous():
            return {'anonymous': True}
        else:
            return UserSerializer(self.request.user, context={'request': self.request}).data

    @classmethod
    def render(cls, request, template, data):
        req_data = JSONRenderer().render(data)
        try:
            headers = {'content-type': 'application/json'}
            query_params = request.GET
            resp = requests.post(_build_url(request.path), params=query_params, data=req_data, headers=headers, timeout=0.1)
            reply = resp.json()

            if resp.status_code == 302:
                return redirect(reply.get('redirect'))

            if 'error' in reply:
                raise Exception("\n\nRemote traceback: {}".format(reply.get('traceback')))

        except requests.exceptions.RequestException as err:
            logger.warn('IsoView request exception: {}'.format(err))
            reply = {}

        return render(request, template, {
            'react': reply.get('result'),
            'data': data
        })

然后像这样使用:

class HomePage(IsoView):

    def get(self, request, *args, **kwargs):
        return self.render(request, 'app.html', {
            'user': json_data...
        })

这还假设使用像这样的Django模板:
<html>
<head>
    <script>
        window.data = {{ data|json }};
    </script>
</head>
<body>{{ react|safe }}</body>
</html>

这段代码的作用是在body标签中呈现从node返回的html,并在window.data对象中转储客户端引导应用所需的json数据。这是该系统的简化版本,但它应该有效。需要注意的是,在window.data部分进行XSS攻击时,请确保转义所有json数据,其他方面都应该没问题。之后,node模板服务器看起来与任何您可以在网上找到的针对服务器端react的教程非常相似——一个简单的express应用程序。或者,如果在node中呈现了完整的内容并将其作为字符串返回,则根本不需要操作django模板。希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接