国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

基于 Webpack 4 多入口生成模板用于服務端渲染的方案及實戰

Lavender / 481人閱讀

摘要:原作者原博文地址基于多入口生成模板用于服務端渲染的方案及實戰法律聲明警告本作品遵循署名非商業性使用禁止演繹未本地化版本協議發布。這是什么背景現代化的前端項目中很多都使用了客戶端渲染的單頁面應用。

原作者:@LinuxerPHL
原博文地址: 基于 Webpack 4 多入口生成模板用于服務端渲染的方案及實戰
法律聲明

警告:本作品遵循 署名-非商業性使用-禁止演繹3.0 未本地化版本(CC BY-NC-ND 3.0) 協議發布。你應該明白與本文有關的一切行為都應該遵循此協議。

這是什么?
背景

現代化的前端項目中很多都使用了客戶端渲染(Client-side Rendering, CSR)的單頁面應用(Single Page Application, SPA)。在大多數情況下,它們都應該通過加載 JavaScript 腳本在瀏覽器中執行以將頁面的大部分視圖渲染出來,以及獲取頁面所需要的數據。單頁面應用有著許多非常顯著的優勢,如它們(單頁面應用)依賴的公共資源通常僅需加載一次。數據都是通過異步的 JavaScript 與 XML 技術(Asynchoronous JavaScript and XML, Ajax))加載的,異步性能往往非常高。在路由切換時,僅需刷新和(或)更改頁面的一部分,而不需要重新加載整個頁面以達到切換路由的目的,因此路由的切換在單頁面應用中顯得比較流暢自然。然而,單頁面應用也存在著很多缺陷,它們包括但不限于:

搜索引擎無法收錄我們的網頁,因為絕大部分的視圖和數據都是通過 JavaScript 在瀏覽器中異步渲染或加載出來的。即使現在有一些搜索引擎爬蟲(如 Google)已經具備了爬取單頁面應用的能力(即爬蟲具備了解析這些 JavaScript 代碼的能力),但等到所有搜索引擎爬蟲都支持爬取單頁面應用顯然不是一個好想法

對于業務邏輯復雜一些的單頁面應用,它們用于渲染頁面的 JavaScript 腳本(可以稱為 Bundle)通常體積巨大。試想加載一個上百 KB,甚至幾 MB 的 JavaScript 腳本,特別是在沒有內容分發網絡(Content Delivery Network, CDN)的情況下,首頁渲染的時延是非常大的

同構與服務端渲染

對于單頁面應用上述的缺點,我們可以考慮利用 Webpack 的多入口配置,將原有的單頁面應用同構成與原先的前端路由相似甚至相同的目錄結構,指定打包后輸出的 HTML 模板。在 Webpack 對整個應用打包之后,將根據入口配置從指定的 HTML 模板生成對應的 HTML 文件,交給位于前端頁面與后端之間的中間層(通常使用 Node.js 編寫,作為服務端渲染(Server-side Rendering, SSR)的服務器)。注意,此時 Webpack 生成的這些 HTML 文件并不能完全被瀏覽器解析,因為這些文件里還有提供給中間層渲染使用的一些插值,在用戶訪問中間層路由時,這些 HTML 文件將被用作服務端渲染的模板,將中間層從后端 API 獲取的數據按照插值的格式填充(也可以稱為“渲染”),最后發送給用戶。

進一步理解

到目前為止,這個描述還是十分令人困惑。不過我們沒有必要一直糾結這些問題,因為下面的圖片也許可以幫助我們進一步了解整個流程:

