日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

网易智慧企业Node.js实践(1) | Node应用架构设计和React同构

發(fā)布時(shí)間:2025/3/8 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网易智慧企业Node.js实践(1) | Node应用架构设计和React同构 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

導(dǎo)讀:

近期網(wǎng)易智慧企業(yè)在 Node.js(以下簡稱 Node) 的接入上已輸出階段性成果,特推出此系列文章,希望能與大家分享部分接入過程的方案,從而提供幫助。系列主要包括以下內(nèi)容。

?

???????? 1. Node 應(yīng)用架構(gòu)設(shè)計(jì)

???????? 2. React 同構(gòu)

???????? 3. 健康檢查和平滑發(fā)布

???????? 4. 前端代碼上CDN、代碼發(fā)現(xiàn)

???????? 5. 應(yīng)用監(jiān)控

???????? 6. 灰度環(huán)境

?

本文作為系列文章的第一篇主要介紹網(wǎng)易智慧企業(yè) Node 從0到1的接入過程,主要涉及 Node 的應(yīng)用架構(gòu)和同構(gòu)渲染,也就是1、2這兩部分。后續(xù)會(huì)分享關(guān)于 Node 工程實(shí)踐相關(guān)內(nèi)容(3、4、5、6)。

?

關(guān)于 Node

Node 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行時(shí)。它誕生于2009年,Node 第一次把JavaScript帶入到后端服務(wù)器開發(fā),另外還可以通過它編寫工具,比如代碼打包工具,但是它誕生的最初目的還是為了實(shí)現(xiàn)高性能 Web 服務(wù)器。它內(nèi)部實(shí)現(xiàn)的異步 IO、事件驅(qū)動(dòng)就是為高性能 Web 服務(wù)而生的。

?

經(jīng)過過去這么多年發(fā)展,Node 已經(jīng)形成了非常成熟的應(yīng)用模式,比如:BFF(Back-end For Front-end)——服務(wù)于前端的后端,把 Node 作為后端的一層,專門為前端提供數(shù)據(jù)裁剪和格式化、聚合編排等功能。另外還有最近非?;馃岬幕?Node 實(shí)現(xiàn)的 Serverless 服務(wù)。那么具體到我們智慧企業(yè)是怎么使用 Node 的呢?那就首先介紹下我們的需求背景。

?

需求背景

?

2019年底網(wǎng)易智慧企業(yè)正在打造一款 SCRM 產(chǎn)品—網(wǎng)易互客(https://huke.163.com),它最初主要有3塊需求:

?

???????? 1、互客平臺(tái)。

???????? 2、互客運(yùn)營系統(tǒng)(內(nèi)部使用)。

???????? 3、互客官網(wǎng)。

?

前兩部分對(duì)交互要求比較高,有一些需求決定技術(shù)上需要優(yōu)先使用單頁應(yīng)用的形式。官網(wǎng)又是對(duì) SEO 有需求的,所以需要有同構(gòu)渲染的能力(前端使用 React 框架);另外鑒于目前的技術(shù)架構(gòu)對(duì)開發(fā)效率的提升已經(jīng)形成瓶頸,所以考慮使用新的技術(shù)方案,來完全解放前后端的生產(chǎn)力,最終考慮使用 Node 來實(shí)現(xiàn)前后端的完全分離,徹底解決之前前端要寫 Java 模版文件和前后端對(duì)頁面數(shù)據(jù)理解不一致尷尬局面。

?

決定使用 Node 后,首先要解決的問題是如何和 Java 端配合,也就是新的前后端分工,鑒于這是我們第一個(gè)對(duì)外服務(wù)的 Node 項(xiàng)目,作為初次的嘗試,我們考慮使用漸進(jìn)式開發(fā)模式,先從接進(jìn)來開始做,所以我們初始給 Node 分配的任務(wù)比較簡單,包括:

?

???????? 1、頁面渲染。

???????? 2、用戶登錄校驗(yàn)。

???????? 3、頁面初始必要數(shù)據(jù)填充。

???????? 4、功能型接口實(shí)現(xiàn)。

?

另外還有一個(gè)目標(biāo)是通過這個(gè)項(xiàng)目,逐步完善智慧企業(yè)的 Node 工程工具體系,最終形成智慧企業(yè)自己的 Node 生態(tài)。

?

設(shè)計(jì)和實(shí)現(xiàn)

?

確定了如何和 Java 端的配合后,另一個(gè)問題是選擇 Node 框架,經(jīng)過調(diào)研,我們選擇了 Egg.js 作為 Node 框架方案,選它的原因是因?yàn)樗鼞?yīng)該是目前國內(nèi)使用最為廣泛,生態(tài)最為完善的 Node 企業(yè)級(jí)框架。任務(wù)分工和框架都定下來之后我們應(yīng)用的整體架構(gòu)也就出來了,如下圖:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?【架構(gòu)圖】

