天天看點

webpack進階篇(二十七):webpack實作SSR打包(下)

說明

玩轉webpack學習筆記

webpack ssr 打包存在的問題

1、浏覽器的全局變量 (Node.js 中沒有 document, window)

  • 元件适配:将不相容的元件根據打包環境進⾏适配
  • 請求适配:将 fetch 或者 ajax 發送請求的寫法改成​

    ​isomorphic-fetch​

    ​ 或者 axios

2、樣式問題 (Node.js ⽆法解析 css)

  • ⽅案⼀:服務端打包通過​

    ​ignore-loader​

    ​ 忽略掉 CSS 的解析
  • ⽅案⼆:将​

    ​style-loader​

    ​​ 替換成​

    ​isomorphic-style-loader​

如何解決樣式不顯示的問題?

使⽤打包出來的浏覽器端 html 為模闆

設定占位符,動态插⼊元件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>
</body>
</html>      

首屏資料如何處理?

服務端擷取資料

替換占位符

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>

    <!--INITIAL_DATA_PLACEHOLDER-->
</body>
</html>      

實作

由于上一節我已經将 ​

​如何解決樣式不顯示的問題?​

​​ 這個問題已經處理,這一節就簡單實作 ​

​首屏資料如何處理?​

1、在上一節的基礎上添加​

​<!--INITIAL_DATA_PLACEHOLDER-->​

​占位符

<!DOCTYPE html>
<html lang="en">
<head>
    <%= require('raw-loader!./meta.html') %>
    <title>Document</title>
    <script><%= require('raw-loader!babel-loader!../../node_modules/lib-flexible/flexible.js') %></script>
</head>
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react.min.js"></script>
    <script type="text/javascript" src="https://11.url.cn/now/lib/16.2.0/react-dom.min.js"></script>
    <!--INITIAL_DATA_PLACEHOLDER-->
</body>
</html>      

2、在server檔案夾裡添加 data.json 檔案,然後在server檔案夾的 index.js 裡引入

data.json

{
  "error": [],
  "extra": [],
  "data": {
    "list": [
      [{
        "sub_count": 5556,
        "column_type": 1,
        "id": 192,
        "column_price_market": 9900,
        "column_bgcolor": "#F6F7FB",
        "column_title": "SQL必知必會",
        "column_cover_small": "https:\/\/static001.geekbang.org\/resource\/image\/1c\/38\/1c5a5b154b543af952312eef33217438.jpg",
        "column_cover": "https:\/\/static001.geekbang.org\/resource\/image\/c7\/0d\/c7ee0aabbcb6d2da09a1b4a56c1a730d.jpg",
        "had_sub": false,
        "price_type": 2,
        "column_unit": "45講",
        "is_experience": false,
        "column_ctime": 1559640855,
        "update_frequency": "每周一 \/ 三 \/ 五更新",
        "is_onboard": true,
        "author_intro": "清華大學計算機博士",
        "column_sku": 100029501,
        "column_cover_wxlite": "https:\/\/static001.geekbang.org\/resource\/image\/cd\/f0\/cd26b744d388dbd4387dcfaa66dd8bf0.jpg",
        "column_price": 6800,
        "column_price_sale": 6800,
        "author_name": "陳旸",
        "column_subtitle": "從入門到資料實戰"
      }]
    ],
    "nav": [{
      "id": 1,
      "name": "專欄",
      "color": "#5ba6ff",
      "icon": "https:\/\/static001.geekbang.org\/resource\/image\/dd\/9e\/dd8cbc79f017d1b01f643c7ea929d79e.png"
    }, {
      "id": 3,
      "name": "視訊課程",
      "color": "#79c109",
      "icon": "https:\/\/static001.geekbang.org\/resource\/image\/4a\/c3\/4aebe8fb752fa21a0fd989a45d9847c3.png"
    }, {
      "id": 2,
      "name": "微課",
      "color": "#5ba6ff",
      "icon": "https:\/\/static001.geekbang.org\/resource\/image\/9c\/f1\/9c223ccae33c5245a3009857582f1df1.png"
    }]
  },
  "code": 0
}      

index.js

if (typeof window === 'undefined') {
  global.window = {};
}

const fs = require('fs');
const path = require('path');
const express = require('express');
const { renderToString } = require('react-dom/server');

const SSR = require('../dist/search-server');
const template = fs.readFileSync(path.join(__dirname, '../dist/search.html'), 'utf-8');
const data = require('./data.json'); // 引入資料

const renderMarkup = (str) => {
  const dataStr = JSON.stringify(data);
  return template.replace('<!--HTML_PLACEHOLDER-->', str)
    .replace('<!--INITIAL_DATA_PLACEHOLDER-->', `<script>window.__initial_data=${dataStr}</script>`);
};

const server = (port) => {
  const app = express();

  app.use(express.static('dist'));

  app.get('/search', (req, res) => {
    console.log('SSR-----------》', SSR);
    console.log('renderToString(SSR)------>', renderToString(SSR));
    const html = renderMarkup(renderToString(SSR));
    res.status(200).send(html);
  });

  app.listen(port, () => {
    console.log(`Server is running on port:${port}`);
  });
};

server(process.env.PORT || 3000);