有了上圖的幫助,我們可以將整個流程歸納為:首先,和單頁面應用一樣,前端代碼先要經過 Webpack 的打包編譯,產物也和單頁面應用一樣,也是 HTML、JavaScript、CSS 文件。唯獨與單頁面應用不同的是,此時的 HTML 文件還存在著插值,這些是要給中間層插入數據最后渲染給用戶的;其次,用戶開始請求一個路由(圖中 1.1),他們現在請求的路由并非前端的路由而是中間件路由(所謂的“中間層”其實可以理解為一個 Node.js 服務器),如果請求的路由與中間件中定義的路由相匹配,中間件就會根據路邏輯向后端服務器獲取指定的數據(圖中 1.2),后端返回相應的數據(圖中 1.3),經過一定的處理后將數據插入到指定的 HTML 文件中(這些 HTML 文件是從 Webpack 打包生成的);然后中間層將渲染出的最終 HTML 發送給客戶端(圖中 1.4)。客戶端收到響應后,接下來的流程就和單頁面應用一樣了:瀏覽器解析中間層發來的 HTML 文件,執行里面的 Bundle。也許 Bundle 中包含了 Ajax 請求,因此瀏覽器向中間件發送了 Ajax 請求(圖中 2.1)。但是中間件并不直接提供后端 API 服務,因此,它必須提供一個可以將請求轉發至后端 API 的代理(圖中 2.2)。后端將數據返回給中間層(圖中 2.3),中間層的代理又將數據發送給客戶端。因此,這種情況下的 Ajax 請求,不論是對于客戶端還是后端,都是透明的,即它們在使用 Ajax 進行數據傳送時,對中間層的存在都毫不知情

將整個流程用一句話描述,即:客戶端訪問了中間層路由,中間層返回渲染好一部分數據的帶 Bundle 的 HTML 文件,再由瀏覽器執行 Bundle 以加載完剩下的數據

基于上面的所有描述,我們大致清楚了中間層應該至少扮演兩種角色

根據客戶端請求獲取相應的數據并響應渲染好的 HTML 文件

代理客戶端和后端之間的 Ajax 請求

充當靜態文件服務器(這并不是必須的,因為可以將靜態的圖片、JavaScript 代碼、CSS 文件等上傳至 CDN)

最佳實踐

至此,我們已經對整個過程有了充分的認識。接下來,我們可以討論一些實際性的配置,例如:我們應該如何對 Webpack 進行配置,如何編寫一個中間層等等。

為了更方便地了解自己的水平是否適合繼續了解和掌握下面的內容,以下列出了下文中使用到的技術:

Node.js

TypeScript

React.js

Webpack

Koa.js

關于我們為何使用 TypeScript 構建我們的實踐項目,請參閱 5 Key Benefits of Angular and TypeScript
如何實踐 需求分析

我們可以從 GitHub API 中獲取 GitHub 上指定用戶的 Gists,根據返回的數據進行渲染。如果時間允許的話,我們也許還能將所有的 Gist 進行分頁渲染。

在這之前……

筆者在實際項目中使用這個方案開發時,遇到了諸多問題。幸運的是,筆者已經基本排除了絕大部分可控的錯誤并且提供了一份最基礎的模板。在接下來的探索中,我們將使用這套模板編寫一個小 Demo:從 GitHub Public API 中獲取一些數據,并按照一定的邏輯進行訪問的渲染。不過,筆者仍希望介紹一下這套模板中的配置,以及為什么需要這樣進行配置。因為筆者堅信:授人以魚,不如授人以漁。

查看這套模板的 GitHub 倉庫

請注意,筆者并不打算直接將這套模板作為實例繼續開發,因此,一個新的倉庫是十分必要的。
筆者將會在 lenconda/webpack-ssr-practice 中同步這篇文章的所有更改。在每一段結束后,如果有必要的話,筆者也會提供對應時間點的 Commit ID,以便于我們對整個過程有更深刻的印象。

Webpack 配置

通常,各種前端框架的腳手架都有自己的 Webpack 配置,以便于開發者快速進入開發狀態。然而,在本文中,我們可能無法找到滿足我們需求的配置方案。因此,為了達到我們預期的結果,我們應該從零開始配置 Webpack。

若你希望在這之前對 Webpack 有更深入的了解,請移步 Webpack 的官方文檔。
前置知識

在 Webpack 中,指定入口文件應該在 entry 字段中以鍵值對的形式聲明。其中,鍵為入口的名稱,值為該入口所在的文件的路徑,路徑可以是相對路徑,也可以是絕對路徑。在本文中,如果沒有特殊聲明,路徑一律采用絕對路徑的形式

在打包時,Webpack 會讀取每個 entry,經過相對應的 loaders,根據 output 字段的配置生成輸出文件(有時也稱為“出口文件”)。例如,有如下一段配置:

module.exports = {
    entry: {
        "root": "/path/to/root.js"
    },
    output: {
        path: "../dist",
        filename: "static/js/[name].[hash:8].js",
        publicPath: "/"
    }
}

