私の最初のフルスタックのWebアプリケーション

この記事では、著者はなりReact.jsとRedux.jsフロントエンド技術を使用して、RailsのAPIに、あなたは、Rubyを呼び出すことで、完璧なフルスタックのWebアプリケーションを作成する方法を教えられます。

著者|  アン-LAURE Chadeyas

翻訳|半月板、Zebian |火ミン

出品 | CSDN(ID:CSDNnews)

以下は翻訳です。

チュートリアルと実践4つのプロジェクトの600以上のセクションを学んだ後、今私はようやく私の最後のプロジェクトについて話すことができます。フラットアイアン・スクールは、このオンラインソフトウェア開発自習コースに参加し、私は私が卒業するために、5つのプロジェクトを完了することは知っていました。このプロジェクトは、私がスキルを習得していない多くのことを必要とするため、最後の項目は、手の届かないところだったようです。しかし、今、私は最終的にこのプロジェクトを提出することができます。

このプロジェクトの最後の目標は、美しい1ページのアプリケーションを構築フロントエンドReact.jsとRedux.js、RailsのAPIにルビーを呼び出すことです。

単一ページのアプリケーション

まず、単一ページのアプリケーションは何ですか?単一ページのアプリケーションではなく、サーバーから新しいページをロードする、動的に現在のページの内容を書き換え、ユーザのアクションや行動に基づいて、WebサイトまたはWebアプリケーションです。達成するための2つの方法があります。

  • ページのロード中のすべてのページの内容をお読みください。しかし、アプリケーションの複雑さを考えると、これは、このようにユーザーエクスペリエンスに影響を与え、長い時間がかかることがあります。

  • ユーザイベントの後、適切なコンテンツサーバに要求。一般的なイベントは、ユーザーがキーボードのキーを、ホバー要素の上、ページダウンボタン、スクロールをクリックを押します。

複雑なアプリケーションのために、第二の方法は、より一般的です。すべての後に、単一ページのアプリケーションの存在理由は、それが完全なページのリロードによって中断されることはありませんスムーズなユーザーエクスペリエンスを提供できることです。

単一ページのアプリケーションの観点から、コードの観点からアプリケーション全体が一つだけのHTMLページ、通常のページの名前のindex.htmlであることを意味します。

アプリケーション構造の構築

前端と後端部の2つの部分にアプリケーション。遠位部、即ちユーザインターフェースのユーザ対話。サーバーとユーザーインターフェース間のバックエンド接続を担当。ビルドアプリケーションには、次の2つのオプションがあります。

  • 第一个选择就是把前端和后端都放在同一个代码仓库中(比如GitHub上的代码仓库)。

  • 第二个选择是建立两个代码仓库,一个用于后端,一个用于前端。这样做有几个好处,其中之一就是后端(比如本文中的API)可以被多个前端复用,另一个好处就是编辑器中管理的目录更小。

上述两种方法并没有对错之分。基于上面给出的两个理由。我在构建应用程序时选择了使用两个独立的代码仓库、

第一个代码仓库是后端的。我在终端中使用下述命令创建了一个Rails应用程序作为API,不过没有任何视图。这跟创建普通的Rails应用程序是一样的,只不过多了一个参数。

rails new my_app_backend --api

至于前端,我采用了create-react-app生成器。

npx create-react-app my_app_frontend

这两个命令可以帮我建好所需的一切文件。

关于组件的类型

该项目的技术要求是,至少需要写两个容器组件,以及5个无状态组件。React中的组件是界面的基本构成元素。它可以从父组件接受输入(通过props访问),还可以重用。

下面详细介绍一下容器组件和无状态组件。首先需要解释一下什么是状态(state)。状态就是可能会改变的数据。状态改变可能有多种原因,其中之一就是数据库更新导致状态变化,另一个原因就是用户修改了数据。

容器组件也称为有状态组件,而无状态组件也称为表现组件。容器组件和表现组件并没有严格的区分,每个开发者都可以按照自己的意愿来组织各个组件。但一般而言,容器组件是有状态的,可以通过其状态改变来跟踪,而表现组件没有状态,它可以显示传递过来的props,也可以永远显示固定的内容。

