美文网首页前端跟我学Web前端之路
翻译|如何规模化 React 应用

翻译|如何规模化 React 应用

作者: iKcamp | 来源:发表于2017-01-12 13:50 被阅读133次

    译者:朱会震(沪江前端架构师)

    本文为原创翻译,如需转载请标明出处,不当之处敬请指出

    我们最近发布了 React Boilerplate 3.0,在发布前几个月,我们与数百位开发者进行了沟通,讨论了他们是如何构建和规模化WEB应用的。下面将我们从中学到的东西分享给大家。

    很早我们就意识到,我们不希望React Boilerplate仅仅是“另一个模板项目”,我们希望它能为开发者提供创业或研发产品的最优解决方案。

    过去,规模化大多和服务器系统有关。随着使用的用户越来越多,你需要确保能在集群中添加更多的服务器,数据库能够拆分到多台服务器上。

    但现在,随着富WEB应用的崛起,规模化在前端也变得越来越重要!一个复杂应用的前端也需要能应付得了大量的用户、开发者和模块,需要重视这三个方面(用户、开发者和组件)的规模化,否则后面就会碰到问题。

    容器和组件

    对于一个大的应用,第一个大的改进就是,了解有状态(容器)和无状态(组件)组件的区别。容器管理数据和连接状态,通常没有样式。而组件则有样式,但不负责任何数据和状态的管理,最初我比较迷惑。基本上,容器负责让组件正常工作,组件负责东西如何展示。

    根据这一点,我们清楚地区分了可复用组件和数据管理层。现在你去编辑组件时,就不用担心会搞混数据结构;编辑容器,也不用担心弄乱样式了。按照这个思路开发应用将会变得很简单!

    代码结构

    按照惯例,开发者们会按照类型来组织结构,比如使用 actions/,components/,containers/ 等文件夹。

    假设有一个名称为NavBar的导航条容器,容器会管理与之相关的状态,还有一个toggleNav动作来打开关闭它。下面就是按类型组织的文件结构:

    react-app-by-type

    ├── css

    ├── actions

    │ └── NavBarActions.js

    ├── containers

    │ └── NavBar.jsx

    ├── constants

    │ └── NavBarConstants.js

    ├── components

    │ └── App.jsx

    └── reducers

    └── NavBarReducer.js

    这种组织方式用来做样例还可以,但如果你有成百上千个组件,开发就会很困难。每添加一项功能,你就必须在上千个文件中找到正确的文件。这非常讨厌,也容易让人感到疲劳。

    经过长期追踪我们的 Github 问题列表和很多不同尝试后,我们找到了一种更好的解决方案:

    那就是按照功能来组织代码!也就是,把和某个功能(比如导航条)相关的所有文件放到同一个文件夹里。

    下面是重新组织的 NavBar 代码:

    react-app-by-feature

    ├── css

    ├── containers

    │    └── NavBar

    ├── NavBar.jsx

    │        ├── actions.js

    │        ├── constants.js

    │        └── reducer.js

    └── components

    └── App.jsx

    开发这个应用,只需要进入一个目录,如果添加一个功能,也只需要创建一个目录。这种方式非常有利于文件的重命名、文件和替换,多人协作也不会导致任何冲突。

    当我第一次知道这种开发React应用的方式时,我在想,我为什么还要按照原来的思路做?这种开发方式太赞了!

    需要注意的是,这种组织方式并不意味着 redux actions 和 reducers 只能用在这个组件里,它们可以(也应该)用于其他组件中!

    当我按照这个思路开发的时候想到了两个问题:

    1、该如何处理样式?

    2、该如何处理数据获取?

    样式管理

    除了结构的考虑之外,由于CSS本身的两个特点,在基于组件的架构中使用 CSS 很难:全局命名和继承。

    唯一的类名

    假设在某个大型应用中存在这段 CSS代码:

    .header { /* … */ }

    .title {

    background-color: yellow;

    }

    很快你就会发现问题,title 太常用了。其他开发者也可能会写出以下代码:

    .footer { /* … */ }

    .title {

    border-color: blue;

    }

    这就产生了一个命名冲突,然后你的title样式就多了一个蓝色边框,让你不得不在上千个文件中找到这段代码。

    幸运的是,有大牛为我们找到了解决方案,那就是CSS Modules。CSS Modules 的关键之处在于:把组件的样式放在组件自己的文件夹里。

    react-app-with-css-modules

    ├── containers

    └── components

    └── Button

    ├── Button.jsx

    └── styles.css

    现在的CSS看起来没有任何区别,不过我们不再担心命名冲突了,可以使用非常常见的名字:

    .button {

    /* … */

    }

    然后就可以在组件中通过require或import引入这些CSS文件,并在 className 中使用了:

    /* Button.jsx */

    var styles = require('./styles.css');

    如果你现在去查看对应的 DOM 结构,你会看到

    !CSS Modules 帮你保证了类名的“唯一”。

    为每个组件重置属性

    在 CSS 中,特定的属性会在 DOM 节点上下继承。比如,如果父节点有 line-height,而子节点没有,子节点就会自动继承父节点的 line-height。

    在基于组件的架构中,这可不是我们想要的。比如下面的代码:

    .header {

    line-height: 1.5em;

    /* … */

    }

    .footer {

    line-height: 1;

    /* … */

    }

    假设我们在这两个组件中渲染一个 Button,这个 Button 在这两个组件里会有不同的外观!这一点不仅适用于 line-height,也适用于其他能继承的属性。

    在以前,我们可以用 Reset CSS、Normalize.css 和 sanitize.css 来重置样式表。但如果我们想将这一理念落实到每一个组件上呢?

    这就是PostCSS插件PostCSS Auto Reset的用途!它会对每个组件进行样式重置,将所有能继承的 CSS 属性设置成默认值,从而覆盖继承。

    数据拉取

    基于组件的架构面临的第二大问题就是数据拉取。对于大部分 action 来说,将 action 和组件放在一起很合理,但数据拉取是全局 action,不和某个组件相连!

    目前大多数开发者使用 Redux Thunk来处理Redux 中的数据拉取。标准的thunked action如下:

    这种在 action 中拉取数据的方法很棒,但存在两个问题:很难测试这些函数,以及在 action 中包含数据拉取看起来不太对。

    Redux 的一大好处就是 pure action creator 很容易测试。而在 action 中加入了 thunk 的数据拉取操作之后,你必须两次调用 action,模拟 dispatch 函数等。

    最近,redux-saga在 React 世界引起了广泛关注。redux-saga 利用了 ES6 的 Generator 函数,让异步代码看起来就像同步代码一样,而且让异步流非常容易测试。redux-saga 给人的感觉是一个单独处理所有异步事务的独立线程,不会干扰应用的其他方面!

    下面是 redux-saga 的一个例子:

    上面的代码读起来就像小说一样,避免了回调地狱,而且非常容易测试。为什么?这是因为 redux-saga 导出的这些“作用”(effect)无需完成即可进行测试。

    我们在文件顶部 import 的这些作用都是 handler,可以让我们轻松与 redux 交互:

    put() 派发一个action

    take() 暂停 saga 直到action发生

    select() 获取部分 redux 状态(有点像 mapStateToProps)

    call() 调用传入的第一个位置的函数,并将其他参数作为被调用函数的参数

    为什么这些作用有用?让我们看看下面的测试:

    只有 generator.next() 被调用时,generator 函数才会继续,直到遇到下一个 yield 关键字!另外,我们也把测试文件和组件放在了一起。

    用 redux-saga 来做胶水中间件

    我们的组件现在真正解耦了。它们不关心其他组件的样式或逻辑;它们只关心自己的事情(绝大多数情况下如此)。

    假设有一个 Clock 和一个 Timer 组件。当 Clock 中的按被按下时,我们想要启动 Timer;当 Timer 中的停止按钮被按下时,我们想要在 Clock 中显示时间。

    你也许会这么做:

    但这么做的话,你无法单独使用这两个组件,复用它们几乎不可能!

    不过,我们可以用 redux-saga 来做这两个解耦组件的“胶水中间件”。取决于应用类型,我们可以通过听取特定 action,来以不同方式作出反应,从而让组件真正变得可复用。

    先修改两个组件:

    注意两个组件现在只关心自己,只 import 了自己的 action !

    下面用一个 saga 来把这两个解耦的组件连接到一起:

    漂亮!

    总结

    容器和组件的区别

    按功能组织代码文件

    使用 CSS modules 和 PostCSS Auto Reset

    使用 redux-saga:

    获得可读并且可测试的异步流

    将解耦组件连接到一起

    如果喜欢本文,可以订阅沪江技术学院公众号。

    相关文章

      网友评论

        本文标题:翻译|如何规模化 React 应用

        本文链接:https://www.haomeiwen.com/subject/tybdbttx.html