Webpack 將會輸出一個類似于 ../dist/static/js/root.ae5fb09e.js 的出口文件。其中,[name]entry 字段中每個鍵的占位符,[hash] 是文件哈希值的占位符,[hash:8] 指的是取文件哈希值的前 8 位。

對 Webpack 生成的文件哈希值感興趣,或者想進一步了解為什么打包出的文件需要將哈希值插入文件名中,請移步 Webpack 的官方文檔
對特定的目錄掃描生成多入口配置

由于我們采用了多入口方案,并且將每個頁面作為一個入口,因此我們無法估量一個項目究竟有多少個頁面(即我們無法估量一個項目有多少個入口)。因此,我們應該編寫一個方法,按照一個特定的模式匹配指定路徑下的入口文件,遞歸生成一個入口列表。我們可以編寫如下的方法:

function getEntries(searchPath, root) {
  const files = glob.sync(searchPath);
  const entries = files.map((value, index) => {
    const relativePath = path.relative(root, value);
    return {
      name: value.split("/")[value.split("/").length - 2],
      path: path.resolve("./", value),
      route: relativePath.split("/").filter((value, index) => value !== "index.tsx").join("/")
    };
  });
  return entries;
}

接下來,我們編寫以下代碼:

test.js

console.log(getEntries(
  path.join(__dirname, "../pages/**/index.tsx"),
  path.join(__dirname, "../pages")
));

這段代碼被期望可以從當前路徑父級目錄的 pages 目錄下找到所有包含 index.tsx 文件的目錄。我們在項目根目錄中運行這段代碼,可以得到如下的結果:

其中,name 指定了入口的名稱,path 指定了入口文件的路徑,route 指定了入口文件的路由名稱(這個字段將在生成出口文件名以及生成 HTML 模板中發揮作用)。

生成對應的服務端渲染模板(Server Bundle) 入口和出口

現在,我們可以得到入口文件列表了:

const entries = getEntries(
  path.join(__dirname, "../pages/**/index.tsx"),
  path.join(__dirname, "../pages")
);

因此,entryoutput 可以是這樣的:

entry: {
    ...Object.assign(...entries.map((value, index) => {
        const entryObject = {};
        entryObject[value.name === "pages" ? "app_root" : value.route.split("/").join("_")] = value.path;
        return entryObject;
    }))
},
output: {
    path: path.join(
        __dirname,
        (config.isDev ? "../../" : "../../dist/") + "server-bundle"
    ),
    filename: "static/js/[name]-route.[hash:8].js",
    chunkFilename: "static/js/[name].[hash:8].chunk.js",
    publicPath: "/"
}

以上這段代碼中,我們也許可以發現很多難以理解的配置項。不過我們不必擔心它們,只需理解我們是如何用 getEntries() 的輸出來配置入口和出口的。其中的一些技術細節(如 ...Object.assign() 等),因篇幅所限,在這里不做詳細闡述。

若你希望繼續深入理解這些操作符或方法,請移步:
Object.assign() - MDN - Mozilla
展開語法- JavaScript | MDN
輸出 HTML 模板
HtmlWebpackPlugin

我們使用 HtmlWebpackPlugin 的 Webpack 插件將多入口輸出至對應的 HTML 中。值得注意的是,我們所需要的 HTML 模板可能不止一個,因為不同的頁面可能有不同的搜索引擎優化(Search Engine Optimization, SEO)配置。因此,我們需要將公共部分(如 footer、共用的head等)提取到一個獨立的 HTML 文件中,再在每個 HTML 模板中將它們引入。這種代碼通常像這樣:

<%= require("html-loader!./parts/footer.html") %>
請注意,這里使用了相對路徑寫法。

正如你所看見的,這種操作需要 html-loader 的支持,如果現在項目中沒有安裝這個依賴,可以這樣安裝:

npm i html-loader -D

也許你很好奇,上面這種寫法似乎并不像 HTML 的寫法。的確,這其實是 EJS 的語法,從每一個 <%= 開頭到 %> 結尾中間的內容,是可以被改變的,我們可以將它們理解為“變量”。那么,是誰會將值插入這些變量呢?其實是 HtmlWebpackPlugin 中的 templateParameter 字段。我們可以在這個字段中向 HTML 模板中注入我們希望的值,例如:

/path/to/test.template.html

