研究ssr

This commit is contained in:
2024-11-26 18:43:58 +08:00
commit f1d67392f1
22 changed files with 69598 additions and 0 deletions

3
gpt-query/one/.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

View File

@@ -0,0 +1,19 @@
import { build } from 'esbuild';
build({
entryPoints: ['./src/client.js'], // 入口文件
bundle: true, // 打包所有依赖
outfile: './public/client.js', // 输出文件
minify: false, // 压缩代码
sourcemap: false, // 生成 Source Map
platform: 'browser', // 指定运行环境为浏览器
target: ['es6'], // 目标环境
loader: { '.jsx': 'jsx', '.js': 'jsx' }, // 处理 JSX 文件
})
.then(() => {
console.log('Client build complete!');
})
.catch((err) => {
console.error('Build failed:', err);
process.exit(1);
});

View File

@@ -0,0 +1,20 @@
import { build } from 'esbuild';
build({
entryPoints: ['./server.tsx'], // 入口文件
bundle: true, // 打包所有依赖
outfile: './server.js', // 输出文件
minify: false, // 压缩代码
sourcemap: false, // 生成 Source Map
platform: 'node', // 指定运行环境为浏览器
target: ['es6'], // 目标环境
external: ['node_modules/*'], // 不打包 node_modules
loader: { '.jsx': 'jsx', '.tsx': 'tsx', '.js': 'jsx' }, // 处理 JSX 文件
})
.then(() => {
console.log('Client build complete!');
})
.catch((err) => {
console.error('Build failed:', err);
process.exit(1);
});

View File

@@ -0,0 +1,25 @@
{
"name": "react-ssr-example",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"build2": "babel src --out-dir public",
"build": "rollup -c",
"start": "node server.js",
"start2": "babel-node server.js"
},
"dependencies": {
"esbuild": "^0.24.0",
"express": "^4.21.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rollup": "^4.27.4"
},
"devDependencies": {
"@babel/cli": "^7.25.9",
"@babel/core": "^7.26.0",
"@babel/node": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.25.9"
}
}

3706
gpt-query/one/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
"use strict";
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _react = _interopRequireWildcard(require("react"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
var App = function App(_ref) {
var initialState = _ref.initialState;
var _useState = (0, _react.useState)(initialState.name),
_useState2 = _slicedToArray(_useState, 2),
name = _useState2[0],
setName = _useState2[1];
return /*#__PURE__*/_react["default"].createElement("div", {
onClick: function onClick() {
setName('Jane Doe');
}
}, "Hello World ", name);
};
var _default = exports["default"] = App;

24579
gpt-query/one/public/client.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
import resolve from '@rollup/plugin-node-resolve'; // 解析模块
import commonjs from '@rollup/plugin-commonjs'; // 转换 CommonJS 为 ESModules
import babel from '@rollup/plugin-babel'; // Babel 支持
// import { terser } from 'rollup-plugin-terser'; // 可选:压缩代码
export default {
input: './src/client.js', // 入口文件
output: {
file: './public/client.js', // 输出文件
format: 'iife', // 输出格式:立即执行函数 (适合浏览器)
sourcemap: true, // 生成 Source Map 方便调试
},
plugins: [
resolve(), // 解析 node_modules 中的依赖
commonjs(), // 转换 CommonJS 模块
babel({
presets: ['@babel/preset-env', '@babel/preset-react'],
babelHelpers: 'bundled', // 使用打包后的 Babel helpers
exclude: 'node_modules/**', // 排除 node_modules
}),
// terser(), // 可选:压缩输出代码
],
};

40021
gpt-query/one/server.js Normal file

File diff suppressed because one or more lines are too long

33
gpt-query/one/server.tsx Normal file
View File

@@ -0,0 +1,33 @@
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './src/App';
const app = express();
// 静态文件服务
app.use(express.static('public'));
// 首页路由
app.get('/', (req, res) => {
const initialState = { name: 'John Doe22' };
const html = renderToString(<App initialState={initialState} />);
res.send(`<!DOCTYPE html>
<html>
<head>
<title>React SSR</title>
</head>
<body>
<div id="root">${html}</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
</script>
<script type="module" src="/client.js"></script>
</body>
</html>`);
});
app.listen(3000, () => {
console.log('Server is running at http://localhost:3000');
});

19
gpt-query/one/src/App.tsx Normal file
View File

@@ -0,0 +1,19 @@
import React, { useEffect, useState } from 'react';
const App = ({ initialState }) => {
const [name, setName] = useState(initialState.name);
useEffect(() => {
setName('Client'); // 仅客户端执行
}, []);
return (
<div
onClick={() => {
setName('Jane Doe233');
}}
>
Hello World {name}
</div>
);
};
export default App;

View File

@@ -0,0 +1,7 @@
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import App from './App';
const initialState = window.__INITIAL_STATE__ || { name: 'Default Name' };
hydrateRoot(document.getElementById('root'), <App initialState={initialState} />);

View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"outDir": "dist",
"target": "ES2020",
"module": "ESNext",
"useDefineForClassFields": true,
"noEmit": true,
"emitDeclarationOnly": false,
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"jsx": "react-jsx",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
}
}