当前位置: 首页 > news >正文

描述cookie隔离的好处_??[译] 正交React组件的好处

v2-52bd86ad9a749171429ad2fb22be8b7b_1440w.jpg?source=172ae18b

作者:Dmitri Pavlutin

原文链接

正交性是几何学中的术语,互为直角的直角坐标系就具有正交性; 在计算技术中表示不依赖性或解耦性。非正交的系统意味着系统中各组件互相高度依赖,这类系统中是不再有局部修正的情况了。

#1. 为什么好的系统设计是重要的?

在5年前,我正在为一家欧洲初创公司开发跨平台移动应用。初期的功能是易于实现的,进展顺利。

6个月过去,需要不断的在现有功能上添加新的功能,随着时间的推移,对现有模块的更改越来越困难。

在部分需求上,开始拒绝某些新的功能和更新,因为它们将需要太多的时间实施。这个故事以移动应用程序完全重写为原生应用而告终,主要是因为进一步的维护非常的困难。

我将上述问题归咎于跨平台框架中的错误,归咎于客户端需求变更。但这不是主要问题,我没有意识到一点,我一直在于高度耦合的模块组件做战,就像堂吉柯德大战风车一样。

我忽略了组件易于更改的特性。我未遵循良好的设计原则,没有赋予组件适应潜在的变化的特性。学习设计原则,一个特别有影响力的正交原理,它可以隔离由于不同原因而变化的事物。

#2. 正交组件

如果A和B正交的,则更改A不会更改B(反之亦然)。这就是正交性的概念。在广播设备中,音量和电台选择控件是正交的。音量控制仅更改音量,而电台选择控件仅更改接收到的电台。

af5d5bdd9685ad43cfceba369172623a.png

想象一下广播设备坏了,音量控制可更改音量,但也可修改选定的广播电台。音量控制和电台选择控制不是正交的:音量控制会产生副作用。当你尝试向紧密耦合的组件中添加更改时,也会发生相同的情况:你不得不面对更改产生的副作用。

如果一个组件的更改不影响其他组件,则两个或多个组件正交。例如,显示文章列表的组件应与获取文章的逻辑正交。

一个好的React应用程序设计是正交的:

  • UI元素
  • 全局状态管理
  • 持久性逻辑(本地存储,cookie)
  • 获取数据 (fetch library, REST or GraphQL)

将组件隔离,并独立封装。这将使你的组件正交,并且你所做的任何更改都将被隔离,并且仅集中在一个组件上。这就是可预测且易于开发的系统的诀窍。

#3.使组件正交以获取获取

让我们来看看下面的例子:

import React, { useState } from 'react';
import axios from 'axios';
import EmployeesList from './EmployeesList';

function EmployeesPage() {
  const [isFetching, setFetching] = useState(false);
  const [employees, setEmployees] = useState([]);

  useEffect(function fetch() {
    (async function() {
      setFetching(true);
      const response = await axios.get("/employees");
      setEmployees(response.data);
      setFetching(false);
    })();
  }, []);
  
  if (isFetching) {
    return <div>Fetching employees....</div>;
  }
  return <EmployeesList employees={employees} />;
}

在以上代码中<EmployeesPage>通过axios库,执行GET请求获取数据。

如果以后从axios和REST切换到GraphQL会发生什么?如果应用程序具有数十个与获取数据逻辑耦合的组件,则必须手动更改所有组件。其实有更好的方法,让我们从组件中分离出获取数据逻辑细节。

一个很好的方法是使用React的新功能Suspense:

import React, { Suspense } from "react";
import EmployeesList from "./EmployeesList";

function EmployeesPage({ resource }) {
  return (
    <Suspense fallback={<h1>Fetching employees....</h1>}>
      <EmployeesFetch resource={resource} />
    </Suspense>
  );
}

function EmployeesFetch({ resource }) {
  const employees = resource.employees.read();
  return <EmployeesList employees={employees} />;
}

现在,直到<EmployeesFetch>读取异步资源之前,<EmployeesPage>都会挂起.

重要的是<EmployeesPage>与获取数据逻辑正交<EmployeesPage>不在乎axios是否实现抓取,你可以轻松地将axios更改为本地获取、或迁移为GraphQL:<EmployeesPage>不受影响。