<%= title %>
const HtmlWepackPlugin = require("html-webpack-plugin");
new HtmlWebpackPlugin({
    filename: "test.html",
    template: "/path/to/test.template.html",
    templateParameter: {
        title: "Hello, world!"
    }
});

如果不出意外,我們也許可以看到下面的輸出結果:

test.html

Hello, world!
選擇服務端渲染模板引擎

在服務端渲染時,我們可能也需要類似于以上的配置。不幸的是,Webpack 的某些插件已經使用了 EJS 的語法以傳遞數據。因此,我們已經無法使用 EJS 作為服務端渲染時的模板引擎了。不過,目前還有許多結構和 HTML 基本一致的模板渲染引擎,我們選擇的是 Handlebars,因為這是結構最接近 HTML 的語法。我們可以寫出下面的代碼:

<%= title %>

Hello, {{name}}

Webpack 配置仍然沿用上一個例子。

如果不出意外,我們可能可以看到下面的輸出結果:

Hello, world!

Hello, {{name}}

看到這樣的結果,說明用于服務端渲染的插值依然還在,也就表明這種語法對于 Webpack 來說是“安全的”。

配置 HtmlWebpackPlugin

基于上文的探討,我們大致可以得出一份可行的 HtmlWebpackPlugin 配置:

...entries.map((value, index) => {
      return new HtmlWebpackPlugin({
          filename: path.join(
              __dirname,
              (config.isDev ? "../../" : "../../dist/") + "server-templates/",
              value.route === "" ? "index.html" : value.route + "/index.html"
          ),
          template:
              path.resolve(
                  __dirname,
                  "../templates/" + (pages[value.route] && (pages[value.route].template || "index.html") || "index.html")
                  ),
          templateParameters: {
              title: pages[value.route] && (pages[value.route].title || config.name) || config.name
          },
          inject: true,
          chunks: [(value.name === "pages" ? "app_root" : value.route.split("/").join("_")), "common"]
    });
})

你也許會發現這段代碼缺少一些上下文變量,如 configpages 等。因為這段代碼是直接從一個上線項目中拷貝來的。不過這并不傷大雅,而且之后的案例中還會使用這個案例的全部配置。因此,我們仍然不用過度關心這段代碼的上下文,僅需理解每個配置項分別意味著什么

進一步優化

我們已經將 Webpack 核心的配置都梳理出來了。現在,我們還需要對這份配置進行一些優化。優化的方法可以是下面提及的:

提取公共依賴

將 CSS 多帶帶提取

將打包的 Bundle 上傳至 CDN

React Router 配合 React.lazy() 和 Suspense

中的一種或多種。但是具體的優化步驟由于篇幅所限,不做詳細闡述。

中間層配置

在選擇中間層用何種語言(或技術)時,筆者選擇了 Node.js (平臺)和 Koa.js (框架)進行中間層開發。原則上,中間層的選擇搭配可以是隨意的,例如 Java、Python + Flask 等。但是筆者推薦的還是基于 Node.js 平臺的框架,因為 Node.js 使用的語言仍然是 JavaScript,因此,我們編寫中間層的學習成本和重構成本是極低的。

koa-views

不同于 Express.js,Koa.js 并不原生提供渲染引擎。因此,我們需要安裝 koa-views 賦予 Koa.js 渲染 HTML 模板的能力。

npm i koa-views @types/koa-views -S

我們可以在服務端代碼中編寫如下的代碼:

/server/index.ts

import views from "koa-views";
...
app.use(views(path.join(__dirname, "../server-templates"), {
    map: {
        html: "handlebars"
    }
}));

這段代碼指定了要在當前目錄父級目錄下的 server-templates 中指定的模板。如果我們的項目中存在 /path/to/project/server-dist/index.html,則可以通過下面的代碼找到它,并將它渲染出來:

import Router from "koa-router";

const indexRouter = new Router();

indexRouter.get("/", async (ctx, next) => {
    await ctx.render("index.html");
});
這并非 Koa.js 原生的語法,而是 koa-router 路由匹配的語法。若你希望對它有進一步的了解,請移步 koa-router - npm。
代理轉發 Ajax 請求

