<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ flux - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ flux - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 26 Sep 2026 20:57:40 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/flux-2/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 Flux 来管理 ReactJS 的状态 ]]>
                </title>
                <description>
                    <![CDATA[ 如果你最近开始使用ReactJS，那么你可能想知道如何在React中管理状态以便扩展应用程序。 许多公司就这个问题给出了自己的解决方案，而ReactJS的创始团队Facebook给出的解决方案是Flux [https://facebook.github.io/flux/]。 如果你曾经研究过AngularJS或者EmberJS，你可能听说过Redux。ReactJS也有一个实现Redux的库。 但是在学习Redux之前，我建议你先学习并理解Flux。我这样说是因为Redux是Flux的更高级版本。再清楚了Flux的概念之后，你可以学习redux并将其集成到你的应用程序中。 什么是Flux Flux采用单向数据流模式来解决状态管理的复杂性。记住它不是一个框架，而更像是一个解决状态管理问题的模式。 你是否会好奇现有的MVC框架出了什么问题？假设你的客户端应用扩展之后，各种模型（model）和视图（view）之间的交互错综复杂，是不是会变得一团糟？ 备注：图片取自Facebook F8 Flux 大会组件之间的关系变得复杂，导致扩展应用变得麻烦。Facebook曾面临同样的问题， ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-use-flux-in-react-example/</link>
                <guid isPermaLink="false">63089b29aeb66407f67d0fb1</guid>
                
                    <category>
                        <![CDATA[ flux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ReactJS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ PapayaHUANG ]]>
                </dc:creator>
                <pubDate>Thu, 25 Aug 2022 08:06:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/08/Flux-4.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-use-flux-in-react-example/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use Flux to Manage State in ReactJS - Explained with an Example</a>
      </p><!--kg-card-begin: markdown--><p>如果你最近开始使用ReactJS，那么你可能想知道如何在React中管理状态以便扩展应用程序。</p>
<p>许多公司就这个问题给出了自己的解决方案，而ReactJS的创始团队Facebook给出的解决方案是<a href="https://facebook.github.io/flux/"><strong>Flux</strong></a>。</p>
<p>如果你曾经研究过<strong>AngularJS</strong>或者<strong>EmberJS</strong>，你可能听说过<strong>Redux</strong>。ReactJS也有一个实现Redux的库。</p>
<p>但是在学习Redux之前，我建议你先学习并理解Flux。我这样说是因为Redux是Flux的更高级版本。再清楚了Flux的概念之后，你可以学习redux并将其集成到你的应用程序中。</p>
<h2 id="flux">什么是Flux</h2>
<p>Flux采用<strong>单向数据流模式</strong>来解决状态管理的复杂性。记住它不是一个框架，而更像是一个解决状态管理问题的模式。</p>
<p>你是否会好奇现有的MVC框架出了什么问题？假设你的客户端应用扩展之后，各种模型（model）和视图（view）之间的交互错综复杂，是不是会变得一团糟？</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2020/04/Screenshot-2020-04-16-at-6.38.14-PM.png" alt="备注：图片取自Facebook F8 Flux 大会" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>备注：图片取自Facebook F8 Flux 大会</figcaption>
</figure>
<p>组件之间的关系变得复杂，导致扩展应用变得麻烦。Facebook曾面临同样的问题，所以他们搭建了<strong>单向数据流</strong>来解决这个问题。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2020/04/Flux-3.png" alt="备注：图片取自Facebook的Flux文档" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>备注：图片取自Facebook的Flux文档</figcaption>
</figure>
<p>如图所示，Flux包含许多组件，让我们逐一讲解这些组件。</p>
<p><strong>视图（View）:</strong> 这个组件渲染UI。每当视图层上发生任何用户交互（如事件），都会触发渲染。同样，当数据层（store）通知视图层有变化的时候，视图重新渲染。例如，用户点击了<strong>添加</strong>按钮。</p>
<p><strong>动作（Action）:</strong> 这部分处理所有的事件。事件由view组件传递。这一层通常被用于进行API调用。 一旦处理完毕，就使用派发器（dispatcher）派发任务。动作（action）可以是添加一个帖子、删除一个帖子等用户交互。</p>
<p>派发事件的通用负载（payload）结构为：</p>
<pre><code class="language-js">{
	actionType: "",
    data: {
        title: "Understanding Flux step by step",
        author: "Sharvin"
    }
}
</code></pre>
<p><code>actionType</code>是强制键，dispatcher通过它传递更新到相应的store。通常使用常量来保存<code>actionType</code>的值，这样不会出现拼写错误。<code>data</code>包含了我们想从Action派发到Store的信息。这个键的名称可以为任意值。</p>
<p><strong>派发器（Dispatcher）:</strong> 这里是中央枢纽和单例注册表。dispatcher将负载由Action派发到Store，同时也要确保在派发的过程中没有产生<a href="https://baike.baidu.com/item/%E7%BA%A7%E8%81%94%E6%95%88%E5%BA%94/15623566">级联效应</a>。 它确保数据层在完成处理和存储操作之前没有任何其他操作。</p>
<p>可以把这个组件想象成一个系统的交通控制器，它将回调集中到一个清单，调用回调，并且广播由action传递过来的负载。</p>
<p>由于这个组件的存在，数据流变得可以预测。每一个action都由注册在dispatcher的回调更新到对应的store。</p>
<p><strong>数据（Store）:</strong> 这里包含了应用的状态，是这个模式中的数据层。不要把它类比为MVC中的模型（model）。一个应用可以有一个或者多个store。store通过注册在dispatcher的回调来更新数据。</p>
<p>Node的EventEmitter（事件发射器）被用来更新store并广播更新到view。view从不直接更新应用程序的状态，它被更新是因为store的变化。</p>
<p>这是Flux模型中唯一可以更新数据的组件。store内的接口包括：</p>
<ol>
<li>**EventEmitter(事件发射器)**通知view，store的数据更新了。</li>
<li><strong>addChangeListener</strong>和<strong>removeChangeListener</strong>这类监听器被添加。</li>
<li><strong>emitChange</strong>用于发射更改。</li>
</ol>
<p>假设上述的范式有多个store和view，这个模式和数据流还是保持不变。因为和MVC或者双向绑定不同的是，flux模式是单向模式，数据流是可以预见的。 这就提高了<strong>数据的一致性</strong>并且<strong>更容易发现bug</strong>。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2020/04/Flux-Flow-3.png" alt="Flux数据流" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Flux数据流</figcaption>
</figure>
<p>因为<strong>单向数据流</strong>的特性，Flux有以下优点：</p>
<ol>
<li>代码更加简洁且便于理解。</li>
<li>更容易使用单元测试进行测试。</li>
<li>应用可以被扩展。</li>
<li>可预见的数据流。</li>
</ol>
<blockquote>
<p><em><strong>注意:</strong> Flux唯一的缺点是我们需要编写一些样板。除去样板，在往应用添加新的组件的时候，我们只需要编写一点点代码。</em></p>
</blockquote>
<h2 id="">应用模板</h2>
<p>我们将通过学习创建一个博客页面来学习如何在ReactJS中实现flux。我们将在页面中展现所有文章。应用模板参见这个<a href="https://github.com/Sharvin26/DummyBlog/tree/0d56987b2d461b794e7841302c9337eda1ad0725">commit</a>。我们将在这个模板的基础上结合Flux。</p>
<p>复制commit的代码：</p>
<pre><code class="language-shell">git clone  https://github.com/Sharvin26/DummyBlog.git
</code></pre>
<pre><code class="language-shell">git checkout 0d56987b2d461b794e7841302c9337eda1ad0725
</code></pre>
<p>我们需要引入<strong>react-router-dom</strong>和<strong>bootstrap</strong>模型。使用以下命令安装包：</p>
<pre><code>npm install react-router-dom@5.0.0 bootstrap@4.3.1  
</code></pre>
<p>完成后，你会看到以下界面：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/captured.gif" alt="captured" width="600" height="400" loading="lazy"></p>
<p>虚拟博客</p>
<p>我们仅通过实现<strong>GET</strong>方法来了解Flux的细节。完成后你会发现<strong>POST</strong>、<strong>EDIT</strong>和<strong>DELETE</strong>的实现过程相同。</p>
<p>目录结构如下：</p>
<pre><code class="language-shell">+-- README.md 
+-- package-lock.json
+-- package.json
+-- node_modules
+-- .gitignore
+-- public
|   +-- index.html
+-- src
|   +-- +-- components
|   +-- +-- +-- common
|   +-- +-- +-- +-- NavBar.js
|   +-- +-- +-- PostLists.js
|	+-- +-- pages
|   +-- +-- +-- Home.js
|   +-- +-- +-- NotFound.js
|   +-- +-- +-- Posts.js
|   +-- index.js
|   +-- App.js
|   +-- db.json
</code></pre>
<blockquote>
<p><strong>Note:</strong> 我们添加了<code>db.json</code>文件，这是一个虚拟的数据文件。因为我们专注在Flux的讲解而不是如何搭建API，所以我们将从这个文件中获取数据。</p>
</blockquote>
<p>我们的应用的基础组件是<code>index.js</code>。在这个文件中我们渲染了<code>App.js</code>，它位于public文件夹内的<code>index.html</code>内部，采用了<strong>render</strong>和 <strong>getElementById</strong>方法渲染 <code>App.js</code>用来配置路由。</p>
<p>同时，我们还添加了<strong>NavBar</strong>组件，让所有组件都可以访问到这个组件。</p>
<p>在<strong>pages</strong>目录中有三个文件=&gt; <code>Home.js</code>、 <code>Posts.js</code>和<code>NotFound.js</code>。 <code>Home.js</code>用于展示<code>Home</code>组件，如果用户登陆到一个不存在的路由，会渲染<code>NotFound.js</code>。</p>
<p><code>Posts.js</code>是一个父组件，从<code>db.json</code>文件获取数据，并将数据传递给<code>PostLists.js</code>，它位于<strong>components</strong>目录下。它是一个虚拟组件，仅用于渲染UI。从父组件(<code>Posts.js</code>)由props的形式获取数据，并以卡片的形式展示数据。</p>
<p>现在我们已经知道了应用的结构，让我们在这个基础上集成Flux。</p>
<h2 id="flux">集成Flux</h2>
<p>使用以下命令行安装Flux：</p>
<pre><code class="language-shell">npm install flux@3.1.3
</code></pre>
<p>为了集成Flux，需要把我们的应用分成四个小部分：</p>
<ol>
<li>派发器（Dispatcher）</li>
<li>动作（Actions）</li>
<li>数据（Stores）</li>
<li>视图（View）</li>
</ol>
<p>注意：完整的代码在这个<a href="https://github.com/Sharvin26/DummyBlog">仓库</a>。</p>
<h3 id="dispatcher">派发器（Dispatcher）</h3>
<p>首先，我们在<strong>src</strong>目录下创建两个新的文件夹<strong>actions</strong>和<strong>stores</strong>。然后在同一个目录下创建<code>appDispatcher.js</code>。</p>
<p><strong>注意：<strong>因为不是ReactJS的组件，现在开始所有和Flux相关的文件命名都为</strong>驼峰式</strong>。</p>
<p>进入<code>appDispatcher.js</code>，并复制以下代码：</p>
<pre><code class="language-js">import { Dispatcher } from "flux";
const dispatcher = new Dispatcher();
export default dispatcher;
</code></pre>
<p>这里我们导入了flux库里的Dispatcher，创建了一个新的对象，并且导出这个对象，供action模块使用。</p>
<h3 id="actions">行为（Actions）</h3>
<p>进入<strong>actions</strong>目录，创建<code>actionTypes.js</code>和<code>postActions.js</code>。 在<code>actionTypes.js</code>中，我们将定义被<code>postActions.js</code>和store模块引用的常量。</p>
<p>定义常量的原因是我们不想出现任何拼写错误，并不是强制要求这样做，但是这是一个推荐的办法。</p>
<pre><code class="language-js">// actionTypes.js

export default {
    GET_POSTS: "GET_POSTS",
};

</code></pre>
<p>在<code>postActions.js</code>内，我们调用<code>db.json</code>数据，并使用dispatcher来派发。</p>
<pre><code class="language-js">//postActions.js

import dispatcher from "../appDispatcher";
import actionTypes from "./actionTypes";
import data from "../db.json";

export function getPosts() {
    dispatcher.dispatch({
        actionTypes: actionTypes.GET_POSTS,
        posts: data["posts"],
    });
}
</code></pre>
<p>在上述代码中，我们引入了dispatcher对象，actionTypes常量和data。我们使用dispatch方法将数据发送到store。在我们的例子中的数据将以以下格式被发送：</p>
<pre><code class="language-json">{
	actionTypes: "GET_POSTS",
    posts: [
        {
            "id": 1,
            "title": "Hello World",
            "author": "Sharvin Shah",
            "body": "Example of blog application"
        },
        {
            "id": 2,
            "title": "Hello Again",
            "author": "John Doe",
            "body": "Testing another component"
        }
    ]
}
</code></pre>
<h3 id="stores">数据（Stores）</h3>
<p>现在我们需要创建作为<strong>数组层</strong>来存储文章的store。它内部包含一个<strong>事件监听器</strong>来告诉view发生了变化，并且会使用dispatcher的<strong>register</strong>获取action的数据。</p>
<p>导航到store目录，创建一个文件名为<code>postStore.js</code>的文件。首先我们导入Events包的<strong>EventEmitter</strong>，这个是NodeJS默认的方法，同时我们还将导入dispatcher对象和actionTypes常量文件。</p>
<pre><code class="language-js">import { EventEmitter } from "events";
import dispatcher from "../appDispatcher";
import actionTypes from "../actions/actionTypes";
</code></pre>
<p>我们将为<strong>change</strong>事件创建一个常量，还要创建一个变量来存储dispatcher传递过来的文章。</p>
<pre><code class="language-js">const CHANGE_EVENT = "change";
let _posts = [];
</code></pre>
<p>现在我们编写一个由<strong>EventEmitter</strong>扩展而来的类，并在这个类中声明以下方法：</p>
<p><code>addChangeListener</code>: 使用NodeJS的<strong>EventEmitter.on</strong>方法，添加一个change监听器接受回调函数作为参数。</p>
<p><code>removeChangeListener</code>: 使用NodeJS的 <strong>EventEmitter.removeListener</strong>，当你再不需要监听一个事件，就可以使用这个方法。</p>
<p><code>emitChange</code>: 使用NodeJS的<strong>EventEmitter.emit</strong>，每当变化发生，就发出这个变化。</p>
<p>这个类还将有一个方法叫做<code>getPosts</code>，会返回我们在类上面声明的<code>_posts</code>变量。</p>
<p>代码如下：</p>
<pre><code class="language-js">class PostStore extends EventEmitter {
    addChangeListener(callback) {
        this.on(CHANGE_EVENT, callback);
    }

    removeChangeListener(callback) {
        this.removeListener(CHANGE_EVENT, callback);
    }

    emitChange() {
        this.emit(CHANGE_EVENT);
    }

    getPosts() {
        return _posts;
    }
}
</code></pre>
<p>现在我们为PostStore类创建<code>store</code>对象，并导出这个对象供view使用：</p>
<pre><code class="language-js">const store = new PostStore();
</code></pre>
<p>接下来，我们使用dispatcher的<strong>register</strong>方法来接受Actions组件的负载。</p>
<p>我们使用<code>actionTypes</code>值来判断是什么action，以及需要处理的对应的数据。代码如下：</p>
<pre><code class="language-js">dispatcher.register((action) =&gt; {
    switch (action.actionTypes) {
        case actionTypes.GET_POSTS:
            _posts = action.posts;
            store.emitChange();
            break;
        default:
    }
});
</code></pre>
<p>导出这个对象以便其他的模块使用：</p>
<pre><code class="language-js">export default store;
</code></pre>
<h3 id="view">视图（View）</h3>
<p>现在我们更新view，一旦文章页面加载，并且从postStore接受到负载就将事件发送到<code>postActions</code>。进入<strong>pages</strong>目录的<code>Posts.js</code>，你会看到<strong>useEffect</strong>方法内部的代码如下：</p>
<pre><code class="language-js">useEffect(() =&gt; {
	setposts(data["posts"]);
}, []);
</code></pre>
<p>我们将改变useEffect读取和更新代码的方法，首先我们将使用来自postStore类的<code>addChangeListener</code>方法，并传入一个<code>onChange</code>回调。 将<code>posts</code>的状态设置为<code>postStore.js</code>文件中的<code>getPosts</code>方法的返回值。</p>
<p>一开始store会返回空数组，因为没有可用数据。我们调用<code>postActions.js</code>内的<code>_getPosts_</code>方法。这个方法会读取和传递数据。然后store发送数据，<code>addChangeListener</code>监听到变化，并使用<code>onChange</code>回调来更新<code>posts</code>的值。</p>
<p>这听起来有点让人困惑，没关系下面的流程图会让一切变得清晰。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/FluxBlogFlow-1.png" alt="FluxBlogFlow-1" width="600" height="400" loading="lazy"></p>
<p>更新<code>Posts.js</code>内的代码：</p>
<pre><code class="language-js">import React, { useState, useEffect } from "react";
import PostLists from "../components/PostLists";
import postStore from "../stores/postStore";
import { getPosts } from "../actions/postActions";

function PostPage() {
    const [posts, setPosts] = useState(postStore.getPosts());

    useEffect(() =&gt; {
        postStore.addChangeListener(onChange);
        if (postStore.getPosts().length === 0) getPosts();
        return () =&gt; postStore.removeChangeListener(onChange);
    }, []);

    function onChange() {
        setPosts(postStore.getPosts());
    }

    return (
        &lt;div&gt;
            &lt;PostLists posts={posts} /&gt;
        &lt;/div&gt;
    );
}

export default PostPage;
</code></pre>
<p>在这段代码中我们删除了原有的导入，并且在回调函数中使用<code>setPosts</code>取代了使用useEffect方法获取数据。<code>return () =&gt; postStore.removeChangeListener(onChange);</code>用来在离开页面时删除监听器。</p>
<p>然后我们的博客页面就可以完全运作了。和之前的唯一区别是，之前我们使用<strong>useEffect</strong>获取数据，而现在我们是用action来读取数据，用store来存储数据，以及将数据传输到需要的组件中。</p>
<p>使用实际API时，你会发现应用程序从API加载一次数据并将其存储在store中。当我们重新访问同一页面时，你会发现不需要再次调用API。你可以在 Chrome开发者控制台的源选项卡进行监控。</p>
<p>介绍完毕。希望这篇教程可以帮助你对Flux有一个清晰的认识，以及日后你可以将其应用到你的项目中。</p>
<blockquote>
<p>欢迎通过<a href="https://twitter.com/sharvinshah26">Twitter</a>和<a href="https://github.com/Sharvin26">GitHub</a>联系我。</p>
</blockquote>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
