服务端渲染(SSR)中的设计模式考量
服务端渲染(SSR)中的设计模式考量
服务端渲染在现代Web开发中扮演着重要角色,它直接影响首屏性能、SEO和用户体验。在实现SSR时,合理运用设计模式能有效解决数据流管理、组件复用和状态同步等核心问题。
工厂模式在组件创建中的应用
服务端渲染环境需要创建大量组件实例,工厂模式能统一组件创建逻辑。特别是在需要根据运行环境区分客户端和服务器端组件时,这种模式表现出色。
class ComponentFactory {
static createComponent(type, props) {
if (typeof window === 'undefined') {
return new ServerComponent(props);
} else {
return new ClientComponent(props);
}
}
}
// 使用示例
const component = ComponentFactory.createComponent('button', {
text: 'Click me'
});
这种实现方式确保在服务器端和客户端使用不同的组件实现,同时保持一致的接口。对于同构组件,可以进一步扩展工厂方法:
class UniversalComponentFactory {
static createComponent(type) {
const components = {
header: UniversalHeader,
footer: UniversalFooter
};
if (!components[type]) {
throw new Error(`Unknown component type: ${type}`);
}
return components[type];
}
}
单例模式管理全局状态
SSR应用中,需要确保服务器端和客户端共享相同的应用状态。单例模式在这里能防止状态重复初始化,保持数据一致性。
class AppState {
constructor() {
if (AppState.instance) {
return AppState.instance;
}
this.user = null;
this.theme = 'light';
AppState.instance = this;
}
static getInstance() {
if (!AppState.instance) {
AppState.instance = new AppState();
}
return AppState.instance;
}
}
// 服务器端使用
const serverState = new AppState();
serverState.user = { id: 1, name: 'John' };
// 客户端获取相同实例
const clientState = AppState.getInstance();
console.log(clientState.user); // { id: 1, name: 'John' }
对于更复杂的场景,可以考虑结合序列化技术:
class SerializableState {
// ...类似上面的实现...
serialize() {
return JSON.stringify(this);
}
static deserialize(json) {
const data = JSON.parse(json);
const instance = new SerializableState();
Object.assign(instance, data);
return instance;
}
}
// 服务器端
const state = new SerializableState();
const serialized = state.serialize();
// 将serialized嵌入HTML
// 客户端
const restoredState = SerializableState.deserialize(window.__INITIAL_STATE__);
策略模式处理渲染差异
不同页面可能需要不同的渲染策略,策略模式允许动态切换渲染逻辑而不修改主流程代码。
class RenderStrategy {
render() {
throw new Error('Method not implemented');
}
}
class SSGStrategy extends RenderStrategy {
render(component) {
// 静态生成逻辑
return generateStaticHTML(component);
}
}
class SSRStrategy extends RenderStrategy {
render(component) {
// 服务端渲染逻辑
return renderToString(component);
}
}
class RenderContext {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
executeRender(component) {
return this.strategy.render(component);
}
}
// 使用示例
const context = new RenderContext(new SSRStrategy());
const html = context.executeRender(App);
// 切换到静态生成
context.setStrategy(new SSGStrategy());
观察者模式实现数据预取
SSR需要预先获取数据再渲染,观察者模式能优雅地处理数据准备和渲染的依赖关系。
class DataObserver {
constructor() {
this.observers = [];
this.data = null;
}
subscribe(fn) {
this.observers.push(fn);
}
unsubscribe(fn) {
this.observers = this.observers.filter(subscriber => subscriber !== fn);
}
async fetchData() {
this.data = await fetch('/api/data');
this.notify();
}
notify() {
this.observers.forEach(observer => observer(this.data));
}
}
// 组件中使用
const dataObserver = new DataObserver();
class MyComponent {
constructor() {
dataObserver.subscribe(this.update.bind(this));
}
update(data) {
this.render(data);
}
render(data) {
// 使用数据渲染
}
}
// 启动数据获取
dataObserver.fetchData();
代理模式处理API请求
在SSR环境中,服务器端和客户端的API请求方式可能不同,代理模式可以统一接口。
class ApiClient {
request(endpoint) {
// 基础请求实现
}
}
class ServerApiProxy extends ApiClient {
request(endpoint) {
// 服务器端特定逻辑,如添加headers
return super.request(endpoint);
}
}
class ClientApiProxy extends ApiClient {
request(endpoint) {
// 客户端特定逻辑,如处理凭证
return fetch(endpoint);
}
}
// 根据环境创建代理
function createApiClient() {
if (typeof window === 'undefined') {
return new ServerApiProxy();
} else {
return new ClientApiProxy();
}
}
const api = createApiClient();
api.request('/data');
装饰器模式增强组件功能
SSR场景下,组件可能需要额外的服务端能力,装饰器模式可以动态添加这些功能。
function withServerData(WrappedComponent) {
return class extends React.Component {
static async getInitialProps(ctx) {
const data = await fetchServerData();
return { ...data, ...(WrappedComponent.getInitialProps ? await WrappedComponent.getInitialProps(ctx) : {}) };
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
// 使用装饰器
class MyComponent extends React.Component {
// 组件实现
}
export default withServerData(MyComponent);
对于更复杂的装饰场景,可以组合多个装饰器:
function withLogger(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log('Component mounted', WrappedComponent.name);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
// 组合装饰
const EnhancedComponent = withLogger(withServerData(MyComponent));
模板方法模式统一渲染流程
SSR通常有固定的处理流程,模板方法模式可以确保步骤一致性。
abstract class RenderPipeline {
async render() {
await this.prepareData();
const content = await this.renderContent();
const html = this.wrapDocument(content);
return html;
}
abstract prepareData();
abstract renderContent();
wrapDocument(content) {
return `
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="app">${content}</div>
<script src="/client.js"></script>
</body>
</html>
`;
}
}
class ProductPageRenderer extends RenderPipeline {
async prepareData() {
this.data = await fetchProductData();
}
async renderContent() {
return renderToString(<ProductPage data={this.data} />);
}
}
// 使用
const renderer = new ProductPageRenderer();
const html = await renderer.render();
状态模式管理渲染生命周期
SSR过程涉及多个状态转换,状态模式可以清晰管理这些状态。
class RenderState {
constructor(context) {
this.context = context;
}
start() {
throw new Error('Method not implemented');
}
complete() {
throw new Error('Method not implemented');
}
error() {
throw new Error('Method not implemented');
}
}
class InitialState extends RenderState {
start() {
console.log('Starting render process');
this.context.setState(new DataFetchingState(this.context));
}
}
class DataFetchingState extends RenderState {
async start() {
try {
this.context.data = await fetchData();
this.context.setState(new RenderingState(this.context));
this.context.currentState.start();
} catch (err) {
this.context.setState(new ErrorState(this.context, err));
}
}
}
class RenderingState extends RenderState {
start() {
this.context.html = renderToString(this.context.app);
this.context.setState(new CompletedState(this.context));
}
}
class RenderContext {
constructor() {
this.setState(new InitialState(this));
this.data = null;
this.html = null;
}
setState(state) {
this.currentState = state;
}
async render() {
await this.currentState.start();
}
}
// 使用
const context = new RenderContext();
await context.render();
组合模式构建页面结构
复杂页面通常由多个部分组成,组合模式可以统一处理整体和部分的关系。
class PageComponent {
constructor(name) {
this.name = name;
this.children = [];
}
add(component) {
this.children.push(component);
}
async render() {
const childrenHtml = await Promise.all(
this.children.map(child => child.render())
);
return `
<section class="${this.name}">
${childrenHtml.join('')}
</section>
`;
}
}
class HeaderComponent {
async render() {
return '<header>Header Content</header>';
}
}
class FooterComponent {
async render() {
return '<footer>Footer Content</footer>';
}
}
// 构建页面
const page = new PageComponent('app');
page.add(new HeaderComponent());
page.add(new PageComponent('main-content'));
page.add(new FooterComponent());
const html = await page.render();
适配器模式整合不同SSR框架
当需要整合多个SSR解决方案时,适配器模式可以提供统一接口。
class NextJSAdapter {
constructor(nextApp) {
this.nextApp = nextApp;
}
async render(req, res) {
return this.nextApp.render(req, res);
}
}
class NuxtJSAdapter {
constructor(nuxt) {
this.nuxt = nuxt;
}
async render(req, res) {
return this.nuxt.render(req, res);
}
}
class SSRGateway {
constructor(adapter) {
this.adapter = adapter;
}
setAdapter(adapter) {
this.adapter = adapter;
}
handleRequest(req, res) {
return this.adapter.render(req, res);
}
}
// 使用
const nextApp = require('next')(...);
const adapter = new NextJSAdapter(nextApp);
const gateway = new SSRGateway(adapter);
// 处理请求
server.use((req, res) => gateway.handleRequest(req, res));
备忘录模式实现渲染快照
在SSR过程中,有时需要保存和恢复渲染状态,备忘录模式可以管理这些状态快照。
class RendererMemento {
constructor(state) {
this.state = JSON.parse(JSON.stringify(state));
}
getState() {
return this.state;
}
}
class SSRRenderer {
constructor() {
this.state = {
data: null,
html: '',
error: null
};
}
createMemento() {
return new RendererMemento(this.state);
}
restoreMemento(memento) {
this.state = memento.getState();
}
async render() {
try {
const memento = this.createMemento();
this.state.data = await fetchData();
this.state.html = renderToString(<App data={this.state.data} />);
return this.state;
} catch (error) {
this.state.error = error;
throw error;
}
}
}
// 使用
const renderer = new SSRRenderer();
// 渲染前保存状态
const beforeRender = renderer.createMemento();
try {
await renderer.render();
} catch (error) {
// 出错时恢复状态
renderer.restoreMemento(beforeRender);
}
责任链模式处理渲染中间件
SSR流程通常需要经过多个处理步骤,责任链模式可以灵活组织这些步骤。
class RenderMiddleware {
constructor() {
this.nextMiddleware = null;
}
setNext(middleware) {
this.nextMiddleware = middleware;
return middleware;
}
async process(request, response) {
if (this.nextMiddleware) {
return await this.nextMiddleware.process(request, response);
}
return null;
}
}
class DataFetchingMiddleware extends RenderMiddleware {
async process(request, response) {
request.data = await fetchData(request.url);
return super.process(request, response);
}
}
class RenderingMiddleware extends RenderMiddleware {
async process(request, response) {
request.html = renderToString(<App data={request.data} />);
return super.process(request, response);
}
}
class ResponseMiddleware extends RenderMiddleware {
async process(request, response) {
response.send(request.html);
return true;
}
}
// 构建责任链
const dataMiddleware = new DataFetchingMiddleware();
const renderMiddleware = new RenderingMiddleware();
const responseMiddleware = new ResponseMiddleware();
dataMiddleware.setNext(renderMiddleware).setNext(responseMiddleware);
// 处理请求
server.use(async (req, res) => {
await dataMiddleware.process(req, res);
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:前端性能优化中的设计模式实践