至于本文讨论的应用程序,我决定采用最基本的分割方法。我给API中的每个模型都建立了一个容器组件。随着项目的进行,我删掉了一些不再需要的组件,同时还添加了一些其他组件。有状态组件基本上都是表单。最好的例子就是注册表单和登录表单。在React的表单中,每次用户输入都会导致状态的变化,可能是局部状态变化,也可能是Redux存储状态变化(我们稍后讨论Redux)。无状态组件的例子就是 BicyclesList.js 中的自行车列表。这个组件通过 props 接受一个来自 CitiesContainer 组件的城市列表,它本身与状态没有任何关系。

React中的路由

由于单页应用中不会重新加载完整的页面,因此产生的问题之一就是路由如何进行。Web应用程序中路由的作用是,在用户访问特定网页时确定需要执行什么。我们的单页应用中只有一个视图,因此没办法像Rails应用程序那样在用户点击链接时跳转到另一个视图。

凡事都有解决的办法,对于这个问题,我们可以使用react-router库。它有许多功能,其中包括:

  • URL显示用户的当前位置,而不仅仅是显示根页面的URL

  • 用户可以使用浏览器的前进和后退按钮

  • 用户可以在地址栏中输入URL,跳转到指定页面

下面以 CitiesContainer 为例来介绍路由的工作方式:

App.jsimport React from 'react';
import { Route } from 'react-router-dom';
import CitiesContainer from './containers/CitiesContainer';class CitiesContainer extends React.Component {
  render() {
    return (
      <Route path='/cities' component={CitiesContainer} />
    )
  }
}export default App;

这里使用了 path 而不是 exact path,因此所有包含 /cities 的路径都会被处理。CitiesContainer 容器的内部如下:

containers/CitiesContainer.js import React from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router-dom';
import { fetchCities } from '../actions/fetchCities';
import CitiesList from '../components/CitiesList';
import CityPage from '../components/CityPage';
import BicyclesList from '../components/BicyclesList';class CitiesContainer extends React.Component {
  componentDidMount() {
    this.props.fetchCities()
  } 

  render() {
    return (
      <div>
        <Route exact path='/cities' render={() => <CitiesList 
         cities={this.props.cities} />} />
        <Route exact path='/cities/:id' render={(routerProps) => 
         <CityPage {...routerProps} cities={this.props.cities} 
         />}/>
        <Route path='/cities/:id/bicycles' render={(routerProps) => 
         <BicyclesList {...routerProps} cities={this.props.cities} 
         />}/>
      </div>
    )
  }
}const mapStateToProps = state => {
  return {
    cities: state.cities
  }
}export default connect(mapStateToProps, { fetchCities })(CitiesContainer)

React-router-dom 包为开发者提供了 routerProps。该属性可以将URL的内容作为参数传递给props。在这里,我们可以通过 props 访问城市的id,进而可以对 cities(通过props访问)进行过滤,找到我们需要的那个城市。

使用Redux 