React:Suspense 文档

#4.使视图与滚动监听器正交

假设您你要跳转到顶部按钮,以在用户向下滚动500px以上时显示。单击该按钮时,页面将自动滚动到顶部。

f92a2710f31f74de1ec41c5c7122ff6a.gif

<ScrollToTop>第一个简单的实现:

import React, { useState, useEffect } from 'react';

const DISTANCE = 500;

function ScrollToTop() {
  const [crossed, setCrossed] = useState(false);

  useEffect(
    function() {
      const handler = () => setCrossed(window.scrollY > DISTANCE);
      handler();
      window.addEventListener("scroll", handler);
      return () => window.removeEventListener("scroll", handler);
    },
    []
  );

  function onClick() {
    window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
  }

  if (!crossed) {
    return null;
  }
  return <button onClick={onClick}>Jump to top</button>;
}

<ScrollToTop>实现滚动监听器并呈现一个将页面滚动到顶部的按钮,问题在于这些概念可能会以不同的形式变化。

更好的正交设计应将滚动监听器与UI隔离,让我们将滚动监听器逻辑提取到自定义钩子useScrollDistance()中:

import { useState, useEffect } from 'react';

function useScrollDistance(distance) {
  const [crossed, setCrossed] = useState(false);

  useEffect(function() {
    const handler = () => setCrossed(window.scrollY > distance);
    handler();
    window.addEventListener("scroll", handler);
    return () => window.removeEventListener("scroll", handler);
  }, [distance]);

  return crossed;
}

然后,在组件<IfScrollCrossed>中使用useScrollAtBottom()

function IfScrollCrossed({ children, distance }) {
  const isBottom = useScrollDistance(distance);
  return isBottom ? children : null;
}

<IfScrollCrossed>仅在用户滚动特定距离时才显示,最后,这是单击时滚动到顶部的按钮:

function onClick() {
  window.scrollTo({
    top: 0,
    behavior: 'smooth'
  });
}

function JumpToTop() {
  return <button onClick={onClick}>Jump to top</button>;
}

现在,如果你想使一切正常工作,只需将<JumpToTop>放在<IfAtBottom>的中即可:

import React from 'react';
// ...
const DISTANCE = 500;

function MyComponent() {
  // ...
  return (
    <IfScrollCrossed distance={DISTANCE}>
      <JumpToTop />
    </IfScrollCrossed>
  );
}

重要的是<IfScrollCrossed>隔离滚动监听器,UI元素的更改也隔离在<JumpToTop>组件中,在这里滚动监听器逻辑和UI元素是正交的。另一个好处是你可以将<IfScrollCrossed>与任何UI结合使用。例如,当用户向下滚动300px时,您可以显示新闻表单:

import React from 'react';
// ...
const DISTANCE_NEWSLETTER = 300;

function OtherComponent() {
  // ...
  return (
    <IfScrollCrossed distance={DISTANCE_NEWSLETTER}>
      <SubscribeToNewsletterForm />
    </IfScrollCrossed>
  );
}

#5.“Main”组件

尽管将变化隔离到单独的组件中是正交性的全部内容,但是可能由于不同的原因改变组件。这些就是所谓的“Main”组件(也称为“App”)组件。

你可以在最外层的index.jsx(或app。jsx)文件内找到“Main”组件:即启动应用程序的组件。它知道有关该应用程序的所有细节:初始化全局状态提供程序(如Redux),配置获取库(如GraphQL Apollo),将路由与组件关联等等。

你可能有几个“Main”组件:用于客户端(在浏览器中运行)和用于服务器端(实现服务器端渲染)。

#6.正交设计的好处

#易于修改

当组件是正交设计时,对组件所做的任何更改都将隔离在组件内。

#易读

由于正交组件仅负责一个任务,因此更容易了解该组件的功能,它不被不属于这里的细节所困扰。

#易测试

正交组件仅专注于执行单个任务,你要做的只是测试组件是否正确执行任务。通常,非正交组件需要大量的模拟和手动设置才能进行测试,而且,如果难以测试。而现在你只需修改单个组件。

#7.思考设计原则

我喜欢新的React功能,例如hookssuspense等。但是,我也尝试着更广泛地思考,探索这些功能是否有助于我遵循良好的设计。

  • 为什么要使用React hooks?它们使UI渲染逻辑state副作用逻辑正交
  • 为什么Suspense获取?它使获取的细节和组件正交

