03-Mediator/Middleware Pattern 中介者/中间件模式-vanilla篇

中介者模式使组件可以通过一个中心点(中介者)相互交互。中介者不是直接相互交谈,而是接收请求并将其转发。在 JavaScript 中,中介者通常只不过是一个对象文字或一个函数。 可以将此模式与空中交通管制员和飞行员之间的关系进行比较。飞行员之间不会直接相互交谈(这可能会导致混乱),而是与空中交通管制员交谈。空中交通管制员确保所有飞机都能收到安全飞行所需的信息,而不会撞到其他飞机。 在 Javascript 中,我们经常需要处理对象之间的多向数据。项目中如果有大量组件,组件之间的通信很可能会变得相当混乱。 对象的请求由中介处理,而不是让每个对象直接与其他对象对话,从而形成多对多关系。中介器处理该请求,并将其转发到需要的位置。 调解者模式的一个很好的用例是聊天室。聊天室中的用户不会直接相互交谈。相反,聊天室充当用户之间的中介。 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 方法,我们可以使用它来发送消息。 const chatroom = new ChatRoom(); const user1 = new User("John Doe", chatroom); const user2 = new User("Jane Doe", chatroom); user1....

August 2, 2023 · 1 min · 160 words · Anna.me

01-Command Pattern 命令模式-vanilla篇

使用命令模式,可以将执行某个任务的对象与调用该方法的对象解耦。 假设有一个外卖配送平台。用户可以下单、跟踪和取消订单。 class OrderManager() { constructor() { this.orders = [] } placeOrder(order, id) { this.orders.push(id) return `You have successfully ordered ${order} (${id})`; } trackOrder(id) { return `Your order ${id} will arrive in 20 minutes.` } cancelOrder(id) { this.orders = this.orders.filter(order => order.id !== id) return `You have canceled your order ${id}` } } 在 OrderManager 类上,可以访问 placeOrder 、 trackOrder 和 cancelOrder 方法。直接使用这些方法将是完全可行的。 但是,直接在 manager 实例上调用方法也有缺点。我们可能会决定稍后重命名某些方法,或者改变某些方法的功能。 假设现在将其重命名为 addOrder ,而不是 placeOrder ,这意味着我们必须确保代码库中的没有任何位置调用 placeOrder 方法,这种场景在大型应用程序中可能非常棘手。相反,我们希望将方法与 manager 对象解耦,并为每个命令创建单独的命令函数。...

July 12, 2023 · 1 min · 195 words · Anna.me

Redux 与 Redux-Toolkit 的实现对比

Redux 基本概念与 API Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。 Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。 State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。 Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。 Action 描述当前发生的事情。改变 State 的唯一办法,就是使用 Action。它会运送数据到 Store。 Reducer 是一个函数,它接受 当前 State 和 Action 作为参数,返回一个新的 State。 Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。 store 的三个方法 store.getState() // 获取当前 state store.dispatch() // 派发 action 至 reducer 处理 store.subscribe() // 订阅事件 state 变化即触发 示例代码 实现一个能够添加书籍,并能够根据书籍 ID 删除的功能。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1....

June 10, 2023 · 4 min · 723 words · Anna.me

记一次线上问题解决-访问页面刷新后报错 403 Forbidden

线上问题描述: 香港某站点登陆后跳转 dashboard 页面,刷新页面后报错 403 Forbidden。 问题定位: 前端目录文件下有命名为 dashboard 的文件夹,与路由重名 以下为 nginx 部分配置文件代码: location /entry { index entry.html } location / { ... autoindex on; index index.html; try_files $uri $uri/ /index.html } 打开 autoindex,从下图可以得出命中了第二条$uri/规则,但该目录下没有 index.html,nginx 尝试检索目录但是被禁止,所以报错 403。 为什么刷新后才报错? 第一次请求是 xxx.com/,命中 index.html,VueRouter 在 history 模式下,login 成功后,借助 history.pushState 实现页面的无刷新跳转到了 /,这种方式改变了 url,之后再根据判断权限点通过 redirect 跳转到对应的页面。如果重新刷新页面会造成一个新的 http 请求,因此会重新请求服务器,如果 nginx 没有匹配到当前 url,就会出现报错页面。 xxx.com/dashboard,按照 try_files 匹配顺序走到了$uri/,而目录中正好命中了 dashboard 文件夹,然而其中没有 index.html,nginx 尝试检索目录但是被禁止,于是报错 403 Forbidden。 为什么测试环境没有报错? 测试环境的 nginx 配置文件代码为: location / { ....

June 3, 2023 · 1 min · 92 words · Anna.me

TypeScript练习题:30天LeetCode挑战(day1 to day7)

LeetCode 挑战题目汇总地址: 30 Days of JavaScript Challenge DAY1: Create Hello World Function 解题代码: function createHelloWorld() { return function(...args): string { return "Hello World" }; }; 官方解題說明 https://leetcode.com/problems/create-hello-world-function/editorial/ DAY2: Counter 题目说明: 第一次调用 createCounter 传值 n, 后面每次调用返回都比前一次返回的值加1。 解题思路: 可以用变量和运算符的相对位置来记递增运算符返回的值是计算前还是计算后的值:n++ 的 n 放在 ++ 的前面,所以是运算前的值;++n 是放在 ++ 的后面,所以是运算后的值。 function createCounter(n: number): () => number { return function() { return n++ } } 官方解題說明 https://leetcode.com/problems/counter/editorial/ DAY3: Counter II 题目说明: 实现函数 createCounter,可以传一个初始值参数,返回包含三个函数的对象,三个函数分别是 increment()、decrement()、reset()。 解题思路: increment()、decrement() 都是返回计算过后的值,变量要放在运算符的后面。reset() 重置初始值,需要额外用变量储存当前计算值,保留初始值 init。...

February 20, 2023 · 2 min · 372 words · Anna.me