在 Node.js 服務端程序中,代理某些請求通常可以使用 http-proxy-middleware 的中間件(請注意,這里的“中間件”并不是上文提及的“中間層”)。但在 Koa.js 中,我們并不能直接使用它作為代理轉發請求。我們還需要將它包裝進 koa2-connect 中。我們的代碼應該像這樣:

app.use(async (ctx, next) => {
    if (ctx.url.startsWith("/api")) {
        ctx.respond = false;
        await connect(proxy({
            target: "SOME_API_URL",
            changeOrigin: true,
            // pathRewrite: {
            //   "^/api": ""
            // },
            secure: config.isDev ? false : true,
        }))(ctx, next);
    }
    await next();
});
前端代碼目錄結構

也許你已經注意到,Webpack 中對路徑掃描生成入口列表的方式已經決定了我們的前端目錄結構應該要遵守某種約定。在這個實例中,我們通過閱讀 Webpack 配置可以了解到:Webpack 將會掃描 /src/page 目錄下所有包含 index.tsx 文件的目錄,根據指定的相對路徑根目錄(即 getEntries() 的第二個參數,我們使用了 /src/pages)計算出對應的路由(例如:假設存在 /src/pages/test/hello/index.tsx,那么從它計算出的路由是 test/hello)。

每個包含 index.tsx 的目錄中,index.tsx 應該像這樣:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(, document.getElementById("root"));

因此,我們也許可以得出這樣一個結論:/src/pages 目錄下的每一個子目錄,包括它本身,都是一個獨立的入口,而每個入口也可以是一個獨立的 React App。

編寫代碼 首頁 /src/pages/App.tsx

這個頁面將會向中間層的 /api/users 發送一個 Ajax 請求。顯然,此時的 Ajax 請求是經過中間層代理轉發的。同時,我們也許可以發現,這個頁面并沒有使用服務端渲染,而是通過中間層直接渲染出來的。因為這個頁面沒有必要做服務端渲染,也無法使用服務端渲染。

import React, { useState } from "react";
import "./App.scss";
import http from "../utils/http";

const App = (): JSX.Element => {
    const [inputValue, setInputValue] = useState("");
  
    // 執行搜索用戶的方法,該方法會被 button 調用
    const searchUser = () => {
        // 通過 Ajax 獲取指定用戶的信息,如果存在,就跳轉至相應的頁面
        http
            .get(`/api/users/${inputValue}`)
            .then(res => {
                if (res.data) {
                    window.location.href = `/user/${inputValue}`;
                }
            });
    };

    return (
        
{* 在輸入時,將輸入的內容用 Hooks 傳入 inputValue *} setInputValue(event.target.value)} />
); }; export default App;

目前的代碼

用戶 Gist 列表頁面 /src/pages/user/App.tsx
import React from "react";
import "./App.scss";

