1. 本地Egg項目啓動
- 首先執行
node index.js
或者npm run dev
啓動 Egg應用 - 在 Egg Agent 裏面啓動koa服務, 同時在koa服務裏面啓動Webpack編譯服務
- 掛載Webpack內存文件讀取方法覆蓋本地文件讀取的邏輯
app.react.render = (name, locals, options) => {
const filePath = path.isAbsolute(name) ? name : path.join(app.config.view.root[0], name);
const promise = app.webpack.fileSystem.readWebpackMemoryFile(filePath, name);
return co(function* () {
const code = yield promise;
if (!code) {
throw new Error(`read webpack memory file[${filePath}] content is empty, please check if the file exists`);
}
// dynamic execute javascript
const wrapper = NativeModule.wrap(code);
vm.runInThisContext(wrapper)(exports, require, module, __filename, __dirname);
const reactClass = module.exports;
if (options && options.markup) {
return Promise.resolve(app.react.renderToStaticMarkup(reactClass, locals));
}
return Promise.resolve(app.react.renderToString(reactClass, locals));
});
};
- Worker 監聽Webpack編譯狀態, 檢測Webpack 編譯是否完成, 如果未完成, 顯示Webpack 編譯Loading, 如果編譯完成, 自動打開瀏覽器
- Webpack編譯完成, Agent 發送消息給Worker, Worker檢測到編譯完成, 自動打開瀏覽器, Egg服務正式可用
2. 本地服務端渲染頁面訪問
- npm run dev
- 瀏覽器輸入URL請求地址, 然後 Egg 接收到請求, 然後進入 Controller
- Node層獲取數據後(Node通過http/rpc方式調用Java後端API數據接口), 進入模板render流程
- 進入render流程後, 通過worker進程通過調用
app.messenger.sendToAgent
發送文件名給Agent進程, 同時通過app.messenger.on
啓動監聽監聽agent發送過來的消 - Agent進程獲取到文件名後, 從Webpack編譯內存裏面獲取文件內容, 然後Agent 通過
agent.messenger.sendToApp
把文件內容發送給Worker進程 - Worker進程獲取到內容以後, 進行React編譯HTML, 編譯成HTML後, 進入jss/css資源依賴流程
- 如果啓動代理模式(見easywebpack的setProxy), HTML直接注入相對路徑的JS/CSS, 如下:
頁面可以直接使用 /public/client/js/vendor.js
相對路徑, /public/client/js/vendor.js
由後端框架代理轉發到webpack編譯服務, 然後返回內容給後端框架, 這裏涉及兩個應用通信. 如下:
<link rel="stylesheet" href="/public/client/css/home/android/home.css">
<script type="text/javascript" src="/public/client/js/vendor.js"></script>
<script type="text/javascript" src="/public/client/js/home.js"></script>
- 如果非代理模式(見easywebpack的setProxy), HTML直接注入必須是絕對路徑的JS/CSS, 如下:
頁面必須使用 http://127.0.0.1:9000/public/client/js/vendor.js
絕對路徑
<link rel="stylesheet" href="http://127.0.0.1:9000/public/client/css/home/android/home.css">
<script type="text/javascript" src="http://127.0.0.1:9000/public/client/js/vendor.js"></script>
<script type="text/javascript" src="http://127.0.0.1:9000/public/client/js/home.js"></script>
其中 http://127.0.0.1:9000 是 Agent裏面啓動的Webpack編譯服務地址, 與Egg應用地址是兩回事
- 最後, 模板渲染完成, 服務器輸出HTML內容給瀏覽器.
3. 正式環境發佈模式構建流程和運行模式
- Webpack通過本地構建或者ci直接構建好服務端文件和客戶端資源文件到磁盤
- Egg render直接讀取本地文件, 然後渲染成 HTML
- 根據
manfifest.json
文件注入 jss/css資源依賴注入 - 模板渲染完成, 服務器輸出HTML內容給瀏覽器.
4. 關於 Egg 框架中的 Agent 和 Worker
- 我們利用本地開發修改Node層代碼修復重啓時, 只會重啓Worker進程, 不會重啓Agent進程, 我們可以在Agent裏面啓動Webpack編譯服務解決Webpack compiler實例問題.
- 因爲Egg App進程 和 Agent進程是兩個進程, 當url訪問時, 我們通過worker發送消息給agent進程, 獲取服務端渲染的文件內容, 然後Agent再發送消息給Worker解決文件讀取問題.
- 本地開發webpack熱更新內存存儲讀取和線上應用本機文件讀取邏輯分離功能, 我們通過本地開發模式時, 通過讀取Webpack內存內容覆蓋本地文件讀取的邏輯, 這樣在開發模式和發佈模式可以無縫對接.
- worker和agent通信機制: https://eggjs.org/zh-cn/core/cluster-and-ipc.html
- 實現egg項目服務器代碼修改項目自動重啓的功能可以使用egg-development插件.
5.項目和插件
- egg-react-webpack-boilerplate基於easywebpack-react和egg-view-react(ssr)插件的工程骨架項目
- easywebpack-react Webpack React 構建工程化方案.
- easywebpack-cli Webpack 構建工程化腳手架.
- egg-view-react-ssr react ssr 解決方案.
- egg-webpack 本地開發熱更新使用.
- egg-webpack-react 本地開發渲染內存讀取輔助插件