有兴趣阅读我们的下一本书吗?了解更多关于 使用 React 构建大型 JavaScript Web 应用

设计模式

中介者/中间件模式

中介者模式允许组件通过一个中心点(中介者)相互交互。组件之间不直接通信,而是由中介者接收请求并转发!在 JavaScript 中,中介者通常只是一个对象字面量或一个函数。

你可以将这个模式与空中交通管制员和飞行员之间的关系进行比较。飞行员之间不直接交流(这样可能会很混乱),而是通过空中交通管制员进行交流。空中交通管制员确保所有飞机都能收到它们安全飞行所需的信息,而不会撞上其他飞机。

虽然我们希望在 JavaScript 中不会控制飞机,但我们经常不得不处理对象之间的多向数据。如果组件数量很多,组件之间的通信可能会变得相当混乱。

Flow

与其让每个对象都直接与其他对象进行交流(导致多对多关系),不如让对象的请求由中介者处理。中介者处理这个请求,并将其转发到需要的地方。

Flow

中介者模式的一个很好的用例是聊天室!聊天室中的用户不会直接互相交流。相反,聊天室充当用户之间的中介者。

class ChatRoom {
  logMessage(user, message) {
    const time = new Date();
    const sender = user.getName();

    console.log(`${time} [${sender}]: ${message}`);
  }
}

class User {
  constructor(name, chatroom) {
    this.name = name;
    this.chatroom = chatroom;
  }

  getName() {
    return this.name;
  }

  send(message) {
    this.chatroom.logMessage(this, message);
  }
}

我们可以创建连接到聊天室的新用户。每个用户实例都有一个 send 方法,我们可以使用它来发送消息。

index.js
1class ChatRoom {
2 logMessage(user, message) {
3 const sender = user.getName();
4 console.log(`${new Date().toLocaleString()} [${sender}]: ${message}`);
5 }
6}
7
8class User {
9 constructor(name, chatroom) {
10 this.name = name;
11 this.chatroom = chatroom;
12 }
13
14 getName() {
15 return this.name;
16 }
17
18 send(message) {
19 this.chatroom.logMessage(this, message);
20 }
21}
22
23const chatroom = new ChatRoom();
24
25const user1 = new User("John Doe", chatroom);
26const user2 = new User("Jane Doe", chatroom);
27
28user1.send("Hi there!");
29user2.send("Hey!");

案例研究

Express.js 是一款流行的 Web 应用服务器框架。我们可以为用户可以访问的特定路由添加回调函数。

假设我们想要在用户访问根目录 '/' 时向请求添加一个标题。我们可以在中间件回调函数中添加这个标题。

const app = require("express")();

app.use("/", (req, res, next) => {
  req.headers["test-header"] = 1234;
  next();
});

next 方法调用请求-响应循环中的下一个回调函数。实际上,我们是在请求和响应之间创建了一个中间件函数链,反之亦然。

Flow

让我们再添加一个中间件函数,检查 test-header 是否已正确添加。先前中间件函数添加的更改将在此链中可见。

const app = require("express")();

app.use(
  "/",
  (req, res, next) => {
    req.headers["test-header"] = 1234;
    next();
  },
  (req, res, next) => {
    console.log(`Request has test header: ${!!req.headers["test-header"]}`);
    next();
  }
);

完美!我们可以通过一个或多个中间件函数跟踪和修改请求对象,直至响应。

index.js
1const app = require("express")();
2 const html = require("./data");
3
4 app.use(
5 "/",
6 (req, res, next) => {
7 req.headers["test-header"] = 1234;
8 next();
9 },
10 (req, res, next) => {
11 console.log(`Request has test header: ${!!req.headers["test-header"]}`);
12 next();
13 }
14 );
15
16 app.get("/", (req, res) => {
17 res.set("Content-Type", "text/html");
18 res.send(Buffer.from(html));
19 });
20
21 app.listen(8080, function() {
22 console.log("Server is running on 8080");
23 });

每当用户访问根端点 '/' 时,都会调用这两个中间件回调函数。


中间件模式使我们能够轻松地简化对象之间的多对多关系,通过让所有通信都通过一个中心点进行。


参考资料