使用react的static方法实现同构以及同构的常见问题(代码片段)

llguanli llguanli     2022-10-28     610

关键词:

代码地址请在github查看,假设有新内容。我会定时更新。也欢迎您star,issue,共同进步

1.我们服务端渲染数据从何而来

1.1 怎样写出同构的组件

服务端生成HTML结构有时候并不完好。有时候不借助js是不行的。比方当我们的组件须要轮询服务器的数据接口,实现数据与服务器同步的时候就显得非常重要。事实上这个获取数据的过程能够是数据库获取,也能够是从其它的反向代理服务器来获取。

对于client来说,我们能够通过ajax请求来完毕,仅仅要将ajax请求放到componentDidMount方法中来完毕就能够。

而之所以放在该方法中有两个原因,第一个是为了保证此时DOM已经挂载到页面中;还有一个原因是在该方法中调用setState会导致组件又一次渲染(详细你能够查看这个文章)。而对于服务端来说,
一方面它要做的事情便是:去数据库或者反向代理服务器拉取数据 -> 依据数据生成HTML -> 吐给client。这是一个固定的过程,拉取数据和生成HTML过程是不可打乱顺序的。不存在先把内容吐给client,再拉取数据这种异步过程。所以,componentDidMount在服务器渲染组件的时候,就不适用了(由于render方法已经调用,可是componentDidMount还没有运行,所以渲染得到的是没有数据的组件。原因在于生命周期方法componentDidMount在render之后才会调用)。

还有一方面,componentDidMount这种方法,在服务端确实永远都不会运行!因此我们要採用和client渲染全然不一致的方法来解决渲染之前数据不存在问题。

关于服务端渲染和client渲染的差别你能够查看Node直出理论与实践总结

var React = require(‘react‘);
var DOM = React.DOM;
var table = DOM.table, tr = DOM.tr, td = DOM.td;
var Data = require(‘./data‘);
module.exports = React.createClass(
    statics: 
        //获取数据在实际生产环境中是个异步过程,所以我们的代码也须要是异步的
        fetchData: function (callback) 
            Data.fetch().then(function (datas) 
                callback.call(null, datas);
            );
        
    ,
    render: function () 
        return table(
                children: this.props.datas.map(function (data) 
                    return tr(null,
                        td(null, data.name),
                        td(null, data.age),
                        td(null, data.gender)
                    );
                )
            );
    ,
    componentDidMount: function () 
        setInterval(function () 
            // 组件内部调用statics方法时,使用this.constructor.xxx
            // client在componentDidMount中获取数据,并调用setState改动状态要求
            // 组件又一次渲染
            this.constructor.fetchData(function (datas) 
                this.setProps(
                    datas: datas
                );
            );
        , 3000);
    
);

当中服务器端的处理逻辑render-server.js例如以下:

var React = require(‘react‘);
var ReactDOMServer = require(‘react-dom/server‘);
// table类
var Table = require(‘./Table‘);
// table实例
var table = React.createFactory(Table);
module.exports = function (callback) 
    //在client调用Data.fetch时,是发起ajax请求。而在服务端调用Data.fetch时,
    //有可能是通过UDP协议从其它数据服务器获取数据、查询数据库等实现
    Table.fetchData(function (datas) 
        var html = ReactDOMServer.renderToString(table(datas: datas));
        callback.call(null, html);
    );
;

以下是服务器的逻辑server.js:

var makeTable = require(‘./render-server‘);
var http = require(‘http‘);
//注冊中间件
http.createServer(function (req, res) 
    if (req.url === ‘/‘) 
        res.writeHead(200, ‘Content-Type‘: ‘text/html‘);
        //先訪问数据库或者反代理服务器来获取到数据,并注冊回调,将含有数据的html结构返回给client,此处仅仅是渲染一个组件。否则须要renderProps.components.forEach来遍历全部的组件获取数据
        //http://www.toutiao.com/i6284121573897011714/
        makeTable(function (table) 
            var html = ‘<!doctype html>\n                      <html>                        <head>                            <title>react server render</title>                        </head>                        <body>‘ +
                            table +
                            //这里是client的代码。实现每隔一定事件更新数据,至于怎样加入以下的script标签内容,能够參考这里https://github.com/liangklfangl/react-universal-bucket
                            ‘<script src="pack.js"></script>                        </body>                      </html>‘;
            res.end(html);
        );
     else 
        res.statusCode = 404;
        res.end();
    
).listen(1337, "127.0.0.1");
console.log(‘Server running at http://127.0.0.1:1337/‘);

注意:由于我们的react服务端渲染仅仅是一次性的。不会随着调用setState而又一次reRender,所以我们须要在返回给client的html中加入client的代码,真正的每隔一定时间更新组件的逻辑是client通过ajax来完毕的。

1.2 怎样避免服务端渲染后client再次渲染

服务端生成的data-react-checksum是干嘛使的?我们想一想。就算服务端没有初始化HTML数据,仅仅依靠client的React也全然能够实现渲染我们的组件,那服务端生成了HTML数据。会不会在clientReact运行的时候被又一次渲染呢?我们服务端辛辛苦苦生成的东西,被client无情地覆盖了?当然不会!

React在服务端渲染的时候,会为组件生成相应的校验和(在redux的情况下事实上应该是一个组件树,为整个组件树生成校验和,由于这整个组件树就是我们首页要显示的内容)(checksum)。这样clientReact在处理同一个组件的时候,会复用服务端已生成的初始DOM,增量更新(也就是说当client和服务端的checksum不一致的情况下才会进行dom diff,进行增量更新),这就是data-react-checksum的作用。能够通过以下的几句话来总结下:

 假设data-react-checksum同样则不又一次render。省略创建DOM和挂载DOM的过程,接着触发 componentDidMount 等事件来处理服务端上的未尽事宜(事件绑定等),从而加快了交互时间;不同一时候,组件在client上被又一次挂载 render。

ReactDOMServer.renderToString 和 ReactDOMServer.renderToStaticMarkup 的差别在这个时候就非常好解释了。前者会为组件生成checksum,而后者不会。后者仅仅生成HTML结构数据。所以,仅仅有你不想在client-服务端同一时候操作同一个组件的时候,方可使用renderToStaticMarkup。注意:上面使用了statics块,该写法仅仅在createClass中可用。你能够使用以下的写法:

//组件内的写法
class Component extends React.Component 
    static propTypes = 
    ...
    
    static someMethod()
    

在组件外面你能够依照例如以下写法:

class Component extends React.Component 
   ....

Component.propTypes = ...
Component.someMethod = function()....

详细你能够查看这里

关于服务端渲染经常会出现以下的warning,大多数情况下是由于在返回 HTML 的时候没有将服务端上的数据一同返回,或者是返回的数据格式不正确导致

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generatted on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Insted, figure out why the markup being generated is different on the client and server

2.怎样区分client与服务端代码

2.1 加入client代码到服务端渲染的html字符串

通过这个样例我们知道,将webpack-isomorphic-tools这个插件加入到webpack的plugin中:

module.exports = 
    entry:
        ‘main‘: [
          ‘webpack-hot-middleware/client?path=http://‘ + host + ‘:‘ + port + ‘/__webpack_hmr‘,
        // "bootstrap-webpack!./src/theme/bootstrap.config.js",
        "bootstrap-loader",
        //确保安装bootstrap3,bootstrap4不支持less
          ‘./src/client.js‘
        ]
    ,
   output: 
      path: assetsPath,
      filename: ‘[name]-[hash].js‘,
      chunkFilename: ‘[name]-[chunkhash].js‘,
      publicPath: ‘http://‘ + host + ‘:‘ + port + ‘/dist/‘
      //表示要訪问我们client打包好的资源必须在前面加上的前缀。也就是虚拟路径
    ,
    plugins:[
        new webpack.DefinePlugin(
          __CLIENT__: true,
          __SERVER__: false,
          __DEVELOPMENT__: true,
          __DEVTOOLS__: true //,
        ),
     webpackIsomorphicToolsPlugin.development()
     //在webpack的development模式下一定更要调用它支持asset hold reloading!
     //https://github.com/liangklfang/webpack-isomorphic-tools
    ]

此时我们client.js会被打包到相应的文件路径下。然后在我们的模版中,仅仅要将这个打包好的script文件加入到html返回给client就能够了。以下是遍历我们的webpack-assets.json来获取到我们全部的产生的资源,然后加入到html模板中返回的逻辑:

export default class Html extends Component 
  static propTypes = 
    assets: PropTypes.object,
    component: PropTypes.node,
    store: PropTypes.object
  ;
  render() 
    const assets, component, store = this.props;
    const content = component ? renderToString(component) : ‘‘;
    //假设有组件component传递过来,那么我们直接调用renderToString
    const head = Helmet.rewind();
    return (
      <html lang="en-us">
        <head>
          head.base.toComponent()
          head.title.toComponent()
          head.meta.toComponent()
          head.link.toComponent()
          head.script.toComponent()
          <link rel="shortcut icon" href="/favicon.ico" />
         <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css"/>
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Work+Sans:400,500"/>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/violet/0.0.1/violet.min.css"/>
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          /* styles (will be present only in production with webpack extract text plugin)
             styles属性仅仅有在生产模式下才会存在,此时通过link来加入。

便于缓存 */ Object.keys(assets.styles).map((style, key) => <link href=assets.styles[style] key=key media="screen, projection" rel="stylesheet" type="text/css" charSet="UTF-8"/> ) /* assets.styles假设开发模式下,那么肯定是空,那么我们直接採用内联的方式来插入就可以。

此时我们的css没有单独抽取出来,也就是没有ExtractTextWebpackPlugin。打包到js中从而内联进来 */ /* (will be present only in development mode) */ /* outputs a <style/> tag with all bootstrap styles + App.scss + it could be CurrentPage.scss. */ /* can smoothen the initial style flash (flicker) on page load in development mode. */ /* ideally one could also include here the style for the current page (Home.scss, About.scss, etc) */ </head> <body> <div id="content" dangerouslySetInnerHTML=__html: content/> /*将组件renderToString后放在id为content的div内部*/ <script dangerouslySetInnerHTML=__html: `window.__data=$serialize(store.getState());` charSet="UTF-8"/> /*将store.getState序列化后放在window.__data上,让client代码能够拿到*/ <script src=assets.javascript.main charSet="UTF-8"/> /*将我们的main.js,来自于client打包并放在特定文件夹下的资源放在页面中, 这就成了client自己的js资源了 */ </body> </html> );

所以说以下的div#content中是服务端渲染后得到的html字符串,并被原样返回给client。

这种话。对于服务端的任务就完毕了

 <div id="content" dangerouslySetInnerHTML=__html: content/>

而我们的以下的script标签的内容就是我们的client代码打包后的结果:

   <script src=assets.javascript.main charSet="UTF-8"/>

此时client和服务端的逻辑都已经完毕了,client能够继续接收用户操作而发送ajax请求更新组件状态。

2.2 怎样使得服务端和client发起请求的逻辑通用

一个好的使用方法在于使用isomorphic-fetch

2.3 immutable数据在同构中的注意事项

首先在服务端返回的时候必须将store.getState得到的结果序列化,并且此时假设store返回的某一个部分state是immutbale的,那么client要又一次通过这部分state数据来创建新的immutable对象(如以下的样例中我们的recipeGrid和connect是immutable的):

  <script dangerouslySetInnerHTML=__html: `window.__data=$serialize(store.getState());` charSet="UTF-8"/>

对于client来说,我们必须将从服务端注入到HTML上的state数据转成 immutable对象,并将该对象作为initialState来创建store:

  const data = window.__data;
  //当中data是服务端返回的store.getState的值。也就是store的当前状态
  if (data) 
     data.recipeGrid = Immutable.fromJS(data.recipeGrid);
     //这里必须设置,否则报错说:paginator.equals is not a function
      data.connect = Immutable.fromJS(data.connect);
     //能够使用https://github.com/liangklfang/redux-immutablejs
  
  const store = finalCreateStore(reducer, data);
2.4 服务端server不支持ES6的兼容

假设你想在服务端使用import等ES6的语法的话。你能够採用以下的方式。首先在项目的根文件夹下配置.babelrc文件,内容例如以下:


  "presets": ["react", "es2015", "stage-0"],
  "plugins": [
    "transform-runtime",
    "add-module-exports",
    "transform-decorators-legacy",
    "transform-react-display-name"
  ]

然后配置一个单独的文件server.babel.js:

const fs = require("fs");
const babelrc = fs.readFileSync("./.babelrc");
let config ;
try
    config = JSON.parse(babelrc);
catch(err)
    console.error("你的.babelrc文件有误,请细致检查");
    console.error(err);

//你能够指定ignore配置来忽略某些文件。
//https://github.com/babel/babel/tree/master/packages/babel-register
require("babel-register")(config);
//require("babel-register")会导致以后全部的.es6,.es,.js,.jsx的文件都会被babel处理

最后我们加入我们的server.js,内容例如以下(直接node server.js,而真正的逻辑放在../src/server中):

#!/usr/bin/env node
require(‘../server.babel‘); // babel registration (runtime transpilation for node)
var path = require(‘path‘);
var rootDir = path.resolve(__dirname, ‘..‘);
global.__CLIENT__ = false;
global.__SERVER__ = true;
global.__DISABLE_SSR__ = false;  
// <----- DISABLES SERVER SIDE RENDERING FOR ERROR DEBUGGING
global.__DEVELOPMENT__ = process.env.NODE_ENV !== ‘production‘;
if (__DEVELOPMENT__) 
//服务端代码热载入
  if (!require(‘piping‘)(
      hook: true,
      ignore: /(\/\.|~$|\.json|\.scss$)/i
    )) 
    return;
  

// https://github.com/halt-hammerzeit/webpack-isomorphic-tools
var WebpackIsomorphicTools = require(‘webpack-isomorphic-tools‘);
global.webpackIsomorphicTools = new WebpackIsomorphicTools(require(‘../webpack/webpack-isomorphic-tools-config‘))
  .development(__DEVELOPMENT__)
  .server(rootDir, function() 
  //rootDir必须和webpack的context一致。调用这种方法服务器就能够直接require不论什么资源了
  //这个路径用于获取webpack-assets.json文件,这个是webpack输出的
  // webpack-isomorphic-tools is all set now.
  // here goes all your web application code:
  // (it must reside in a separate *.js file 
  //  in order for the whole thing to work)
  //  此时webpack-isomorphic-tools已经注冊好了,这里能够写你的web应用的代码。并且这些代码必须在一个独立的文件里
    require(‘../src/server‘);
  );

经过上面的babel-register的处理,此时你的../src/server.js中能够使用随意ES6的代码了。

2.5 服务端代码单独使用webpack打包

假设对于服务端的代码要单独打包,那么必须进行以下的设置:

target: "node"

你能够參考这里

2.6 服务端渲染之忽略css/less/scss文件

在2.4中我们使用了babel-register帮助服务端识别特殊的js语法,但对less/css文件无能为力,庆幸的是。在普通情况下,服务端渲染不须要样式文件的參与,css文件仅仅要引入到HTML文件里就可以,因此。能够通过配置项。忽略全部 css/less 文件:

require("babel-register")(
  //默认情况ignore是node_modules表示node_modules下的全部文件的require不会进行处理
  //这里明白指定css/less不经过babel处理
  ignore: /(.css|.less)$/, );

详细内容你能够查看babel-register文档

你能够传递其指定的全部的其它选项。包含plugins和presets。

可是有一点要注意。就是距离我们源文件的近期一个.babelrc始终会起作用,同一时候其优先级也要比你在此配置的选项优先级高。

此时我们忽略了样式文件的解析并不会导致client对组件再次渲染,由于我们的checksum和详细的css/less/scss文件无关,仅仅是和组件render的结果有关。

2.7 使用webpack-isomorphic-tools识别css/less/scss文件

通过 babel-register 能够使用babel解决jsx语法问题,对 css/less 仅仅能进行忽略,但在使用了CSS Modules 的情况下。服务端必须能够解析 less文件,才干得到转换后的类名,否者服务端渲染出的HTML结构和打包生成的client css 文件里,类名无法相应。

其原因在于:我们在服务端使用了CSS Module的情况下必须採用例如以下的方式来完毕类名设置:

const React = require("react");
const styles = require("./index.less");
class Test extends React.Component
 render()
     return (
        //假设不是css module。那么可能是这种情况:className="banner"
           <div className=styles.banner>This is banner<\/div>
        )
   

假设服务端无法解析css/less肯定无法得到终于的class的名称(经过css module处理后的className)。从而导致client和服务端渲染得到的组件的checksum不一致(由于class的值不一致)。而对于2.6提到的忽略less/css文件的情况,尽管服务端没有解析该类名,可是我们的组件上已经通过class属性值指定了同样的字符串,因此checksum是全然一致的。

为了解决问题,须要一个额外的工具,即webpack-isomorphic-tools,帮助识别less文件。通过这个工具,我们会将服务器端组件引入的less/css/scss文件进行特别的处理,如以下是Widget组件引入的scss文件被打包成的内容并写入到webpack-assets.json中:

 "./src/containers/Widgets/Widgets.scss": 
      "widgets": "widgets___3TrPB",
      "refreshBtn": "refreshBtn___18-3v",
      "idCol": "idCol___3gf_9",
      "colorCol": "colorCol___2bs_U",
      "sprocketsCol": "sprocketsCol___3nkz0",
      "ownerCol": "ownerCol___fwn86",
      "buttonCol": "buttonCol___1feoO",
      "saving": "saving___7FVQZ",
      "_style": ".widgets___3TrPB .refreshBtn___18-3v \n  margin-left: 20px;\n\n\n.widgets___3TrPB .idCol___3gf_9 \n  width: 5%;\n\n\n.widgets___3TrPB .colorCol___2bs_U \n  width: 20%;\n\n\n.widgets___3TrPB .sprocketsCol___3nkz0 \n  width: 20%;\n  text-align: right;\n\n\n.widgets___3TrPB .sprocketsCol___3nkz0 input \n  text-align: right;\n\n\n.widgets___3TrPB .ownerCol___fwn86 \n  width: 30%;\n\n\n.widgets___3TrPB .buttonCol___1feoO \n  width: 25%;\n\n\n.widgets___3TrPB .buttonCol___1feoO .btn \n  margin: 0 5px;\n\n\n.widgets___3TrPB tr.saving___7FVQZ \n  opacity: 0.8;\n\n\n.widgets___3TrPB tr.saving___7FVQZ .btn[disabled] \n  opacity: 1;\n\n"
    

此时,在服务端你能够使用上面说的styles.banner这种方式来设置className,而不用操心使用babel-register仅仅能忽略css/less/scss文件而无法使用css module特性,从而导致checksum不一致!

详细你能够查看这里

2.8 前后端路由不同的处理

单页应用一个常见的问题在于:全部的代码都会在页面初始化的时候一起载入,即使这部分的代码是不须要的,这经常会产生长时间的白屏。webpack支持将你的代码进行切分,从而切割成为不同的chunk而按需载入。

当我们在特定路由的时候载入该路由须要的代码逻辑,哪些当前页面不须要的逻辑按需载入

对于server-rendering来说,我们服务端不会採用按需载入的方式,而我们的client经常会使用System.import或者require.ensure来实现按需载入

比方以下的样例:

module.exports = 
    path: ‘complex‘,
    getChildRoutes(partialNextState, cb) 
       //假设是服务端渲染。我们将Page1,Page2和其它全部的组件打包到一起,假设是client,那么我们会将Page1,Page2的逻辑单独打包到一个chunk中从而按需载入
        if (ONSERVER) 
            cb(null, [
                require(‘./routes/Page1‘),
                require(‘./routes/Page2‘)
            ])
         else 
            require.ensure([], (require) => 
                cb(null, [
                    require(‘./routes/Page1‘),
                    require(‘./routes/Page2‘)
                ])
            )
        
    ,
    //IndexRoute表示默认载入的子组件,
    getIndexRoute(partialNextState, cb) 
        if (ONSERVER) 
            const  path, getComponent  = require(‘./routes/Page1‘);
            cb(null,  getComponent );
         else 
            require.ensure([], (require) => 
                // separate out the path part, otherwise warning raised
                // 获取下一个模块的path和getComponent,由于他是採用module.export直接导出的
                // 我们直接将getComponent传递给callback函数
                const  path, getComponent  = require(‘./routes/Page1‘);
                cb(null,  getComponent );
            )
        
    ,
    getComponent(nextState, cb) 
        if (ONSERVER) 
            cb(null, require(‘./components/Complex.jsx‘));
         else 
            require.ensure([], (require) => 
                cb(null, require(‘./components/Complex.jsx‘))
            )
        
    

这个样例的路由相应于/complex,假设是服务端渲染,那么我们会将Page1,Page2代码和其它的组件代码打包到一起。

假设是client渲染,那么我们会将Page1,Page2单独打包成为一个chunk。当用户訪问”/complex”的时候才会载入这个chunk。那么为什么服务端渲染要将Page1,Page2一起渲染呢?事实上你要弄清楚,对于服务端渲染来说。将Page1,Page2一起渲染事实上是获取到了该两个子页面的DOM返回给client(形成当前页面的子页面的两个Tab页面)。而client单独载入chunk事实上仅仅是为了让这部分DOM能够响应用户的点击,滚动等事件而已。注意:服务端渲染和我们的req.url有关,如以下的样例:

 match( history, routes: getRoutes(store), location: req.originalUrl , (error, redirectLocation, renderProps) => 
    if (redirectLocation) 
      res.redirect(redirectLocation.pathname + redirectLocation.search);
      //重定向要加入pathname+search
     else if (error) 
      console.error(‘ROUTER ERROR:‘, pretty.render(error));
      res.status(500);
      hydrateOnClient();
      //发送500告诉client请求失败。同一时候不让缓存了
     else if (renderProps) 
      loadOnServer(...renderProps, store, helpers: client).then(() => 
        const component = (
          <Provider store=store key="provider">
            <ReduxAsyncConnect ...renderProps />
          <\/Provider>
        );
        res.status(200);
        global.navigator = userAgent: req.headers[‘user-agent‘];
        res.send(‘<!doctype html>\n‘ +
          renderToString(<Html assets=webpackIsomorphicTools.assets() component=component store=store\/>));
      );
     else 
      res.status(404).send(‘Not found‘);
    
  );
);