?

簡單介紹下一個(gè)完整的用戶請求的訪問路徑。首先用戶請求到網(wǎng)關(guān),網(wǎng)關(guān)根據(jù) URL 轉(zhuǎn)發(fā)規(guī)則轉(zhuǎn)發(fā)到 Node 或者 Java 應(yīng)用,從而完成一次頁面訪問或接口請求。這里面涉及到路由的設(shè)計(jì),頁面和接口的 URL 要能夠通過 path 區(qū)分。

?

拿我們的客戶列表頁面舉例,客戶列表的 URL 的 path 是 `/admin/customer/all`,我們的規(guī)則是 `/admin*` 對(duì)應(yīng)頁面請求,所以請求會(huì)被網(wǎng)關(guān)轉(zhuǎn)發(fā)到 Node 上,在 Node 中使用 HTTP 請求從 Java 端獲取頁面初始數(shù)據(jù),放入頁面模版,返回給用戶,完成頁面訪問請求。

?

另外一個(gè)比較重要的問題是用戶的登錄信息,我們使用了比較偏傳統(tǒng)的方案,用戶登錄功能在 Java 端實(shí)現(xiàn),當(dāng)用戶訪問頁面時(shí),Node 會(huì)檢查 cookie 里的登錄 token,并進(jìn)行校驗(yàn),如果 token 不存在或不正確,就給用戶 redirect 到登錄頁面,當(dāng)用戶填寫完信息點(diǎn)擊登錄按鈕時(shí),調(diào)用 Java 端的登錄接口進(jìn)行登錄,成功后 Java 端會(huì)給登錄請求的響應(yīng)帶上 cookie ,這樣前端、Node 端、Java 端的登錄信息就能串起來。

?

當(dāng)然這些只是 Node 作為頁面服務(wù)提供的能力,但是我們還需要 React 的同構(gòu)能力。

?

關(guān)于同構(gòu)

一套代碼既可以在服務(wù)端運(yùn)行又可以在客戶端運(yùn)行,在服務(wù)器端執(zhí)行一次,用于實(shí)現(xiàn)服務(wù)器端渲染,在客戶端再執(zhí)行一次,用于接管頁面交互,這就是同構(gòu)應(yīng)用。簡而言之, 就是服務(wù)端直出和客戶端渲染的組合, 能夠充分結(jié)合兩者的優(yōu)勢,并有效避免兩者的不足。

?

同構(gòu)不僅僅能解決前面說的 SEO 問題,它還能有效縮減頁面白屏?xí)r間,因?yàn)樗馨阎暗娜未械?HTTP 請求縮減為一次,而白屏?xí)r間對(duì)用戶的影響也是非常大的。

?

一般前端框架是需要對(duì) DOM 進(jìn)行操作的,在瀏覽器環(huán)境當(dāng)然沒有問題,而在Node 是沒有 DOM 這個(gè)概念的,那 React 是如何實(shí)現(xiàn)在 Node 端進(jìn)渲染的呢?這因?yàn)?React 中引入的虛擬 DOM,虛擬 DOM 是真實(shí) DOM 的一個(gè) JavaScript 對(duì)象映射,React 在做頁面操作時(shí),實(shí)際上不是直接操作 DOM,而是操作虛擬 DOM,也就是操作普通的 JavaScript 對(duì)象,這就使得 SSR 成為了可能。在 Node 端 React 把虛擬 DOM 輸出為字符串,而在瀏覽器端 React 把虛擬 DOM 映射為真實(shí) DOM,完成頁面渲染。

?

那么如何在 Node 端把 React 頁面渲染為字符串呢?React 框架提供了4個(gè)API針對(duì)不同的使用場景,分別是:

?

???????? *? renderToString()

???????? *? renderToStaticMarkup()

???????? *? renderToNodeStream()

???????? *? renderToStaticNodeStream()

?

結(jié)合需求我們選擇 `renderToString` 方法。

?

其實(shí)整個(gè)服務(wù)端渲染的邏輯非常簡單,把初始數(shù)據(jù)傳給 React 組件使用 `renderToString` 進(jìn)行渲染,得到一個(gè)字符串,把字符串放入頁面模版中的 React 掛載節(jié)點(diǎn)內(nèi)就行了。但是要實(shí)現(xiàn)一個(gè)能根據(jù)路由自動(dòng)渲染對(duì)應(yīng)的組件的 Egg.js 插件還是有一點(diǎn)復(fù)雜的,所以我們實(shí)現(xiàn)了 `pp-fishssr` 服務(wù)端渲染插件,以滿足根據(jù)路由渲染對(duì)應(yīng)頁面的需求。

