在服务器上怎么运行react_react项目放在了服务器上如何运行 - CSDN
精华内容
参与话题
  • React项目从开发到上线运行全过程

    万次阅读 多人点赞 2018-08-07 17:02:50
    越来越多的前端工程师使用react来进行开发,当时用react时,creact-react-app无疑进入了大家的眼帘,本篇文章主要讲述如何从无到有的使用creact-react-app开发react应用,然后本地调试,直到最后的上线运行。...

            随着react的流行,越来越多的前端工程师使用react来进行开发,当时用react时,creact-react-app无疑进入了大家的眼帘,本篇文章主要讲述如何从无到有的使用creact-react-app开发react应用,然后本地调试,直到最后的上线运行。

    本篇文章内容结构说明,主要有以下12个部分:

    1. 安装nodejs

    2.使用nodejs的npm包安装create-react-app模块

    3. 使用creat-react-app模块创建我们的项目

    4. 了解控制我们项目运行测试打包的几个命令

    5. 通过npm start运行我们的项目

    6. 查看生产的项目目录的结构,并了解其作用

    7. 开始创建几个我们自己的组件

    8. 再次运行我们的项目

    9. 开始打包生产环境中需要的代码

    10. 将我们的代码部署进入我们的服务器

    11. ok,上线了,我们可以让用户正式访问了

    12. 维护调试

    一、安装node:

    我们要用create-react-app来开发react,首先要通过包管理器安装create-react-app,而包管理器一般安装了nodejs后会自带的,所以我们的第一步是在我们的电脑上安装node。

    请参考以下nodejs安装教程:

    Node.js安装配置

    二、检测npm包是否可用:

    安装好后,并且也配置进入了环境变量,然后我们来检测下你的npm包是否可用,打开cmd窗口,输入npm -v 进行检测,如下所示,若显示版本号则说明安装成功,环境变量也配置成功,可用开始安装creact-react-app进行react开发了。如果,你输入npm -v后没有弹出版本号,那么可能是你的环境变量没有配置好。请参考下面的如何配置环境变量教程。

    环境变量是什么?如何配置环境变量

    三、使用 create-react-app 快速构建 React 开发环境

    create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。

    create-react-app 自动创建的项目是基于 Webpack + ES6 。

    执行以下命令创建项目:

    $ cnpm install -g create-react-app
    $ create-react-app project_name
    $ cd project_name/
    $ npm start在浏览器中打开 http://localhost:3000/ ,结果如下图所示:

    命令解释:

    npm : 表示调用的随node一起安装的npm包

    install : 顾名思义,就是安装的意思

    -g : 表示全局安装,安装后在系统的任意位置都能使用,这就是全局安装的意思

    create-react-app: 这就是我们要安装的模块

    提示:安装好后,如果你想卸载,可以直接把install改为uninstal即可,也就是在前面个un就可以表示卸载了。有时候卸载后在安装可能会报写错,此时你直接定位的create-react-app安装目录,然后把这个目录删除一般就能解决了。

    四、常用命令解释

    安装好后,他给你说了些命令,这些命令大致如下

      npm start

        命令作用:Starts the development server.

      npm run build

         命令作用:构建用于生产的静态代码【我们开发完成后,发布时就使用此命令获得我们想要的用于生产的代码放入服务器】

      npm test

         命令作用:运行测试服务器

      npm run eject

       命令作用:

        复制构建依赖关系,配置文件和脚本进入应用程序目录。(默认依赖关系是隐藏的,如果你执行此了操作,这你的项目就会出现此依赖关系)

    注意:此操作是不可逆的,

       如何开始你的项目:

      cd project_name

      npm start

    五、查看项目目录结构

    然后我们打开项目目录,可以看下其目录结构

    大致如下图所示,共有三个文件夹,四个文件

    解释如下:

    三个文件夹

     node_modules  //用来盛放你安装的所有node模块的文件夹

    public                //用来盛放所有公共资源的文件夹,比如html模板,项目图标

    src                     //用来盛放你自己代码的文件夹,默认生成app.js代码也在里面

    四个文件

    .gitignore       // 这个是用来定义那些在提交到远程代码库时要忽略的文件

    package.json //用来声明项目的各种模块安装信息,脚本信息等

    package-lock.json //用来锁定模块安装版本的,能确保大家安装的模块版本一致

    README.md  //盛放关于这个项目的说明文件(全英文的,有兴趣可以看看)

    1. src文件夹

      然后我们来重点看看src文件夹,因为我们的react组件代码待会也会写在里面。

      这里面的文件也不多,就四部分

      1、APP相关的js,css文件   //自动给我们创建的一个组件

      2、index相关的js,css文件

      3、一个logo.svg图标            //默认的一个简单图标文件

      4、 一个registerServiceWorker.js文件 //此文件能进行缓存一些资源,一般是用到生产环境的,主要是用来加快访问速度,

    2. 大致的分析下组件结构

      主要说些比较重要的文件

      1. 首先是public目录下盛放着index.html模板,如果开发单页应用,那么所有的代码都会渲染在这里。【index.html文件中也就三部分内容。1.通过meat标签引入的主页一个自适应的viewport声明。2. 通过link标签引入的一个项目基本 m配置文件anifest.json和图标,3. 一个用来让react组件渲染的div标签】

      2. 也是在public目录下的manifest.json文件,此文件记录着这个react的APP应用的基本配置信息,他类似于Android的AndroidManifest.xml

      3. 然后就是在scr目录下的index.js文件,他不是一个react组件,他是链接react与htnl模板的桥梁,所有的react组件最终都是由他进行渲染到html模板中。然后整个文件中引入了一个系统默认生成的组件APP,有index.js进行渲染。

      4. registerServiceWorker.js文件,这个文件是用于生产环境的,它可以缓存些资源,让用户在离线模式下也能访问缓存的内容,以给用户更好的体验,开发环境中没什么用处。。。

      5. 最后还有个app.test.js文件,主要是用来组件测试的,有兴趣可以取好好了解下,这里就不多讲这个。

    六、创建我们自己的组件

    1. 整个项目文件基本分析完了,然后我们就可以创建自己的组件了。

    2. 我们在src下新建一个目录:myselfComponent,用来盛放我们新创建的组件

    3. 我们分别创建两个组件C1,C2,他们分别各显示一句话即可,

    4. 然后我们在index.js里面引入C1,C2组件,

    5. 然后在浏览器查看运行效果


    C1.js

    import React, { Component } from 'react';

    import './C1.css';

     

    class C1 extends Component {

      render() {

        return (

          <div className="c1">

            Hello,我是在src/myselfComponent目录下的C1.js文件中的C1组件 我引入了相同目录下的 C1.css 文件,用来给我包含的文字设为红色

          </div>

          );

      }

    }

     

    export default C1;


    C2.js

    import React, { Component } from 'react';

    import './C2.css';

     

    class C2 extends Component {

      render() {

        return (

          <div className="c2">

            Hello,我是在src/myselfComponent目录下的C2.js文件中的C1组件 我引入了相同目录下的 C2.css 文件,用来给我包含的文字设为蓝色

          </div>

          );

      }

    }

     

    export default C2;


    index.js

    import React from 'react';

    import ReactDOM from 'react-dom';

    import './index.css';

    import App from './App';

    import C1 from './myselfComponent/C1.js';

    import C2 from './myselfComponent/C2.js';

    import registerServiceWorker from './registerServiceWorker';

     

    ReactDOM.render(

      <div>

        <C1/>

        <App />

        <C2/>

      </div>,

     

      document.getElementById('root'));

    registerServiceWorker();


    ok,css文件就不展示了,免得让你们眼睛看花了

     在浏览器中的运行效果

    可以看到,运行的非常完美

     再复杂点

    上面中,我们是把所有组件全部引入了index.js文件中,然后统一渲染的。但是在实际开发中组件间必然存在嵌套关系,就是一个组件里面嵌套着另一个组件,

    现在我们就来在写个组件C3,然后把他嵌套进APP组件中。


    C3.js

    import React, { Component } from 'react';

    import './C3.css';

     

    class C3 extends Component {

      render() {

        return (

          <div className="c3">

            Hello,我是在src/myselfComponent目录下的C3.js文件中的C3组件 我引入了相同目录下的 C3.css 文件,用来给我包含的文字设为粗体黄色

          </div>

          );

      }

    }

     

    export default C3;


    APP.js

     

    import React, { Component } from 'react';

    import logo from './logo.svg';

    import './App.css';

    import C3 from './myselfComponent/C3.js';

     

    class App extends Component {

      render() {

        return (

          <div className="App">

            <header className="App-header">

              <img

                   src={ logo }

                   className="App-logo"

                   alt="logo" />

              <h1 className="App-title">Welcome to React</h1>

            </header>

            <C3 />

            <p className="App-intro">

              To get started, edit <code>src/App.js</code> and save to reload.

            </p>

          </div>

          );

      }

    }

     

    export default App;


    七、打包为生产版

    ok,基本上还是很简单的,然后我们的项目开发演示完成了,现在我们来开始进行生成生产环境的代码(所谓生产环境就是值用来发布到服务器里面的代码,是根据我们开发环境的代码生成)

    执行命令:

    num run build

    执行此命令后,他会在我们的项目目录下创建一个build文件夹,里面存放的就是生产环境需要的代码了

    八、将其放在服务器中执行

    生成环境中的代码生成后就要开发发布了,也就是放到我们的服务器上,供用户访问使用

    这部就比较简单了,直接将build里面的所有文件复制到服务器的根目录下即可,如下(这里我是直接复制到我本地搭建的apach服务器的根目录里面的)

     在看浏览器里面的运行结果(我的apach服务器的端口为8087)

     九、将其放到远程服务里面部署

    刚刚演示的是本地服务器,现在我再将其放到远程服务器里面进行部署

    如果你不知道如何将文件上传到远程服务器,请参考这一篇文章

    怎么把H5上传到服务器

    在浏览器中访问,ok,一切正常

    注意:如果你不想将文件复制到根目录,那么你需要修改inde.html文件中对js和css文件的路径,不然无法正常访问。

     十、部署到服务器上后的维护

    当我们项目上线后可能还要开发新功能,也可能项目运行期间会出bug,此时该怎么办呢?

    1. 当要开发新功能时,我们直接在本地开发好,然后打包生产文件,最后直接放到远程服务器里面就可以了

    2. 当出问题后,我们可以直接在浏览器里面访问,然后在线的打开控制台调试,并修改运行中的html代码,当改好后,再在本地修改即可。当然如果bug很复杂的话,那就需要认真思考了,不过要相信没有解决不了的bug

    再总结下:流程如下

    1. 安装nodejs

    2.使用nodejs的npm包安装create-react-app模块

    3. 使用creat-react-app模块创建我们的项目

    4. 了解控制我们项目运行测试打包的几个命令

    5. 通过npm start运行我们的项目

    6. 查看生产的项目目录的结构,并了解其作用

    7. 开始创建几个我们自己的组件

    8. 再次运行我们的项目

    9. 开始打包生产环境中需要的代码

    10. 将我们的代码部署进入我们的服务器

    11. ok,上线了,我们可以让用户正式访问了

    12. 维护调试

    到此,这篇文章就算完了,简单的讲了下,如何从无到有的搭建react开发环境,创建react项目,开发自己的项目,最后部署自己的项目,然后在运营期间的维护等。

    展开全文
  • react 异步渲染 如果您曾经制作过基本的React应用程序页面,则它可能会遇到SEO较差以及速度较慢的设备出现性能问题的情况。 您通常可以使用NodeJS添加回传统的服务器端网页渲染,但这并不是一个简单的过程,尤其...

    react 异步渲染

    如果您曾经制作过基本的React应用程序页面,则它可能会遇到SEO较差以及在速度较慢的设备上出现性能问题的情况。 您通常可以使用NodeJS添加回传统的服务器端网页渲染,但这并不是一个简单的过程,尤其是对于异步API。

    通过在服务器上呈现代码,您可以获得两个主要好处:

    • 提升加载时间性能
    • 提高您的SEO的灵活性。

    请记住,Google确实会等待您JavaScript加载,因此标题内容之类的简单内容将毫无问题地发生变化。 (不过,我无法代表其他搜索引擎,或者说它的可靠性如何。)

    在本文中,我将讨论使用服务器渲染的React代码时如何从异步API获取数据。 React代码具有JavaScript内置的应用程序的整个结构。 这意味着,与带有控制器的传统MVC模式不同,在呈现应用程序之前,您不知道需要什么数据。 使用诸如Create React App之类的框架,您可以快速创建质量很高的工作应用程序,但是它要求您仅在客户端上处理渲染。 这有一个性能问题,还有一个SEO /数据问题,在传统的模板引擎中,您可以根据需要更改磁头。

    问题

    React在大多数情况下都是同步渲染的,因此,如果没有数据,则渲染加载屏幕并等待数据来临。 这在服务器上无法很好地工作,因为在渲染之前您不知道所需的内容,或者您​​已经知道需要什么但您已经渲染了。

    查看以下库存标准渲染方法:

    ReactDOM.render(
      <provider store={store}>
        <browserrouter>
          <app></app>
        </browserrouter>
      </provider>
    , document.getElementById('root')
    )

    问题:

    1. 它是DOM渲染,用于寻找根元素。 这在我的服务器上不存在,因此我们必须将其分开。
    2. 我们无法访问除主根元素之外的任何内容。 我们无法设置Facebook标签,标题,描述,各种SEO标签,并且我们无法控制元素之外的DOM其余部分,尤其是头部。
    3. 我们提供了一些状态,但是服务器和客户端具有不同的状态。 我们需要考虑如何处理该状态(在本例中为Redux)。

    因此,我在这里使用了两个库,它们非常流行,因此希望它可以继承到您正在使用的其他库中。

    Redux :存储服务器和客户端同步状态是一个噩梦。 这非常昂贵,通常会导致复杂的错误。 在服务器端,理想情况下,除了足以使工作正常进行和渲染之外,您不希望对Redux进行任何操作。 (您仍然可以照常使用它;只需设置足够的状态以使其看起来像客户端即可。)如果要尝试,请查看各种分布式系统指南作为起点。

    React-Router :仅供参考,这是默认安装的v4版本,但是如果您有较旧的现有项目,则有很大的不同。 您需要确保使用v4处理路由服务器端客户端,这非常好。

    毕竟,如果需要进行数据库调用怎么办? 突然,这成为一个大问题,因为它是异步的并且在组件内部。 当然,这不是一个新问题:在官方的React repo上查看。

    您必须进行渲染才能确定所需的依赖项(需要在运行时确定),并在提供给客户端之前获取这些依赖项。

    现有解决方案

    下面,我将回顾解决此问题的现有解决方案。

    Next.js

    在我们进行任何工作之前,如果您要生产,服务器端渲染的React代码或通用应用程序, 那么Next.js ]是您想要去的地方。 它可以正常工作,很干净,并且有Zeit支持。

    但是,有人认为,您必须使用他们的工具链,并且他们处理异步数据加载的方式不一定那么灵活。

    从Next.js存储库文档中检查此直接副本:

    import React from 'react'
    export default class extends React.Component {
      static async getInitialProps ({ req }) {
        return req
          ? { userAgent: req.headers['user-agent'] }
          : { userAgent: navigator.userAgent }
      }
      render () {
        return <div>
          Hello World {this.props.userAgent}
        </div>
      }
    }

    getInitialProps是其中的键,它返回一个承诺,该承诺将解析为仅在页面上填充道具的对象。 很棒的是,它们只是内置在他们的工具链中:添加它就可以了,不需要任何工作!

    那么如何获取数据库数据? 您进行API调用。 你不想吗 好吧,那太糟糕了。 (好的,因此您可以添加自定义内容,但是您必须自己完全实现它。)但是,如果考虑到这一点,这是一种非常合理的方法,并且通常来说是一种很好的做法,因为否则,您的客户仍然会相同的API调用,服务器上的延迟几乎可以忽略不计。

    您也只能访问请求对象,几乎就是请求对象。 再说一次,这似乎是一种好习惯,因为您无权访问状态,无论您的服务器还是客户端,状态都不同。 哦,以防万一您以前没有发现它,它仅适用于顶级页面组件。

    Redux Connect

    免费学习PHP!

    全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

    原价$ 11.95 您的完全免费

    Redux Connect是一个非常自以为是的服务器端渲染器,具有不错的理念,但是如果您不使用它们描述的所有工具,则可能不适合您。 这个软件包有很多东西,但是它是如此复杂,还没有升级到React Router v4。 有很多设置,但是让我们起最重要的作用,只是学习一些经验教训:

    // 1. Connect your data, similar to react-redux @connect
    @asyncConnect([{
      key: 'lunch',
      promise: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
    }])
    class App extends React.Component {
      render() {
        // 2. access data as props
        const lunch = this.props.lunch
        return (
          <div>{lunch.name}</div>
        )
      }
    }

    装饰器在JavaScript中不是标准的。 在撰写本文时,它们是第2阶段,因此请自行决定使用。 这只是添加高阶组件的另一种方法。 这个想法很简单:关键是要传递给道具的内容,然后您会得到一个承诺清单,该清单可以解决并被传递。这看起来不错。 也许替代方法就是这样:

    @asyncConnect([{
      lunch: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
    }])

    这似乎对JavaScript可行,没有太多问题。

    React前负荷

    react-frontload存储库没有很多文档或说明,但是也许我能获得的最佳理解是来自测试(例如测试)。
    并只阅读源代码。 挂载某些东西后,它将被添加到一个promise队列中,当它解决时,它将被提供。 它的作用是相当不错的,尽管很难推荐没有充分记录,维护或使用的东西:

    const App = () => (
      <frontload isServer >
        <component1 entityId='1' store={store}></component1>
      </frontload>
    )
    
    return frontloadServerRender(() => (
      render(<app></app>)
    )).then((serverRenderedMarkup) => {
      console.log(serverRenderedMarkup)
    })

    寻找更好的解决方案

    上面的解决方案都没有真正引起我对库所期望的灵活性和简单性的共鸣,因此现在我将介绍自己的实现。 目标不是编写程序包,而是让您了解如何为您的用例编写自己的程序包。

    这个示例解决方案的仓库在这里

    理论

    尽管最终它是相当多的代码,但其背后的想法相对简单。 这是对我们正在讨论的想法的概述。

    服务器必须渲染两次React代码,而我们只使用renderToString 我们想要在第一和第二渲染之间保持上下文。 在我们的第一个渲染中,我们试图消除所有的API调用,承诺和异步操作。 在第二个渲染中,我们希望获取所有获取的数据并将其放回上下文中,因此渲染出我们的工作页面以进行分发。 这也意味着应用程序代码需要根据上下文执行操作(或不执行),例如是在服务器上还是在客户端上,无论哪种情况,是否都在获取数据。

    此外,我们可以根据需要自定义此内容。 在这种情况下,我们根据上下文更改状态代码和头。

    第一次渲染

    在代码内部,您需要知道您正在使用服务器或浏览器,并且理想情况下,您希望对其进行复杂的控制。 使用React Router ,您将获得一个静态上下文道具,这很好,因此我们将使用它。 现在,我们已经从Next.js中学到了一个数据对象和一个请求数据。 服务器和客户端之间我们的API不同,因此您需要提供服务器API,最好提供与客户端API相似的接口:

    const context = {data: {}, head: [], req, api}
    const store = configureStore()
    renderToString(
      <provider store={store}>
        <staticrouter location={req.url}
          context={context}
        >
          <app></app>
        </staticrouter>
      </provider>
    )

    第二次渲染

    在您第一次渲染之后,我们将立即获取那些未完成的promise,并等到这些promise完成,然后重新渲染,更新上下文:

    const keys = Object.keys(context.data)
    const promises = keys.map(k=>context.data[k])
    try {
      const resolved = await Promise.all(promises)
      resolved.forEach((r,i)=>context.data[keys[i]]=r)
    } catch (err) {
      // Render a better page than that? or just send the original markup, let the front end handle it. Many options here
      return res.status(400).json({message: "Uhhh, some thing didn't work"})
    }
    const markup = renderToString(
      <provider store={store}>
        <staticrouter location={req.url}
          context={context}
        >
          <app></app>
        </staticrouter>
      </provider>
    )

    应用程式

    从我们的服务器快速跳转到应用程序代码:在我们所有具有路由器连接的组件中,我们现在都可以做到这一点:

    class FirstPage extends Component {
      async componentWillMount(){
        this.state = {text: 'loading'}
    
        this._handleData('firstPage')
      }
      async _handleData(key){
        const {staticContext} = this.props
    
        if (staticContext && staticContext.data[key]){
          const {text, data} = staticContext.data[key]
          this.setState({text, data})
          staticContext.head.push(
            <meta name="description" content={"Some description: "+text}/>
          )
        } else if (staticContext){
          staticContext.data[key] = this._getData()
        } else if (!staticContext && window.DATA[key]){
          const {text, data} = window.DATA[key]
          this.state = {...this.state, text, data}
          window.DATA[key] = null
        } else if (!staticContext) {
          const {text, data} = await this._getData()
          this.setState({text, data})
        }
      }
      async _getData(){
        const {staticContext} = this.props
        const myApi = staticContext ? staticContext.api : api
        const resp = await butter.post.list()
        const {data} = resp.data
        const {text} = await myApi.getMain()
        return {text, data}
      }
      render() {
        const text = this.state.text
        return (
          <div className='FirstPage'>
            {text}
          </div>
        )
      }
    }

    哇,那是很多复杂的代码。 在此阶段,您可能希望采用一种更中继的方法,在该方法中,将数据提取代码分离到另一个组件中。

    该组件由您可能熟悉的东西(渲染步骤和componentWillMount步骤)预订。 四个阶段的if语句处理不同的状态-预取,后取,保留器渲染,服务器后渲染。 加载数据后,我们还会添加到头部。

    最后,有一个获取数据的步骤。 理想情况下,您的API和数据库具有相同的API,这使执行相同。 您可能需要将它们放入Thunk或Saga中,以使其更具扩展性。

    查看文章“ 服务器端React Rendering ”和repo React服务器端Rendering以获得更多信息。 请记住,您仍然需要处理未加载数据的状态! 您只会在首次加载时进行服务器渲染,因此将在后续页面上显示加载屏幕。

    更改index.html以添加数据

    我们需要发送任何预取的数据作为页面请求的一部分,因此我们将添加一个脚本标签:

    <script>
    window.DATA = {data:{}} // It doesn't really matter what this is, just keep it valid and replaceable
    </script>

    服务

    然后,我们需要将其添加到我们的搜索和替换中。 但是,HTML使用了非常基本的脚本标签查找器,因此,如果您具有脚本标签,则需要对它进行base-64编码。 另外,不要忘记我们的头部标签!

    // earlier on
    const headMarkup = context.head.map(h=>(
      renderToStaticMarkup(h)
    )).join('')
    
    // then render
    const RenderedApp = htmlData.replace('{{SSR}}', markup)
      .replace('{{head}}', headMarkup)
      .replace('{data:{}}', JSON.stringify(new Buffer(JSON.stringify(context.data)).toString('base64')))
    if (context.code)
      res.status(context.code)
    res.send(RenderedApp)

    我们还处理状态代码更改(例如404),因此,如果您有404页面,则可以执行以下操作:

    class NoMatch extends Component {
      componentWillMount(){
        const {staticContext} = this.props
        if (staticContext){
          staticContext.code = 404
        }
      }
      render() {
        return (
          <div>
            Sorry, page not found
          </div>
        )
      }
    }

    摘要

    如果不确定自己在做什么,只需使用Next.js即可 它是为服务器端渲染和通用应用程序设计的,或者如果您希望以所需的方式手动完成所有操作的灵活性。 一个示例可能包括是否在子组件中而不是在页面级别上获取数据。

    希望本文对您有所帮助! 不要忘记签出GitHub存储库以获取有效的实现。

    翻译自: https://www.sitepoint.com/asynchronous-apis-server-rendered-react/

    react 异步渲染

    展开全文
  • React 也可以服务端通过 Node.js 转换成 HTML,直接浏览器端“呈现”处理好的 HTML 字符串,这个过程可以被认为 “同构”,因为应用程序的大部分代码都可以在服务器和客户端上运行。 二、为什么使...

    React 服务端渲染完美的解决方案

    一、服务器端渲染是什么?
    使用 React 构建客户端应用程序,默认情况下,可以在浏览器中输出 React 组件,进行生成 DOM 和操作 DOM。React 也可以在服务端通过 Node.js 转换成 HTML,直接在浏览器端“呈现”处理好的 HTML 字符串,这个过程可以被认为 “同构”,因为应用程序的大部分代码都可以在服务器和客户端上运行。
    二、为什么使用服务器端渲染?
    服务器端渲染(SSR)的优势主要在于:
    更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
    更好的用户体验,对于缓慢的网络情况或运行缓慢的设备,加载完资源浏览器直接呈现,无需等待所有的 JavaScript 都完成下载并执行,才显示服务器渲染的HTML。
    三、服务端渲染方式
    方式一:
    传统方式服务端渲染,解决用户体验和更好的 SEO,有诸多工具使用这种方式如React的(Next.js)、Vue的(Nuxt.js)等。有些工具将 webpack 运行在服务端生产环境,实时编译,将编译结果缓存起来,这都还是传统的方式,只不过将 webpack 运行在服务端实时编译,还是开发环境编译预编译好的问题。
    而这里这里将 webpack 放在开发环境,只做开发打包的功能,打包 客户端 bundle ,服务端 bundle,资源映射文件 assets.json,CSS 等资源进行部署。
    服务器 bundle 用于服务器端渲染(SSR);
    客户端 bundle 给浏览器加载,浏览器通过 bundle 加载更多其它模块(chunk)js;
    资源映射文件 assets.json 则是,服务器 bundle 在准备所需 HTML,需要预插入那些模块(chunk)js,和CSS,这只是提高用户体验。

    ​​

    方式二:
    这是一种创新的方法,前端单页面应用,以前怎么玩儿,现在还怎么玩儿,多的一步是,你得先访问一个Rendora的服务,在前面拦截是否需要服务端渲染。下图为官方图:
    ​​
    这种方式原本只是个想法,想法是前端不用管服务端渲染的事儿了,不就是解决SEO?,这些爬虫过来的时候,可以通过头信息判断,写个服务,然后将需要的内容给爬虫就可以了。
    ​​
    这种方式非常好,之前写好的项目一句不用改,只需新起 Rendora 服务。对于来自前端服务器或外部的每个请求(百度谷歌爬虫),Rendora会根据配置文件,根据头,路径来检测或过滤,以确定 Rendora 是否应该只传递从后端服务器返回的初始HTML或使用Chrome提供的无头服务器端呈现的HTML。更具体地说,对于每个请求,有2条路径:
    1.请求被列入白名单作为SSR的候选者(即过滤后的Get请求),Rendora 会指示无头Chrome实例请求相应的页面,呈现它,并返回包含最终服务器端的响应呈现出HTML。通常只需要将百度、谷歌、必应爬虫等网络抓取工具列入白名单即可。
    2.未列入白名单(即请求不是GET请求或未通过任何过滤器),Rendora将只是充当反向HTTP代理,只是按原样传送请求和响应。
    Rendora可以看作是位于后端服务器(例如Node.js / Express.js,Python / Django等等)之间的反向HTTP代理服务器,也可能是你的前端代理服务器(例如nginx,traefik,apache等)。
    Rendora,新的方式非常厉害,有很多优势:
    1.方便迁移老的项目,前端和后端代码不需要更改;
    2.可能更快的性能,资源(CPU)消耗可能更少,Golang编写的二进制文件;
    3.多种缓存策略;
    4.已经拥有 docker 容器方案。
    最后我更推荐Rendora的方式,这将是未来。

    展开全文
  • 从渲染服务器上的代码得到的两个主要好处是: 加载时间提高性能 提高你的搜索引擎优化的灵活性。 请记住,谷歌确实等待你的JavaScript加载,所以像标题内容简单的事情会改变没有问题。 (我不能代...

    如果您曾经制作过基本的React应用程序页面,则它可能会遇到SEO较差以及在速度较慢的设备上出现性能问题的情况。 您通常可以使用NodeJS添加回传统的服务器端网页渲染,但这并不是一个简单的过程,尤其是对于异步API。

    通过在服务器上呈现代码,您可以获得两个主要好处:

    • 提升加载时间性能
    • 提高您的SEO的灵活性。

    请记住,Google确实会等待您的JavaScript加载,因此标题内容之类的简单事情将毫无问题地发生变化。 (不过,我不能代表其他搜索引擎,或者说它的可靠性如何。)

    在本文中,我将讨论使用服务器渲染的React代码时如何从异步API获取数据。 React代码具有JavaScript内置的应用程序的整个结构。 这意味着,与带有控制器的传统MVC模式不同,在呈现应用程序之前,您不知道需要什么数据。 使用诸如Create React App之类的框架,您可以快速创建质量很高的工作应用程序,但是它要求您仅在客户端上处理渲染。 这有一个性能问题,还有一个SEO /数据问题,在传统的模板引擎中,您可以根据需要更改磁头。

    问题

    React在大多数情况下都是同步渲染的,因此,如果没有数据,则渲染加载屏幕并等待数据来临。 这在服务器上不能很好地工作,因为在渲染之前,您不知道所需的内容,或者您​​已经知道了所需的内容。

    查看以下库存标准渲染方法:

    ReactDOM.render(
      <provider store={store}>
        <browserrouter>
          <app></app>
        </browserrouter>
      </provider>
    , document.getElementById('root')
    )
    

    问题:

    1. 它是DOM渲染,用于寻找根元素。 这在我的服务器上不存在,因此我们必须将其分开。
    2. 我们无法访问除主根元素之外的任何内容。 我们无法设置Facebook标签,标题,描述,各种SEO标签,并且无法控制元素之外的DOM其余部分,尤其是头部。
    3. 我们提供了一些状态,但是服务器和客户端具有不同的状态。 我们需要考虑如何处理该状态(在本例中为Redux)。

    因此,我在这里使用了两个库,它们非常流行,因此希望它可以延续到您正在使用的其他库中。

    Redux :存储服务器和客户端同步状态是一个噩梦。 这非常昂贵,通常会导致复杂的错误。 在服务器端,理想情况下,除了足以使工作正常进行和呈现之外,您不希望对Redux进行任何操作。 (您仍然可以照常使用它;只需设置足够的状态以使其看起来像客户端即可。)如果要尝试,请查看各种分布式系统指南作为起点。

    React-Router :仅供参考,这是默认安装的v4版本,但是如果您有较旧的现有项目,则有很大的不同。 您需要确保使用v4处理路由服务器端客户端,这非常好。

    毕竟,如果需要进行数据库调用怎么办? 突然,这成为一个大问题,因为它是异步的并且在组件内部。 当然,这不是新问题:在官方的React repo上查看。

    您必须进行渲染才能确定所需的依赖项(需要在运行时确定),并在提供给客户端之前获取这些依赖项。

    现有解决方案

    下面,我将回顾当前提供的解决此问题的解决方案。

    Next.js

    在我们进行任何工作之前,如果您要生产,服务器端渲染的React代码或通用应用程序, 那么Next.js ]是您想要去的地方。 它可以正常工作,很干净,并且有Zeit支持。

    但是,有人认为,您必须使用他们的工具链,并且他们处理异步数据加载的方式不一定那么灵活。

    从Next.js存储库文档中检查此直接副本:

    import React from 'react'
    export default class extends React.Component {
      static async getInitialProps ({ req }) {
        return req
          ? { userAgent: req.headers['user-agent'] }
          : { userAgent: navigator.userAgent }
      }
      render () {
        return <div>
          Hello World {this.props.userAgent}
        </div>
      }
    }
    

    getInitialProps是其中的键,它返回一个承诺,该承诺将解析为仅在页面上填充道具的对象。 很棒的是,它们只是内置在他们的工具链中:添加它并可以工作,不需要任何工作!

    那么如何获取数据库数据? 您进行API调用。 你不想吗 好吧,那太糟糕了。 (好的,因此您可以添加自定义内容,但是您必须自己完全实现它。)但是,如果考虑到这一点,这是非常合理的,并且通常来说是一种好的做法,因为否则,您的客户仍然会相同的API调用,服务器上的延迟几乎可以忽略不计。

    您也只能访问请求对象,几乎就是请求对象。 再说一次,这似乎是个好习惯,因为您无权访问状态,无论您的服务器还是客户端,状态都不同。 哦,以防万一您以前没有注意到它,它仅适用于顶级页面组件。

    Redux Connect

    Redux Connect是一个非常自以为是的服务器端渲染器,具有不错的理念,但是如果您不使用它们描述的所有工具,则可能不适合您。 这个软件包有很多东西,但是它是如此复杂,还没有升级到React Router v4。 有很多设置,但是让我们起最重要的作用,只是学习一些经验教训:

    // 1. Connect your data, similar to react-redux @connect
    @asyncConnect([{
      key: 'lunch',
      promise: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
    }])
    class App extends React.Component {
      render() {
        // 2. access data as props
        const lunch = this.props.lunch
        return (
          <div>{lunch.name}</div>
        )
      }
    }
    

    装饰器在JavaScript中不是标准的。 在撰写本文时,它们是第2阶段,因此请自行决定使用。 这只是添加高阶组件的另一种方法。 这个想法很简单:关键是要传递给道具的内容,然后您会得到一个承诺清单,该清单可以解决并被传递。这看起来不错。 也许替代方法就是这样:

    @asyncConnect([{
      lunch: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
    }])
    

    这似乎对JavaScript可行,没有太多问题。

    反应前负荷

    react-frontload存储库没有很多文档或说明,但是也许我能获得的最佳理解是来自测试(例如测试)。
    并阅读源代码。 挂载某些东西后,它将被添加到一个promise队列中,当解析出该消息后,它将被提供。 它的作用是相当不错的,尽管很难推荐没有充分记录,维护或使用的东西:

    const App = () => (
      <frontload isServer >
        <component1 entityId='1' store={store}></component1>
      </frontload>
    )
    
    return frontloadServerRender(() => (
      render(<app></app>)
    )).then((serverRenderedMarkup) => {
      console.log(serverRenderedMarkup)
    })
    

    寻找更好的解决方案

    上面的解决方案都没有真正引起我对库所期望的灵活性和简单性的共鸣,因此现在我将介绍自己的实现。 目标不是编写程序包,而是让您了解如何为您的用例编写自己的程序包。

    这个示例解决方案的仓库在这里

    理论

    尽管最终它是很多代码,但其背后的想法相对简单。 这是对我们正在讨论的想法的概述。

    服务器必须渲染两次React代码,而我们只使用renderToString 我们想要在第一和第二渲染之间保持上下文。 在我们的第一个渲染中,我们正在尝试消除所有API调用,承诺和异步操作。 在第二个渲染中,我们希望获取所有获取的数据并将其放回上下文中,因此渲染出我们的工作页面以进行分发。 这也意味着应用程序代码需要根据上下文执行操作(或不执行),例如,是在服务器上还是在客户端上,无论哪种情况,是否都在获取数据。

    另外,我们可以根据需要自定义此内容。 在这种情况下,我们根据上下文更改状态代码和头。

    第一次渲染

    在代码内部,您需要知道您正在使用服务器或浏览器,并且理想情况下,您希望对其进行复杂的控制。 使用React Router ,您将获得一个静态上下文道具,这很好,因此我们将使用它。 现在,我们已经从Next.js中学到了一个数据对象和一个请求数据。 服务器和客户端之间我们的API不同,因此您需要提供服务器API,最好提供与客户端API类似的接口:

    const context = {data: {}, head: [], req, api}
    const store = configureStore()
    renderToString(
      <provider store={store}>
        <staticrouter location={req.url}
          context={context}
        >
          <app></app>
        </staticrouter>
      </provider>
    )
    

    第二次渲染

    在您第一次渲染之后,我们将立即获取那些未完成的promise,并等到这些promise完成,然后重新渲染,更新上下文:

    const keys = Object.keys(context.data)
    const promises = keys.map(k=>context.data[k])
    try {
      const resolved = await Promise.all(promises)
      resolved.forEach((r,i)=>context.data[keys[i]]=r)
    } catch (err) {
      // Render a better page than that? or just send the original markup, let the front end handle it. Many options here
      return res.status(400).json({message: "Uhhh, some thing didn't work"})
    }
    const markup = renderToString(
      <provider store={store}>
        <staticrouter location={req.url}
          context={context}
        >
          <app></app>
        </staticrouter>
      </provider>
    )
    

    应用程式

    从我们的服务器快速跳转到应用程序代码:在我们所有具有路由器连接的组件中,现在都可以得到:

    class FirstPage extends Component {
      async componentWillMount(){
        this.state = {text: 'loading'}
    
        this._handleData('firstPage')
      }
      async _handleData(key){
        const {staticContext} = this.props
    
        if (staticContext && staticContext.data[key]){
          const {text, data} = staticContext.data[key]
          this.setState({text, data})
          staticContext.head.push(
            <meta name="description" content={"Some description: "+text}/>
          )
        } else if (staticContext){
          staticContext.data[key] = this._getData()
        } else if (!staticContext && window.DATA[key]){
          const {text, data} = window.DATA[key]
          this.state = {...this.state, text, data}
          window.DATA[key] = null
        } else if (!staticContext) {
          const {text, data} = await this._getData()
          this.setState({text, data})
        }
      }
      async _getData(){
        const {staticContext} = this.props
        const myApi = staticContext ? staticContext.api : api
        const resp = await butter.post.list()
        const {data} = resp.data
        const {text} = await myApi.getMain()
        return {text, data}
      }
      render() {
        const text = this.state.text
        return (
          <div className='FirstPage'>
            {text}
          </div>
        )
      }
    }
    

    哇,那是很多复杂的代码。 在此阶段,您可能希望采用一种更中继的方法,在该方法中,将数据提取代码分离到另一个组件中。

    该组件由您可能熟悉的事物(即渲染步骤和componentWillMount步骤)预订。 四个阶段的if语句处理不同的状态-预取,后取,保留器渲染,服务器后渲染。 加载数据后,我们还会添加到头部。

    最后,有一个获取数据的步骤。 理想情况下,您的API和数据库具有相同的API,这使执行相同。 您可能希望将它们放入Thunk或Saga中以使其更具扩展性。

    查看文章“ 服务器端React渲染 ”和repo React服务器端渲染以获得更多信息。 请记住,您仍然需要处理未加载数据的状态! 您只会在首次加载时进行服务器渲染,因此将在后续页面上显示加载屏幕。

    更改index.html以添加数据

    我们需要发送任何预取的数据作为页面请求的一部分,因此我们将添加一个脚本标签:

    <script>
    window.DATA = {data:{}} // It doesn't really matter what this is, just keep it valid and replaceable
    </script>
    

    服务

    然后,我们需要将其添加到我们的搜索和替换中。 但是,HTML使用了非常基本的脚本标签查找器,因此,如果您具有脚本标签,则需要对它进行base-64编码。 另外,不要忘记我们的头部标签!

    // earlier on
    const headMarkup = context.head.map(h=>(
      renderToStaticMarkup(h)
    )).join('')
    
    // then render
    const RenderedApp = htmlData.replace('{{SSR}}', markup)
      .replace('{{head}}', headMarkup)
      .replace('{data:{}}', JSON.stringify(new Buffer(JSON.stringify(context.data)).toString('base64')))
    if (context.code)
      res.status(context.code)
    res.send(RenderedApp)
    

    我们还处理状态码更改(例如404),因此,如果您有404页面,则可以执行以下操作:

    class NoMatch extends Component {
      componentWillMount(){
        const {staticContext} = this.props
        if (staticContext){
          staticContext.code = 404
        }
      }
      render() {
        return (
          <div>
            Sorry, page not found
          </div>
        )
      }
    }
    

    摘要

    如果不确定自己在做什么,请使用Next.js。 它是为服务器端渲染和通用应用程序设计的,或者如果您希望以自己想要的方式灵活地手动执行所有操作。 一个示例可能包括您是否在子组件中而不是在页面级别上获取数据。

    希望本文对您有所帮助! 不要忘记签出GitHub存储库以获取有效的实现。

    From: https://www.sitepoint.com/asynchronous-apis-server-rendered-react/

    展开全文
  • 启动react服务器命令

    千次阅读 2016-06-28 17:45:33
    启动react服务器命令 sudo react-native start 运行模拟器 react-native run-ios
  • dva+react搭建的框架环境,npm run build 打包出来的dist文件夹怎么部署在服务器上呀, 试过在服务器上装了node,然后npm serve,运行那个文件夹中的东西,虽然成功了 但是还是不能访问。请问大神是怎么样运行
  • 调整nginx正确服务react-router应用

    万次阅读 2017-01-09 11:06:35
    如今react应用普遍使用react-router作为路由管理,开发端webpack自带的express服务器运行和测试表现均正常,部署到线上的nginx服务器后,还需要对该应用nginx的配置里作相应调整,否则浏览器将不能正常使用该...
  • react项目的安装与运行

    万次阅读 2017-12-27 11:01:44
     淘宝镜像 不是安装 是所有node设置的关键 作用是 不去访问国外服务器 直接去淘宝服务器下载 插件  npm install -g cnpm --registry=https://registry.npm.taobao.org  3.进入项目 cd [路劲自己打] ...
  • 打包 React 项目并在服务器运行

    千次阅读 2019-06-04 14:30:38
    微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 1.找到项目根目录的package.json文件:如图: ...4.把DIST文件放到服务器phpStudty根目录,访问index.html。 完成。 ...
  • React项目的打包与部署到腾讯云

    万次阅读 2017-03-16 23:43:50
    腾讯云送了30天的免费试用,于是有了把react项目部署到上面的想法。项目是默认生成的,只是一个页面,但是这个过程中也遇到了不少麻烦与问题。下面来具体梳理下: create-react-app  来自Facebook官方的零配置...
  • 1.React项目创建 npm install -g create-react-app create-react-app myProjectName 创建基于Webpack与ES6的简单项目模板 ...2.React项目打包部署到服务器 npm run build 代码会被编译到build目录...
  • React从开始搭建到项目部署到服务器

    千次阅读 2019-06-04 19:02:04
    什么是React?Vue跟React相比的区别是哪些?为什么大厂都会选择React? 区别 设计模式 React采用的是MVC模式(严格的view层);Vue采用的是MVVM模式; 组件写法不同; react主张jsx+inline style,也就是讲...
  • 第一种方式:安装依赖react-scripts 第二种方式: 使用webpack-dev-server来配置启动WEB服务器。 来自链接webpack-dev-server
  • React项目的打包与部署

    万次阅读 2019-03-15 16:12:13
    执行完上述命令,打开本地浏览器,输入localhost:3000,就能看到项目的运行效果。此时是开发模式。 若使用npm run build,代码会被编译到build目录。将整个应用打包发布,自动试用webpack进行压缩与优化。 项目...
  • React项目如何打包发布及遇到的坑

    万次阅读 2018-06-29 15:57:24
    刚开始学习react,遇到一些小问题。我使用的是create-react-app脚手架生成的项目。这个脚手架一键生成react项目,非常方便。先简单记录一下这个新建项目的过程。一、打包1、安装使用npm install -g create-react-app...
  • ssr与csr的优异

    千次阅读 2018-11-06 10:17:25
    浏览器下载html文档→下载js→运行react代码渲染页面→展示页面 优势: 1.前后端分离的架构,利于开发效率的提升 缺点: 1、TTFP 首屏渲染时间比较长(首屏加载速度慢) 2、不能SEO (搜索引擎优化) 。大多数搜索...
  • nginx 部署 react 项目

    万次阅读 2018-02-28 17:39:16
    测试项目:react-demo克隆你的react-demo项目到服务器上(默认使用Github管理我们的项目)如果需要,请安装项目环境,比如:node.js,yarn等进入项目目录,执行npm run build,开始构建项目构建成功之后,会生成一个...
  • 当我们运行一个React Native项目的时候,React Native会启动一个默认端口号为8081的本地服务,该8081的服务就是React Native项目的一个本地服务器,用于提供JSBundle包和一些静态资源。这一切都是React Native帮...
  • 一、创建新项目并运行 1、新建项目 计算机中新建一个文件夹存放工作文件(workspace),并打开终端,进入该文件夹目录下,创建新项目:本项目名为Test react-native init Test Enter回车进行创建 //本文...
  • React Native 常用命令

    万次阅读 2017-09-20 14:59:45
    1.react-native --version //查看当前reactNative版本 2.sudo npm update -g react-native-cli ...3.npm info react-native //查看服务器端的reactNative的各版本信息 4.npm install //给下载的普通工程下载安装RN环境
1 2 3 4 5 ... 20
收藏数 25,879
精华内容 10,351
关键字:

在服务器上怎么运行react