您现在的位置是:网站首页 > 服务端渲染(SSR)中的设计模式考量文章详情

服务端渲染(SSR)中的设计模式考量

服务端渲染(SSR)中的设计模式考量

服务端渲染是现代Web应用开发中的重要技术,它通过在服务器端生成HTML内容来提升首屏性能和SEO效果。在实现SSR时,合理运用设计模式可以显著提高代码的可维护性和扩展性。从工厂模式创建组件实例,到策略模式处理不同渲染策略,再到观察者模式管理状态变化,设计模式为SSR架构提供了系统化的解决方案。

工厂模式在组件初始化中的应用

服务端渲染环境中,组件的创建过程需要考虑同构特性。工厂模式可以帮助统一客户端和服务端的组件实例化逻辑:

class ComponentFactory {
  static createComponent(type, props) {
    if (typeof window === 'undefined') {
      // 服务端渲染逻辑
      return new ServerComponent(type, props);
    } else {
      // 客户端hydrate逻辑
      return new ClientComponent(type, props);
    }
  }
}

// 使用示例
const component = ComponentFactory.createComponent('Button', {
  text: 'Submit'
});

这种实现方式解决了SSR中常见的"window is not defined"问题,同时保持了客户端和服务端创建逻辑的一致性。对于复杂组件树,可以扩展工厂类实现更精细的控制:

class AdvancedComponentFactory extends ComponentFactory {
  static createLayoutComponent(config) {
    const components = config.items.map(item => 
      this.createComponent(item.type, item.props)
    );
    return new LayoutComponent(components);
  }
}

策略模式处理渲染路径选择

不同页面或路由可能需要不同的渲染策略。策略模式允许动态切换SSR、CSR或混合渲染方式:

const renderingStrategies = {
  ssr: (component) => {
    const html = renderToString(component);
    return { html, shouldHydrate: true };
  },
  csr: (component) => {
    return { html: '<div id="root"></div>', shouldHydrate: false };
  },
  hybrid: (component) => {
    const partialHtml = renderToStaticMarkup(component);
    return { html: partialHtml, shouldHydrate: true };
  }
};

function renderPage(strategyType, component) {
  const strategy = renderingStrategies[strategyType] || renderingStrategies.ssr;
  return strategy(component);
}

这种模式特别适合AB测试场景,可以基于用户特征或页面类型动态选择最优渲染策略:

// 根据路由决定渲染策略
function getStrategyForRoute(route) {
  if (route === '/dashboard') return 'csr';
  if (route === '/product') return 'ssr';
  return 'hybrid';
}

观察者模式管理应用状态

在SSR架构中,服务器和客户端需要共享和同步应用状态。观察者模式提供了一种有效的状态管理机制:

class SSRStateManager {
  constructor(initialState) {
    this.state = initialState;
    this.subscribers = [];
  }

  subscribe(component) {
    this.subscribers.push(component);
  }

  setState(newState) {
    this.state = {...this.state, ...newState};
    this.notifySubscribers();
  }

  notifySubscribers() {
    this.subscribers.forEach(sub => sub.update(this.state));
  }

  getSerializedState() {
    return JSON.stringify(this.state);
  }
}

// 服务端使用
const stateManager = new SSRStateManager({ user: null });
stateManager.setState({ user: { name: 'Alice' } });
const serializedState = stateManager.getSerializedState();

// 客户端恢复
window.__INITIAL_STATE__ = serializedState;
const clientStateManager = new SSRStateManager(JSON.parse(serializedState));

代理模式处理API请求

在SSR环境中,服务器和客户端可能需要不同的API请求处理方式。代理模式可以抽象这种差异:

class APIProxy {
  constructor(context) {
    this.context = context; // 包含req/res对象(服务端)或window对象(客户端)
  }

  async fetchData(endpoint) {
    if (this.context.req) {
      // 服务端请求:使用绝对URL并转发cookie
      return this.serverFetch(endpoint);
    } else {
      // 客户端请求:使用相对URL
      return this.clientFetch(endpoint);
    }
  }

  async serverFetch(endpoint) {
    const { req } = this.context;
    const absoluteUrl = `https://api.example.com${endpoint}`;
    const res = await fetch(absoluteUrl, {
      headers: { Cookie: req.headers.cookie || '' }
    });
    return res.json();
  }