我们的服务端依据req.url获取到renderProps,从而将一个组件树渲染成为html字符串返回给client。所以我们服务端不会按需渲染。终于导致的结果仅仅是多渲染了该path下的一部分DOM而已,并且这样有一个优点就是高速响应用户操作(还是要client进行注冊事件等)而不用client又一次render该部分DOM。

而从client来说,我此时仅仅须要载入该path下相应的chunk就能够了,而不是将整个应用的chunk一起载入,从而按需载入,速度更快,更加合理。

服务端match路由须要注意的问题:尽量前置重定向(写到路由的 onEnter 里)。

除非须要拉取数据进行推断,不要在路由确定之后再重定向。

由于在拿到路由配置之后就要依据相应的页面去拉数据了。这之后再重定向就比較浪费。

如以下的样例:

  const requireLogin = (nextState, replace, cb) => 
    function checkAuth() 
      const  auth:  user  = store.getState();
      if (!user) 
        // oops, not logged in, so can‘t be here!
        replace(‘/‘);
      
      cb();
    
    if (!isAuthLoaded(store.getState())) 
      store.dispatch(loadAuth()).then(checkAuth);
     else 
      checkAuth();
    
  ;

以下使用onEnter钩子函数的路由配置:

    <Route onEnter=requireLogin>
       //假设没有登录,那么以下的路由组件根本不会实例化,更不用说拉取数据了
        <Route path="chat" component=Chat/>
        <Route path="loginSuccess" component=LoginSuccess/>
  <\/Route>