主要介紹下我們的實(shí)現(xiàn)的不一樣的地方,首先是配置方式:

?

```json

fishssr: {

???????? routes: [

???????? {

???????? ? path: ‘/admin/*’,

???????? ? Component: () => (require(‘@/page/admin’).default),

???????? ? controller: ‘page.admin’

???????? },

???????? {

???????? ? path: ‘/user/*’,

???????? ? Component: () => (require(‘@/page/user’).default),

???????? ? controller: ‘user.h5Page’,

???????? },

???????? ],

???????? // 頁面模版文件路徑

???????? template: ‘screen/index.html’,

???????? // 服務(wù)端渲染打包后的js文件

???????? serverJs: resolvePath(‘dist/Page.server.js’),

}

```

?

介紹配置項(xiàng):

?

path:? `/admin/*`、`/user/*` 分別對(duì)應(yīng)了一個(gè)單頁應(yīng)用。

Component: 對(duì)應(yīng)了頁面的 React 組件,內(nèi)部會(huì)處理初始數(shù)據(jù),轉(zhuǎn)化為store 的 preloadedState 或 props,里面使用前端路由。

controller:? 對(duì)應(yīng)的是 Egg.js 中的 controller,用來獲取頁面初始數(shù)據(jù),然后使用`this.ctx.fishssr.renderPage(initData)`實(shí)現(xiàn)頁面渲染。

template: 頁面的模版文件,內(nèi)部 `stream` 就是 Node 渲染 React 頁面組件之后得到的字符串,文件的內(nèi)容大致如下:

?

```html

<!DOCTYPE html>

<html lang=‘zh-CN’>

<head>

? <title>網(wǎng)易互客</title>

? <link rel=‘stylesheet’ href=‘/css/Page.css’ />

</head>

?

<body>

? <div id=‘a(chǎn)pp’>

??? {{stream | safe}}

? </div>

???????? <script>

? window.__INITIAL_DATA__ = {{ initialData | safe}};

? </script>

? <script src=‘/js/runtime~Page.js’></script>

? <script src=‘/js/Page.js’></script>

</body>

</html>

```

?

serverJs :是頁面入口文件對(duì)應(yīng)的 Node 端打包版本,入口文件主要代碼如下:

?

```

const clientRender = async () => {

? ReactDOM.hydrate(

??? <>

????? {

?????? ?Routes.map(route => {

????????? const { path, Component } = route

????????? const isMatch = matchPath(window.location.pathname, route)

????????? if ( !isMatch ) {

??????????? return null

????????? }

????????? const ActiveComponent = Component()

????????? const WrappedComponent = GetInitialProps(ActiveComponent)

????????? return <WrappedComponent key={path} />

??????? })

????? }

??? </>, document.getElementById('app'))

}

?

const serverRender = async (params) => {

? const { initData, path, url } = params

? const ActiveComponent = getComponent(Routes, path)()

? return (

??? <StaticRouter location={url} context={initData}>

????? <ActiveComponent {... initData} />

??? </StaticRouter>

? )

}

?

export default __isBrowser__ ? clientRender() : serverRender

```

?

這段代碼會(huì)根據(jù)路由渲染對(duì)應(yīng)的頁面組件,同時(shí)根據(jù)不同打包環(huán)境輸出對(duì)應(yīng) Node 端和瀏覽器端的渲染代碼。

?

總結(jié)

?

Egg.js 作為一個(gè)完備的企業(yè)級(jí) Node 框架,在接入過程中可以說非常順滑,主要精力放在解決自身業(yè)務(wù)需求和后端配合即可。

?

目前使用這個(gè)方案的產(chǎn)品**網(wǎng)易互客**已經(jīng)上線,這個(gè)方案解決了文章開頭所說技術(shù)和業(yè)務(wù)需求的,同時(shí)它帶來的新的前后端配合模式也極大的提高了不僅僅是前端的開發(fā)效率,對(duì)后端來說也是非常友好的。同時(shí)前端也可拓寬自己邊界,能夠承接更多需求,比如我們運(yùn)營系統(tǒng)、功能性 API,比如微信 JS-SDK 認(rèn)證,之前只能放在后端,現(xiàn)在放在 Node 端,前端開發(fā)起來更加靈活,減少很大的溝通成本。但是目前作為對(duì)外服務(wù) Node 應(yīng)用只有這些還是不夠的,還是需要很多工程工具的支持。

?

后續(xù)我會(huì)介紹我們在 Node 工程上的一些實(shí)踐,讓 Node 應(yīng)用更穩(wěn)定的提供服務(wù)、以及更快更方便的排查問題。

總結(jié)

以上是生活随笔為你收集整理的网易智慧企业Node.js实践(1) | Node应用架构设计和React同构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。