  async clientFetch(endpoint) {
    const res = await fetch(endpoint);
    return res.json();
  }
}

// 使用示例
const apiProxy = new APIProxy(typeof window === 'undefined' ? { req } : window);
const data = await apiProxy.fetchData('/user');

装饰器模式增强渲染功能

装饰器模式可以为基本渲染功能动态添加额外能力,如性能监控、缓存等:

function withRenderCache(renderFn) {
  const cache = new Map();
  return async (component, cacheKey) => {
    if (cache.has(cacheKey)) {
      return cache.get(cacheKey);
    }
    const result = await renderFn(component);
    cache.set(cacheKey, result);
    return result;
  };
}

function withTiming(renderFn) {
  return async (component) => {
    const start = Date.now();
    const result = await renderFn(component);
    const end = Date.now();
    console.log(`Render took ${end - start}ms`);
    return { ...result, timing: end - start };
  };
}

// 组合使用装饰器
const basicRender = component => renderToString(component);
const enhancedRender = withTiming(withRenderCache(basicRender));

// 使用增强后的渲染器
const { html, timing } = await enhancedRender(<App />, 'home-page');

适配器模式整合不同SSR框架

当项目需要迁移或整合不同SSR框架时,适配器模式可以平滑过渡:

// Next.js适配器
class NextAdapter {
  constructor(nextApp) {
    this.nextApp = nextApp;
  }

  async render(req, res) {
    return new Promise((resolve) => {
      this.nextApp.render(req, res, req.url, (err, html) => {
        if (err) throw err;
        resolve(html);
      });
    });
  }
}

// Express适配器
class ExpressAdapter {
  constructor(expressApp) {
    this.expressApp = expressApp;
  }

  async render(req, res) {
    return new Promise((resolve) => {
      this.expressApp(req, res, () => {
        resolve(res.html);
      });
    });
  }
}

// 统一接口
async function renderWithAdapter(adapter, req, res) {
  return adapter.render(req, res);
}

单例模式管理全局配置

SSR应用通常需要共享配置信息,单例模式确保配置一致性和内存效率:

class SSRConfig {
  constructor() {
    if (!SSRConfig.instance) {
      this.settings = {
        cacheTTL: 3600,
        compressLevel: 6,
        prerenderRoutes: ['/', '/about']
      };
      SSRConfig.instance = this;
    }
    return SSRConfig.instance;
  }

  updateSettings(newSettings) {
    this.settings = { ...this.settings, ...newSettings };
  }
}

// 服务端使用
const config = new SSRConfig();
config.updateSettings({ cacheTTL: 1800 });

// 客户端使用
const clientConfig = new SSRConfig();
console.log(clientConfig.settings.cacheTTL); // 1800

模板方法模式定义渲染流程

模板方法模式可以规范SSR的生命周期,确保必要的步骤得到执行:

abstract class SSRRenderer {
  async render(component) {
    this.beforeRender();
    const data = await this.fetchData();
    const html = this.renderToString(component, data);
    const finalHtml = this.afterRender(html);
    return finalHtml;
  }

  beforeRender() {
    // 默认空实现
  }

  abstract fetchData();

  abstract renderToString(component, data);

  afterRender(html) {
    // 基本后处理
    return html;
  }
}

class ProductPageRenderer extends SSRRenderer {
  async fetchData() {
    return api.fetch('/products');
  }

  renderToString(component, data) {
    return renderToString(<component products={data} />);
  }

  afterRender(html) {
    return minifyHTML(html);
  }
}

组合模式构建页面结构

复杂页面的SSR可以通过组合模式将简单组件组合成复杂结构:

class PageComponent {
  constructor() {
    this.children = [];
  }

  add(component) {
    this.children.push(component);
  }

  async renderToString() {
    const childrenHTML = await Promise.all(
      this.children.map(child => child.renderToString())
    );
    return `<div class="page">${childrenHTML.join('')}</div>`;
  }
}

class HeaderComponent {
  async renderToString() {
    return `<header>...</header>`;
  }
}

class ContentComponent {
  async renderToString() {
    const data = await fetchContent();
    return `<main>${data}</main>`;
  }
}

// 组合使用
const page = new PageComponent();
page.add(new HeaderComponent());
page.add(new ContentComponent());
const html = await page.renderToString();

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步