參考资料:

React同构思想

React数据获取为什么一定要在componentDidMount里面调用?

ReactJS 生命周期、数据流与事件

React statics with ES6 classes

React同构直出优化总结

腾讯新闻React同构直出优化实践

Node直出理论与实践总结

React+Redux 同构应用开发

ReactJS 服务端同构实践「QQ音乐web团队」

代码拆分 - 使用 require.ensure

性能优化三部曲之三——Node直出让你的网页秒开 #6

同构 React 中的实时环境变量

...repo中的入门套件松散地构建了一个同构React应用程序。它使用webpack构建生产代码。问题是,我需要将服务器上的一些环境变量的值暴露给浏览器中的客户端代码,而不需要重新构建生产代码。我希望能够更改环境变量的值,并... 查看详情

react前后端如何同构,防止重复渲染

...解决某些问题(比如SEO、提升渲染速度等)react提供了2个方法在服务端生成一个HTML文本格式的字符串。在得到了这个HTML格式的字符串之后,通常会将其组装成一个页面直接返回给用户的浏览器。到这里,服务端的活已经干完了... 查看详情

React JS 同构渲染

...现静态代码。在服务器端渲染期间,ComponentWillMount和render方法被启动,但不是ComponentDidMount。即 查看详情

如何在同构 React + React 路由器应用程序中处理发布请求

