架構(gòu)設(shè)計
- 腳手架命令行工具: ssr
提供了常用的命令:start build deploy
本地開發(fā)環(huán)境
- 執(zhí)行
ssr start
// 核心執(zhí)行代碼如下
// 插件通過項目中的plugin.js文件配置
const plugin = loadPlugin()
// start client webpack dev server
await plugin.clientPlugin?.start?.(argv)
// 啟動midway
await plugin.serverPlugin?.start?.(argv)
- plugin.js 中聲明
serverPlugin clientPlugin
插件
const { midwayPlugin } = require('ssr-plugin-midway')
const { reactPlugin } = require('ssr-plugin-react')
module.exports = {
serverPlugin: midwayPlugin(),
clientPlugin: reactPlugin()
}
- ssr-plugin-react 邏輯
// 依賴webpack-chain
import * as WebpackChain from 'webpack-chain'
// 包裝了webpack的功能
import { startClientServer, startServerBuild, startClientBuild } from 'ssr-webpack'
// 獲取客戶端和服務(wù)端構(gòu)建的配置
import { getClientWebpack, getServerWebpack } from './config'
export function reactPlugin () {
return {
name: 'plugin-react',
// 本地開發(fā)環(huán)境
start: async () => {
// 使用weback-chain 來配置webpack
const serverConfigChain = new WebpackChain()
// 調(diào)用的是 ssr-webpack 里面的方法
// server端開發(fā)環(huán)境的配置 見[附錄1]
await startServerBuild(getServerWebpack(serverConfigChain))
const clientConfigChain = new WebpackChain()
// 開發(fā)環(huán)境client端的配置 見[附錄2]
await startClientServer(getClientWebpack(clientConfigChain))
},
build: async () => {
const serverConfigChain = new WebpackChain()
await startServerBuild(getServerWebpack(serverConfigChain))
const clientConfigChain = new WebpackChain()
await startClientBuild(getClientWebpack(clientConfigChain))
}
}
}
- ssr-webpack
import { webpackPromisify } from '../utils/promisify'
const startServerBuild = async (webpackConfig: webpack.Configuration) => {
const { webpackStatsOption } = loadConfig()
// 這里將webpack包裝成了一個promise對象 方便結(jié)合await使用
const stats = await webpackPromisify(webpackConfig)
console.log(stats.toString(webpackStatsOption))
}
- promisify邏輯
比較簡單,利用了nodejs自帶的模塊 util提供的promisify 函數(shù)乙墙;
這個函數(shù)能夠?qū)xx(arg1, (err, res)=> {}) 這種格式的函數(shù)轉(zhuǎn)換成一個promise函數(shù)
import { promisify } from 'util'
import * as webpack from 'webpack'
const webpackPromisify = promisify<webpack.Configuration, webpack.Stats>(webpack)
export {
webpackPromisify
}
附錄:
- 開發(fā)環(huán)境server bundle構(gòu)建配置
{
"mode": "development",
"devtool": "eval-source-map",
"target": "node",
"watch": true,
"output": {
"path": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/build/server",
"filename": "[name].server.js",
"libraryTarget": "commonjs"
},
"resolve": {
"alias": {
"@": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/web",
"react": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/react/index.js",
"react-router": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/react-router/index.js",
"react-router-dom": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/react-router-dom/index.js"
},
"extensions": [
".web.mjs",
".mjs",
".web.js",
".js",
".web.ts",
".ts",
".web.tsx",
".tsx",
".json",
".web.jsx",
".jsx",
".vue"
],
"modules": [
"node_modules",
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules",
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/"
]
},
"module": {
"strictExportPresence": true,
"rules": [
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/url-loader/dist/cjs.js",
"options": {
"limit": 10000,
"name": "static/[name].[hash:8].[ext]",
"esModule": false,
"fallback": {
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/file-loader/dist/cjs.js",
"options": {
"name": "static/[name].[hash:8].[ext]",
"esModule": false
}
}
}
}
]
},
{
"test": {},
"exclude": [
{},
{}
],
"use": [
{
"loader": "babel-loader",
"options": {
"cacheDirectory": true,
"cacheCompression": false,
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
],
[
"react-app",
{
"flow": false,
"typescript": true
}
]
],
"plugins": [
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "lib",
"style": "css"
}
]
]
}
}
]
},
{
"test": {},
"exclude": [
[
{},
{}
]
],
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-hot-loader/index.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/mini-css-extract-plugin/dist/loader.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-loader/index.js",
"options": {
"importLoaders": 1,
"modules": true
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/postcss-loader/src/index.js",
"options": {
"ident": "postcss"
}
}
]
},
{
"test": {},
"include": [
[
{},
{}
]
],
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-hot-loader/index.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/mini-css-extract-plugin/dist/loader.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-loader/index.js",
"options": {
"importLoaders": 1,
"modules": false
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/postcss-loader/src/index.js",
"options": {
"ident": "postcss"
}
}
]
},
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-hot-loader/index.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/mini-css-extract-plugin/dist/loader.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-loader/index.js",
"options": {
"importLoaders": 2,
"modules": true
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/postcss-loader/src/index.js",
"options": {
"ident": "postcss"
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/less-loader/dist/cjs.js"
}
]
},
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/file-loader/dist/cjs.js",
"options": {
"name": "static/[name].[hash:8].[ext]",
"esModule": false
}
}
]
},
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/file-loader/dist/cjs.js",
"options": {
"name": "static/[name].[hash:8].[ext]",
"esModule": false
}
}
]
}
]
},
"plugins": [
{
"options": {
"filename": "static/css/[name].css",
"ignoreOrder": false,
"chunkFilename": "static/css/[name].chunk.css"
}
},
{
"definitions": {
"__isBrowser__": false
}
}
],
"entry": {
"Page": [
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/ssr-plugin-react/cjs/entry/server-entry.js"
]
}
}
- 開發(fā)環(huán)境client的webpack配置
{
"mode": "development",
"devtool": "cheap-module-source-map",
"output": {
"path": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/build/client",
"filename": "static/js/[name].js",
"chunkFilename": "static/js/[name].chunk.js",
"publicPath": "/"
},
"resolve": {
"alias": {
"@": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/web",
"react": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/react/index.js",
"react-router": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/react-router/index.js",
"react-router-dom": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/react-router-dom/index.js"
},
"extensions": [
".web.mjs",
".mjs",
".web.js",
".js",
".web.ts",
".ts",
".web.tsx",
".tsx",
".json",
".web.jsx",
".jsx",
".vue"
],
"modules": [
"node_modules",
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules",
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/"
]
},
"module": {
"strictExportPresence": true,
"rules": [
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/url-loader/dist/cjs.js",
"options": {
"limit": 10000,
"name": "static/[name].[hash:8].[ext]",
"esModule": false,
"fallback": {
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/file-loader/dist/cjs.js",
"options": {
"name": "static/[name].[hash:8].[ext]",
"esModule": false
}
}
}
}
]
},
{
"test": {},
"exclude": [
{}
],
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/babel-loader/lib/index.js",
"options": {
"cacheDirectory": true,
"cacheCompression": false,
"sourceType": "unambiguous",
"presets": [
[
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/@babel/preset-env/lib/index.js",
{
"modules": false
}
],
[
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/babel-preset-react-app/index.js",
{
"flow": false,
"typescript": true
}
]
],
"plugins": [
[
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/@babel/plugin-transform-runtime/lib/index.js",
{
"regenerator": false,
"corejs": false,
"helpers": true
}
],
[
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/babel-plugin-import/lib/index.js",
{
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css"
}
]
]
}
}
]
},
{
"test": {},
"exclude": [
[
{},
{}
]
],
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-hot-loader/index.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/mini-css-extract-plugin/dist/loader.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-loader/index.js",
"options": {
"importLoaders": 1,
"modules": true
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/postcss-loader/src/index.js",
"options": {
"ident": "postcss"
}
}
]
},
{
"test": {},
"include": [
[
{},
{}
]
],
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-hot-loader/index.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/mini-css-extract-plugin/dist/loader.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-loader/index.js",
"options": {
"importLoaders": 1,
"modules": false
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/postcss-loader/src/index.js",
"options": {
"ident": "postcss"
}
}
]
},
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-hot-loader/index.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/mini-css-extract-plugin/dist/loader.js"
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/css-loader/index.js",
"options": {
"importLoaders": 2,
"modules": true
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/postcss-loader/src/index.js",
"options": {
"ident": "postcss"
}
},
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/less-loader/dist/cjs.js"
}
]
},
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/file-loader/dist/cjs.js",
"options": {
"name": "static/[name].[hash:8].[ext]",
"esModule": false
}
}
]
},
{
"test": {},
"use": [
{
"loader": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/file-loader/dist/cjs.js",
"options": {
"name": "static/[name].[hash:8].[ext]",
"esModule": false
}
}
]
}
]
},
"optimization": {
"runtimeChunk": true,
"splitChunks": {
"chunks": "initial",
"name": false,
"cacheGroups": {
"vendors": {
"name": "vendor"
}
}
}
},
"plugins": [
{
"options": {
"filename": "static/css/[name].css",
"ignoreOrder": false,
"chunkFilename": "static/css/[name].chunk.css"
}
},
{
"definitions": {
"__isBrowser__": true
}
},
{
"appPath": "/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr"
},
{
"opts": {
"publicPath": "/",
"basePath": "",
"fileName": "asset-manifest.json",
"transformExtensions": {},
"writeToFileEmit": false,
"seed": null,
"filter": null,
"map": null,
"generate": null,
"sort": null
}
}
],
"entry": {
"Page": [
"/Users/oker/projects/company/tmp/ssr/ykfe-ssr/ssr/example/midway-react-ssr/node_modules/ssr-plugin-react/cjs/entry/client-entry.js"
]
}
}