这次我要上SSR

栏目: JavaScript · 发布时间: 4年前

  1. SEO:让各爬虫能爬到首屏的内容,匹配到搜索关键字,提高在百度谷歌的排序
  2. 加快首屏速度,不再是加载完js然后 ReactDom.render('#app', Com) ,让服务器分担部分渲染的压力

精简易懂的内容来了

SSR怎么实现首屏渲染

  1. 通过express服务来返回给浏览器首屏的HTML
  2. HTML是由 react-dom/server 提供的 renderToString 来生成,理论依据是node虽然不能识别HTML和JSX,但React能生成虚拟DOM,然后插入HTML直接返回给浏览器识别渲染
import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router-dom";
const app = express();
app.get( "/*", (req, res) => {
  const context = {}; // 上下文对象,可以存储有关渲染的信息
  const jsx = (
    <StaticRouter context={ context } location={ req.url }>
      <App />
    </StaticRouter>
  );
  const reactDom = renderToString( jsx ); // 在服务端将JSX渲染为HTML
  res.writeHead( 200, { "Content-Type": "text/html" } );
  res.end(`
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <link rel="icon" href="data:;base64,=">
      <title>React SSR</title>
    </head>
    <body>
      <div id="app">${ reactDom }</div>
      <script src="/index.js"></script> // 注意这里,服务端返回的HTMl会去加载webpack打包好的SPA应用,然后客户端接管应用,绑定事件,处理路由跳转和其他Ajax请求
    </body>
    </html>
  `);
});
app.listen(3000, () => {
  console.log('Running on http://localhost:3000/');
});
复制代码
const jsx = (
    <Provider store={store}>
      <Router>
        <Layout />
      </Router>
    </Provider>
);
const app = document.getElementById( "app" );
ReactDOM.hydrate( jsx, app ); // 客户端接管应用后,React会将服务端渲染的标记加载,并将尝试将事件侦听器附加到现有标记
复制代码

结合Router

  1. 服务端通过StaticRouter来渲染对应的组件,根据浏览器请求的url来匹配路由并返回对应组件
  2. 返回给浏览器后,加载webpack打包好的客户端js,然后接管页面
  3. 具体看代码哈

结合Redux

  1. 首屏页一般要请求接口渲染对应数据
  2. 将请求的数据设置到状态树上,并将JSON一并返回给浏览器
    <script>
        window.REDUX_DATA = ${ JSON.stringify( state ) }
    </script>
    复制代码
  3. 客户端接管应用后将数据初始化到客户端的状态树上
    const store = createClientStore( window.REDUX_DATA );
    const jsx = (
        <Provider store={store}>
          <Router>
            <Layout />
          </Router>
        </Provider>
    );
    复制代码

怎么按需加载

  1. 使用 react-lodable 按需加载,其原理是 import().then() ,webpack在识别到后就能代码分割了
  2. 其会生成 react-loadable.json 来标识需要动态加载的组件
    const jsx = (
      <Provider store={store}>
        <Loadable.Capture report={moduleName => modules.push(moduleName)}> // 需要懒加载的模块,最后在返回的HTML中去遍历
          <StaticRouter context={ context } location={ req.url }>
              <Layout />
          </StaticRouter>
        </Loadable.Capture>
      </Provider>
    );
    
    ${bundles.map(bundle => {
        return `<script src="/${bundle.file}"></script>`
        // alternatively if you are using publicPath option in webpack config
        // you can use the publicPath value from bundle, e.g:
        // return `<script src="${bundle.publicPath}"></script>`
      }).join('\n')}
    复制代码
  3. lodable实现原理
    Lodable({
      loader: ()=> import(/* webpackChunkName: 'Hello' */'./Hello'),
      loading,
    })
    class Lodable {
      componentWillMount() {
        this.cancelUpdate = false;
        const { loader } = this.props;
        loader.then(Com=> {
          this.Com = Com;
          if(!this.cancelUpdate) {
            thi.forceUpdate(); // 初次懒加载完后重新渲染
          }
        })
      }
      componentWillUnmount() {
        this.cancelUpdate = true;
      }
      render() {
        const { comProps } = this.props;
        return this.Com ? (
          <this.Com.default {...comProps} />
        ) : (
          <this.Com {...comProps}/>
        )
      }
    }
    复制代码

服务端怎么处理CSS和图片

  1. 服务端还是通过webpack来编译对应的node端的代码,来实现对CSS和图片资源的引用和处理
  2. 其实这里只要理解客户端和服务端分别打包,最后node运行服务端的js,返回HTML后加载客户端js,实现客户端接管应用
  3. 处理静态资源还有 webpack-isomorphic-tools , universal-webpack ,前者作者已经不更新了,我后面发现其实用webpack直接处理简单多了,可能是应用场景还用不到
    const serverConfig = {
      target: 'node', // 标记为node端
      mode: 'development',
      entry: './src/server',
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build')
      },
      externals: [nodeExternals()], // 不会讲node_modules打包进去
      module: {
        rules: [{
          test: /\.js?$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
          options: {
            cacheDirectory: true,
            plugins: ['transform-decorators-legacy'],
            presets: ["react", ["env", {
              "targets": {
                "node": "current"
              }
            }], "stage-0"],
          }
        }, {
          test: /\.css?$/,
          use: ['isomorphic-style-loader', { // 处理CSS服务端的loader
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              modules: true,    // 开启css-module
              localIdentName: '[name]_[local]_[hash:base64:5]' //生成的class的命名规则
            }
          }]
        }, {
          test: /\.(png|jpeg|jpg|gif|svg)?$/,
          loader: 'url-loader',
          options: {
            limit: 8000,
            outputPath: '../build/',
            publicPath: '/'
          }
        }]
      },
      plugins: [
        new ReactLoadablePlugin({
          filename: './build/react-loadable.json',  // 按需加载的配置文件
        }),
      ]
    };
    复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Operating System Algorithms

Operating System Algorithms

Nathan Adams、Elisha Chirchir / CreateSpace Independent Publishing Platform / 2017-4-21 / USD 39.15

Operating System Algorithms will walk you through in depth examples of algorithms that you would find in an operating system. Selected algorithms include process and disk scheduling.一起来看看 《Operating System Algorithms》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具