...请求的同构应用程序。这是我到目前为止所做的:服务器使用r 查看详情

在同构 React 组件中导入 CSS 文件

...nts【发布时间】:2015-08-0114:03:10【问题描述】:我有一个使用ES6编写的组件的React应用程序-通过Babel和Webpack进行转译。如reactwebpackcookbook中所建议的,在某些地方我想包含带有特定组件的特定CSS文件但是,如果在任何组件文件中... 查看详情

浅谈react前后端同构防止重复渲染

...解决某些问题(比如SEO、提升渲染速度等)react提供了2个方法在服务端生成一个HTML文本格式的字符串。在得到了这个HTML格式的字符串之后,通常会将其组装成一个页面直接返回给用户的浏览器。到这里,服务端的活已经干完了... 查看详情

如何在使用同构获取的异常处理承诺后解析 json

】如何在使用同构获取的异常处理承诺后解析json【英文标题】:howtoparsejsonafterexceptionhandlingpromisewithisomorphic-fetch【发布时间】:2016-04-1818:50:48【问题描述】:在使用React、Redux、isomorphic-fetch、ES6Babel实现登录功能期间。问题我不... 查看详情

构建同构 React 应用程序时如何处理 SASS 的导入?

...前设置:反应应用通过webpack编译React应用的Express服务器使用babel-node运行具有ES6特性等的Express应用一切正常,我启 查看详情

