这次我要上SSR

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

  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',  // 按需加载的配置文件
        }),
      ]
    };
    复制代码

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

查看所有标签

猜你喜欢:

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

程序员2010精华本

程序员2010精华本

程序员杂志社 / 电子工业 / 2011-1 / 49.00元

《程序员(2010精华本)》主要内容:《程序员》创刊10年来,每年末编辑部精心打造的“合订本”已经形成一个品牌,得到广大读者的认可和喜爱。今年,《程序员》杂志内容再次进行了优化整合,除了每期推出的一个大型专题策划,各版块也纷纷以专题、策划的形式,将每月的重点进行了整合,让内容非常具有凝聚力,如专题篇、人物篇、实践篇等。另外杂志的版式、色彩方面也有了很大的飞跃,给读者带来耳目一新的阅读体验。一起来看看 《程序员2010精华本》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

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

html转js在线工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试