#8. 权衡

让我们回想一下“星球大战之西斯的复仇”电影中的一幕。在阿纳金·天行者被他的前导师欧比·旺·克诺比(Obi-Wan Kenobi)击败后,后者说:

给原力带来平衡,不要把它留在黑暗中

阿纳金·天行者被选为绝地武士,在黑暗与光明的两面之间取得平衡。

正交设计通过YAGNI: “You ain't gonna need it”原则来平衡。

YAGNI成为极限编程的原则:

始终在真正需要它们时执行这些事情,永远不要在仅仅预见到可能需要它们时才执行。

(我的理解:只有真正需要时才使用)

d656eebc3e4d651556952fde1a82a58c.png

回顾一下文章的开头部分我的故事:我最终获得了一个困难且更改成本很高的应用程序,我的错误是:我无意中创建了并非为更改而设计的组件。这是YAGNI的极端情况。

另一方面,如果每条逻辑正交,那么你最终将创建过多不需要的抽象,这是正交设计的极限。

实际的方法是预见变化,详细研究你的应用程序解决的领域问题,并提供潜在功能的列表。如果你认为某个地方会发生变化,请使用正交设计原则。

#8.关键点

编写软件不仅与实现应用程序的要求有关,同样重要的是,要努力设计好组件。

良好设计的关键原则是隔离最有可能改变的逻辑:使其正交。这使你的整个系统具有灵活性,并且可以适应更改或添加新功能。

你想知道更多吗?你的下一步是可以阅读全英版:The Pragmatic Programmer。

ps: 微信公众号:Yopai,有兴趣的可以关注,每周不定期更新。不断分享,不断进步

f005ccb5edfb501b09f3e3cb4486871d.png

相关文章:

  • CSDN英雄会上会英雄
  • bat批量查找文件并复制_Word还有这么超级实用的批量操作技巧,得学会
  • datagridview删除选中行_删除重复项见多了,但保留重复项要怎么做
  • python如何安装whl_python pip whl安装和使用
  • Symbian OS编码诀窍之设计诀窍
  • python程序语法元素的描述_python有哪些语法元素
  • Symbian屏幕双缓冲DSA
  • mysql如何判断当前扫描的是第一条记录_MySQL锁机制——你想知道的都在这了
  • CSDN英雄会游记
  • python批量将pdf转成word_python批量实现Word文件转换为PDF文件
  • lstm时间序列预测_pytorch入门使用PyTorch进行LSTM时间序列预测
  • CSDN软件英雄会流水帐
  • jvm内存结构_你真的懂JVM内存结构吗?—深入理解JVM之内存结构
  • 技术大会英雄谱
  • python自动化和java自动化_Python和Java哪个更适合做自动化测试
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • Angularjs之国际化
  • avalon2.2的VM生成过程
  • JavaScript 奇技淫巧
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • JS基础之数据类型、对象、原型、原型链、继承
  • Kibana配置logstash,报表一体化
  • SpringBoot几种定时任务的实现方式
  • Sublime text 3 3103 注册码
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 思否第一天
  • 听说你叫Java(二)–Servlet请求
  • 微服务入门【系列视频课程】
  • 最简单的无缝轮播
  • ​第20课 在Android Native开发中加入新的C++类
  • %@ page import=%的用法
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (C语言)逆序输出字符串
  • (day 12)JavaScript学习笔记(数组3)
  • (pojstep1.3.1)1017(构造法模拟)
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (篇九)MySQL常用内置函数
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (十六)一篇文章学会Java的常用API
  • (译)2019年前端性能优化清单 — 下篇
  • (转)ObjectiveC 深浅拷贝学习
  • (转)德国人的记事本
  • .gitignore文件设置了忽略但不生效
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .NET Core跨平台微服务学习资源
  • .net和php怎么连接,php和apache之间如何连接
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .NET中winform传递参数至Url并获得返回值或文件
  • /var/log/cvslog 太大
  • [1159]adb判断手机屏幕状态并点亮屏幕
  • [20150707]外部表与rowid.txt
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗
  • [C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)
  • [C++]打开新世界的大门之C++入门