如何使 reactjs/flux/react-router spa 同构?

...15-10-1010:07:51【问题描述】:我用reactjs、flux(没有第三方实现)和react-router开发了spa。我现在如何使它同构?用于SEO和更好的初始加载性能。【问题讨论】:这个问题太宽泛了。你试过谷歌搜索“react 查看详情

GraphQL 同构模型

...2016-08-1303:18:43【问题描述】:我计划将Angular2与GraphQL一起使用。我正在寻找一种在客户端上使用GraphQL模型来动态构建查询的方法。我该如何实现这个想法?GraphQL模型看起来像exportsdefaultnewGrapqhQLObjectType(name:\'User\',description:\'A 查看详情

reactJS:具有通量的同构应用程序

】reactJS:具有通量的同构应用程序【英文标题】:reactJS:isomorphicappwithflux【发布时间】:2015-11-0902:07:25【问题描述】:React-flux-cart。此示例仅包含react的客户端代码。如何使其同构。哪些地方需要更改以包含节点并使其在服务器... 查看详情

将 React 应用程序转换为同构应用程序?

...omorphicapp?【发布时间】:2016-07-1605:40:30【问题描述】:我使用React和Redux开发了一个Web应用程序,然后用Webpack打包它,它使用IIS托管,可能只是运行客户端并调用WebAPI(出于某种原因.net);也托管在IIS上。如何进行跳转并使此应... 查看详情

在同构应用程序中使用 redux

】在同构应用程序中使用redux【英文标题】:Usereduxinaisomorphicapp【发布时间】:2016-08-1013:21:25【问题描述】:我正在尝试使用react+redux+express创建一个同构/通用应用程序,但在从server.js创建商店时遇到了一些问题。在server.js我有... 查看详情

pat-树的同构

...先建树,然后判断,知道怎么判断就知道怎么写了,具体实现看代码代码/*Name:helloworld.cppAuthor:AADescription:唯代码与你不可辜负*/#include<bits/stdc++.h> 查看详情

什么是 ReactJS 中的同构渲染方式

】什么是ReactJS中的同构渲染方式【英文标题】:WhatisIsomorphicwayofRenderinginReactJS【发布时间】:2019-05-1010:11:01【问题描述】:Javascript框架不是“搜索引擎友好”的。搜索引擎在动态加载数据时遇到问题,最终框架会创建标记。这... 查看详情

同构 JS - 仅限 httpRequest 客户端

...】:关于同构通量应用中存储数据填充的问题。(我正在使用react、alt、iso和node,但理论适用于其他示例)我有一个通量“存储”(http://alt.js.org/docs/stores/),需要从api获取数据:getState()re 查看详情

vue.js前后端同构方案之准备篇——代码优化

...起到基础作用的。此准备篇是独立的,即使你们的项目不使用Vue.js,也不影响文章的阅读,是代码的基础优化。一、前言目前Vue.js的火爆不亚于当初的React,本人对写代码还是有一定洁癖的,代码也是艺术。很长时间在找寻 查看详情

树同构/树哈希(代码片段)

...;树的同构与哈希判断两棵树是否同构,可以用树哈希实现,两颗树的哈希值一样时,我们可以判定他们同构。树的哈希只在有根树中有意义。当要判定两棵树是否同构时,我们可以选择它们的重心为根进行哈希。... 查看详情