const App = (): JSX.Element => {
    return (
        
Gists
); }; export default App;
/src/templates/user.html




  
  <%= title %>
  <%= require("html-loader!./parts/head.html") %>



  
{{#data}}
{{description}}
{{id}}

Detail →
{{/data}}
<%= require("html-loader!./parts/footer.html") %>
/server/routers/index.ts
import Router from "koa-router";
import http from "../utils/http";

const indexRouter = new Router();

indexRouter.get("/", async (ctx, next) => {
    await ctx.render("index.html");
});

// 匹配 /user 路徑,其 ID 可以通過 ctx.params.id 獲得
indexRouter.get("/user/:id", async (ctx, next) => {
    // 中間件發送 HTTP 請求給后端,從后端獲取數據
    const res = await http.get(`/users/${ctx.params.id}/gists`);
    // 根據后端返回的數據進行處理,使它與模板中的插值一致
    const result = res.data.map((value, index) => {
        return {
            url: value.url,
            id: value.id,
            description: value.description,
            files: Object.keys(value.files).map((key, index) => {
                return {
                    name: key,
                    raw_url: value.files[key].raw_url
                };
            })
        };
    });
    // 渲染相應的頁面,將插值傳遞到第二個變量中
    await ctx.render("user/index.html", { data: result });
});

export default indexRouter;

這個頁面稍微有點復雜,無論是中間層的路由邏輯還是 HTML 模板。但是你可能已經發現,和首頁不同的是,/src/pages/user/App.tsx 中的代碼其實非常少,這是因為這個頁面完全使用了服務端渲染,因此它的 React.js 邏輯(也就是前端邏輯)非常簡單,而 HTML 模板和路由邏輯稍微復雜一些。

目前的代碼

構建

筆者在模板中預置了我們可能需要的 npm 腳本。

現在,我們需要使用構建命令將前端代碼通過 Webpack 打包編譯,以及將使用 TypeScript 編寫的中間層代碼編譯為 JavaScript 代碼(這并不是必須的,因為幾乎所有持久化產品(如 nodemon、pm2、forever 等)都支持直接運行 TypeScript 代碼)。

npm run build

打包編譯僅需數十秒的時間。完成之后,我們可以在項目根目錄下看見一個名為 dist 的目錄,里面的內容可能像這樣:

.
├── server
│   ├── config.js
│   ├── config.js.map
│   ├── index.js
│   ├── index.js.map
│   ├── routers
│   │   ├── index.js
│   │   └── index.js.map
│   └── utils
│       ├── http.js
│       └── http.js.map
├── server-bundle
│   ├── app_root.33a07d26.css
│   ├── assets
│   │   └── css
│   │       └── index.css
│   ├── static
│   │   └── js
│   │       ├── app_root-route.33a07d26.js
│   │       ├── common.33a07d26.chunk.js
│   │       └── user-route.33a07d26.js
│   └── user.33a07d26.css
└── server-templates
    ├── index.html
    └── user
        └── index.html
展示

現在,我們已經有了打包編譯后的所有文件。為了啟動測試用的服務器,我們應該執行

node dist/server/index.js

如果沒有在系統環境變量中指定 PORT 的值,它將會在 127.0.0.1:5000 啟動一個 Node.js 服務器,也就是所謂的中間層服務器。

我們直接訪問 http://localhost:5000,頁面也許像這樣:

我們也可以看一看首頁路由究竟返回了什么:

我們并不能直接在返回的 HTML 中找到首頁上的視圖。因為視圖都是通過 HTML 末尾處引入的 Bundle 渲染的。前文中,我們已經知道,這是因為這個頁面并沒有使用服務端渲染。

我們再來看一看用戶 Gist 頁面。我們在首頁輸入 “octocat”,點擊 “Search”,稍等一會,頁面就會跳轉到 /user/octocat

此時,中間層的 Ajax 代理捕獲了一個由客戶端發送給后端的 Ajax 請求:

那么現在,我們看一看這個路由返回了什么:

我們發現,用紅色墨跡圈出的部分是已經在服務端渲染出來的。

更完整的實例

筆者使用本文討論的方案構建了一個簡單的 Web 應用,目前已經在線上運行了。這個應用的頁面很少,其中使用了服務端渲染的頁面也非常少(因為大部分頁面實在沒有使用服務端渲染的必要,也不希望被搜索引擎爬蟲抓取),但是筆者認為它完全可以體現這篇文章的核心思想。

請移步 lenconda/tracelink_web
線上地址:https://tracel.ink
總結

到此為止,我們已經達到了我們所要討論的預期目的:使用 Webpack 的多入口特性,生成可以用于服務端渲染的模板,進行服務端渲染。和大部分現有的方案不同的是,我們使用了更“另類”的方式:盡最大的努力是中間層以原生(在本文中指的是 Koa.js 的模板渲染引擎),而無需考慮如何針對特定的前端框架同構(如 Next.js 之于 React.js、Nuxt.js 之于 Vue.js 等)

仍然存在的疑惑

即使我們“大費周章”地將這套方案詳細地討論,疑惑仍然還是存在的。筆者將這套方案分享給身邊的朋友時,他們幾乎都沒有馬上理解和完全接受。不過,他們提出的問題也許非常有價值。我們不妨來幫忙解答其中的一些問題:

為什么要使用 React.js 作為前端框架?我可不可以用 Vue.js / Angular / 原生 JavaScript 編寫前端?

筆者選擇 React.js 作為前端框架是因為個人喜好。當然,完全可以其他任何框架甚至原生 JavaScript。因為中間層只需要 HTML 模板進行渲染,而我們則是通過 Webpack 打包編譯的。Webpack 多入口和任何框架都沒有關系,不論是用 React.js、Vue.js 還是 Angular,只要前端邏輯一樣,它們打包編譯出來的代碼也幾乎完全一樣。因此,我們可以隨心所欲選擇自己喜愛的技術編寫前端代碼。

既然都已經有中間層渲染頁面了,為什么還要多此一舉再去用 Webpack 打包出的 Bundle 加載剩下的數據?

因為在真實的項目中,我們不可能完全依賴于服務端渲染,也不可能完全靠客戶端渲染。我們必須明白一個最核心的原則:對于我們希望搜索引擎爬蟲爬取的內容,我們應該盡可能地使用服務端渲染;對于我們不希望,或者沒必要被搜索引擎爬蟲爬取的內容,我們應該盡可能地使用客戶端渲染(即 Ajax 方式)。因此,我們需要服務端渲染我們想要渲染的數據,再將帶著 Bundle 的渲染完畢的 HTML 發送給瀏覽器,由瀏覽器繼續執行 Bundle 加載剩下的數據和視圖

服務端渲染難道不是指后端從數據庫取出數據再渲染嗎?為什么還有個中間層?它屬于前端還是后端?

服務端渲染中的“服務端”并不是真正的后端,它是無法接觸到數據庫的。它充當著客戶端與后端的“聯絡員”。在同構時,后端不需要經過任何更改。中間層屬于前端。

參考資料

《深入淺出 Webpack》

webpack多入口文件頁面打包配置- 掘金

webpack4.x實現Js和Html多入口、多出口以及html-webpack-plugin插件實現html各自引入各自的js文件 第三節

webpack多入口文件頁面打包配置

Webpack 4 course – part four. Code splitting with SplitChunksPlugin

Webpack4+react多頁面架構- 作品- React 中文

HtmlWebpackPlugin | webpack

HtmlWebpackPlugin用的html的ejs模板文件中如何使用條件判斷

Webpack實戰-構建同構應用

從零開始搭建React同構應用(三):配置SSR

React服務端渲染(項目搭建)

Documentation - Getting Started | Next.js

使用Next.js構建React服務端渲染應用

react + express + webpack 搭建 React SSR(一)

Server Side Rendering - SurviveJS

React 中同構(SSR)原理脈絡梳理- 掘金

React 服務端渲染與同構- 前端- 掘金

React 同構直出優化總結| AlloyTeam

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106498.html

相關文章

  • 基于 Webpack 4 入口生成模板用于服務渲染方案實戰

    摘要:原作者原鏈接基于多入口生成模板用于服務端渲染的方案及實戰法律聲明警告本作品遵循署名非商業性使用禁止演繹未本地化版本協議發布。這是什么背景現代化的前端項目中很多都使用了客戶端渲染的單頁面應用。 原作者:@LinuxerPHL原鏈接:基于 Webpack 4 多入口生成模板用于服務端渲染的方案及實戰 法律聲明 警告:本作品遵循 署名-非商業性使用-禁止演繹3.0 未本地化版本(CC BY-...

    big_cat 評論0 收藏0
  • Vue.js 服務渲染業務入門實踐

    摘要:說起,其實早在出現之前,網頁就是在服務端渲染的。沒有涉及流式渲染組件緩存對的服務端渲染有更深一步的認識,實際在生產環境中的應用可能還需要考慮很多因素。選擇的服務端渲染方案,是情理之中的選擇,不是對新技術的盲目追捧,而是一切為了需要。 作者:威威(滬江前端開發工程師)本文原創,轉載請注明作者及出處。 背景 最近, 產品同學一如往常笑嘻嘻的遞來需求文檔, 縱使內心萬般拒絕, 身體倒是很誠實...

    miya 評論0 收藏0
  • Vue 服務渲染實踐 ——Web應用首屏耗時最優化方案

    摘要:好在后是支持服務端渲染的,零零散散花費了兩三周事件,通過改造現有項目,基本完成了在現有項目中實踐了服務端渲染。在服務端生成對應的字符串,客戶端接收到對應的字符串,能立即渲染,最高效的首屏耗時。服務端渲染的原理是虛擬。實現前后端同構應用。 隨著各大前端框架的誕生和演變,SPA開始流行,單頁面應用的優勢在于可以不重新加載整個頁面的情況下,通過ajax和服務器通信,實現整個Web應用拒不更新...

    terasum 評論0 收藏0
  • 關于Vue2一些值得推薦文章 -- 五、六月份

    摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...

    sutaking 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<