终于讲到了Redux。Redux是什么呢?这个问题我也问过自己,也问过Google。我在Youtube上找到了这个视频(https://www.youtube.com/watch?v=np8A_aW7Pew),介绍得非常清楚。我希望对你也有帮助。Redux的文档告诉我们,Redux是一个供 JavaScript 应用程序使用的、“可预测的”状态容器。文档中强调了Redux的4个方面:

  • 可预测:它有助于编写在所有环境中行为都很一致的应用程序,更容易测试。

  • 中心化:应用程序的状态和逻辑中心化,可以实现强大的功能,如状态持久化等。

  • 可调式:Redux DevTools 可以非常方便地跟踪应用程序状态变化发生的时间、位置、原因以及方式等。

  • 灵活:Redux可以与任何UI层结合使用。

在我们的应用程序中,Redux有许多便利之处,但最重要的一点是你可以从任何地方访问当前用户的信息(如果存在当前登录用户的话)。我们将用户信息保存到Redux存储中,就可以从应用程序的任意位置访问,而不仅限于将当前用户通过props传递的那些组件。实际上,任何子组件都可以通过以下两种方式之一连接到Redux存储:

  • 使用mapsStateToProps(),无需从组件中访问Redux存储,就能将状态内容放到props中

  • 使用mapDispatchToProps(),无需从组件中访问Redux存储,就能分发actions。

这样就可以分离状态管理和状态显示。但这并非Redux的全部功能。我们在构建异步action creator的时候还是用了thunk中间件。

什么是中间件?维基百科的解释是“提供系统软件和应用软件之间连接的软件,以便于软件各部件之间的沟通”。你可以认为中间件就像胶水一样。因此 thunk 可以让我们做一些原本做不到的事情。thunk 函数的参数是 dispatch,因此可以在函数中使用,在本例中就是在action creator中使用。由于dispatch可以在函数内使用,我们可以利用这一点,仅在fetch请求结束时进行分发。如下例所示,我们仅在获取所有城市的fetch请求结束后进行分发:

actions/fetchCities.jsexport const fetchCities = () => {
  return (dispatch) => {
    fetch('http://localhost:3000/api/v1/cities')
    .then(response => response.json())
    .then(cities => {
      dispatch({
        type: 'FETCH_CITIES',
        payload: cities
      })
    })
  }
}


利用fetch实现数据持久化

我们可以通过fetch,用GET方式从服务器上获得数据,也可以用POST方式将数据发送到服务器。用GET获取数据的方式可以参照前面讨论路由的时候提到的 CitiesContainer 示例。我们在下面的action控制器中,利用fetch以GET方式获取所有城市:

actions/fetchCities.jsexport const fetchCities = () => {
  return (dispatch) => {
    fetch('http://localhost:3000/api/v1/cities')
    .then(response => response.json())
    .then(cities => {
      dispatch({
        type: 'FETCH_CITIES',
        payload: cities
      })
    })
  }
}

在认证部分,登录action creator是一个以POST方式向服务器发送数据的例子:

actions/auth.jsexport const login = credentials => {
  return (dispatch) => {
    fetch('http://localhost:3000/api/v1/login', {
      credentials: 'include',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(credentials)
    })
      .then(response => response.json())
      .then(user => {
        if (user.error) {
          alert(user.error)
        } else {
          dispatch(setCurrentUser(user))
          dispatch(resetLoginForm())
        }
      })
      .catch(console.log)
  }
}export const setCurrentUser = user => {
  return {
    type: 'SET_CURRENT_USER',
    payload: user
  }
}export const resetLoginForm = () => {
  return {
    type: 'RESET_LOGIN_FORM'
  }
}

至于样式,我最初的计划是使用React版的Bootstrap,因为这是唯一一个我听说过的库。在样式方面我还是新手,而且我没找到导航条,因此搜索了一下React中有什么可以使用的框架。然后在这篇文章(https://medium.com/@zeolearn/6-best-reactjs-based-ui-frameworks-9c780b96236c)中看到了Semantic UI React的Menu组件,这正是我所需要的东西。我从来没有用过其他样式框架,因此没办法比较Semantic UI和其他组件哪个更容易使用,但我必须要说,Semantic UI非常容易上手。 

样式并不是这个项目的重点,但我计划在这个应用程序中实现这个样式。我已经建立了基础结构和基本的功能,现在添加新功能已经比较容易了。我计划继续改进应用程序的功能。

原文:https://medium.com/@annelaure.developer/my-first-full-stack-web-application-8ac82db61b10

本文为 CSDN 翻译,转载请注明来源出处。

【End】

热 文 推 荐 

AI 没让人类失业,搞 AI 的人先失业了

2020 年,Android 还有哪些新期待?

☞Java 14 有哪些新特性?

铁打的春晚,流水的互联网公司

达摩院 2020 预测:模块化降低芯片设计门槛 | 问底中国 IT 技术演进

千万不要和程序员一起合租!

在调查过基于模型的强化学习方法后,我们得到这些结论

漫话:如何给女朋友解释为什么一到年底,部分网站就会出现日期混乱的现象?

你点的每个“在看”,我都认真当成了喜欢

发布了1643 篇原创文章 · 获赞 4万+ · 访问量 1338万+

おすすめ

転載: blog.csdn.net/csdnnews/article/details/103982292