• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

使用Laravel后端构建React应用程序:React

这是使用 Laravel 后端构建 react 应用程序系列的第二部分,也是最后一部分。在本系列的第一部分中,我们使用 Laravel 创建了一个 restful api,用于一个基本的产品列表应用程序。在本教程中,我们将使用 React 开发前端。

我们还将考虑所有可用的选项来弥合 Laravel 和 React 之间的差距。您无需阅读本系列的第一部分即可理解本教程。如果你是来看看 React 和 Laravel 是如何一起运行的,那么事实上,你可以避开第一部分。您应该前往GitHub,克隆存储库,然后按照下面的快速回顾开始。

快速回顾

在上一个教程中,我们开发了一个响应 API 调用的 Laravel 应用程序。我们为简单的产品列表应用程序创建了路由、控制器和模型。由于控制器的工作是返回对 HTTP 请求的响应,因此完全跳过了视图部分。

然后我们讨论了使用 Laravel 进行异常处理和验证的技术。在教程结束时,我们有了一个 Laravel 后端 API。我们现在可以使用此 API 为 Web 和各种移动设备构建应用程序。

在本教程中,我们将把重点转移到前端。教程的前半部分是关于在 Laravel 环境中设置 React。我还将向您介绍 Laravel Mix(由 Laravel 5.4 及更高版本支持),这是一个用于编译资产的 API。在教程的后半部分,我们将从头开始构建一个 React 应用程序。

在 Laravel 中设置 React

Laravel Mix 是在 Laravel 5.4 中引入的,它是目前连接 React 和 Laravel 的理想方式。使用 Laravel 7,整个过程变得更加容易。我在下面描述了这两种方法。

使用 React Preset 命令(Laravel 5.5 和 Laravel 7+)

Laravel 5.5 带有一个功能,可以让你使用 artisan 的preset react命令为 React 组件构建代码。在以前的 Laravel 版本中,在 Laravel 中设置 React 并不容易。如果您正在运行最新版本的 Laravel,请运行以下命令将 React 预设添加到您的项目中。

php artisan preset react

Laravel 默认附带 vue 预设,上面的命令将所有 Vue 实例替换为 React。有趣的是,如果您不需要预设,您可以使用php artisan preset none命令将它们完全删除。

如果一切顺利,这应该会显示在您的终端中。

React scaffolding installed successfully.

Please run "npm install && npm run dev" to compile your fresh scaffolding.

对于 Laravel 7 及更高版本,您也可以安装laravel/ui Composer 包并使用它来创建 React 脚手架:

composer require laravel/ui

php artisan ui react

// Generate login & registration scaffolding...

php artisan ui react --auth

最后一个命令将为登录和注册组件生成一个脚手架,用于用户身份验证。

在后台,Laravel 使用 Laravel Mix,它是 webpack 的平滑包装器。您可能已经知道,webpack 是一个模块捆绑器。它解决了所有模块依赖关系并为 javascriptcss 生成必要的静态资产。React 需要一个模块打包器才能工作,而 webpack 非常适合这个角色。所以 Laravel Mix 是位于 webpack 之上的层,它使得在 Laravel 中使用 webpack 变得更加容易。

如果您以后需要自定义 webpack 配置,那么更好地了解 Laravel Mix 的工作原理非常重要。React 预设命令没有给我们关于后台如何工作的信息。因此,让我们删除 React 预设并手动回溯这些步骤。

手动方法(Laravel 5.4)

如果您正在运行 Laravel 5.4,或者您只是想了解 Laravel Mix 的配置方式,那么您需要遵循以下步骤:

安装react、react-dom和babel-preset-react使用 npm。也安装 Yarn 可能是个好主意。Laravel 和 React 更喜欢 Yarn 而不是 npm,这已经不是什么秘密了。

打开webpack.mix.js,位于 Laravel 项目的根目录中。这是您声明资产应如何编译的配置文件。mix.js('resources/assets/js/app.js', 'public/js');用替换该行mix.react('resources/assets/js/app.js', 'public/js');。app.js是我们的 JavaScript 文件的入口点,编译后的文件将位于public/js中。在终端中运行npm install以安装所有依赖项。

接下来,转到resources/assets/js。已经有一个 components 文件夹和几个其他 javaScript 文件。React 组件将进入 components 目录。删除现有的Example.vue文件并为示例 React 组件创建一个新文件。

资源/资产/js/component/Main.js

import React, { Component } from 'react';

import ReactDOM from 'react-dom'; 

/* An example React component */

class Main extends Component {

    render() {

        return (

            <div>

                <h3>All Products</h3>

            </div>

        );

    }

}

export default Main;

/* The if statement is required so as to Render the component on pages that have a div with an ID of "root"; 

*/

if (document.getElementById('root')) {

    ReactDOM.render(<Main />, document.getElementById('root'));

}

更新app.js以删除所有与 Vue 相关的代码并导入 React 组件。

资源/资产/js/app.js

require('./bootstrap');

/* Import the Main component */

import Main from './components/Main';

现在,我们只需要让视图可以访问资产。视图文件位于资源/视图目录中。让我们为welcome.blade.php添加一个<script>标签,这是您导航到 时呈现的默认页面。删除视图文件的内容并用下面的代码替换它:localhost:8000/

资源/视图/welcome.blade.php

<!doctype html><html lang="{{ app()->getLocale() }}">   

 <head>        

<meta charset="utf-8">        

<meta http-equiv="X-UA-Compatible" content="IE=edge">        

<meta name="viewport" content="width=device-width, initial-scale=1">        

<title>Laravel React application</title>       

 <link href="{{mix('css/app.css')}}" rel="stylesheet" type="text/css">    </head>    

<body>   

 <h2 style="text-align: center"> Laravel and React application </h2>        

<div id="root"></div>        

<script src="{{mix('js/app.js')}}" ></script>    

</body></html>

最后,执行npm run dev或yarn run dev编译资产。如果您访问localhost:8000,您应该会看到:

使用Laravel后端构建React应用程序:React  第1张

package.json有一个监视脚本,可在检测到任何更改时自动编译资产。要启用此模式,请运行npm run watch.

恭喜——你已成功配置 React 以使用 Laravel。现在,让我们为前端创建一些 React 组件。

开发 React 应用程序

如果您是 React 新手,您会发现本教程的其余部分有些挑战性。我建议参加React Crash Course for Beginners系列,以更好地熟悉 React 概念。让我们开始吧!

React 应用程序是围绕组件构建的。组件是 React 中最重要的结构,我们有一个专门用于组件的目录。

组件允许您将 UI 拆分为独立的、可重用的部分,并单独考虑每个部分。从概念上讲,组件就像 JavaScript 函数。它们接受任意输入(称为“道具”)并返回描述应该出现在屏幕上的 React 元素。—官方 React 文档

对于我们正在构建的应用程序,我们将从显示服务器返回的所有产品的基本组件开始。我们将其命名为 Main 组件。组件最初应该处理以下事情:

  • 从 API (GET /api/products ) 获取所有产品。

  • 将产品数据存储在其状态中。

  • 显示产品数据。

React 不是一个成熟的框架,因此该库本身没有任何 ajax 功能。我将使用fetch(),这是一个用于从服务器获取数据的标准 JavaScript API。但是有很多替代方法可以对服务器进行 AJAX 调用,比如Axios

我们将使用新的 React API 来创建我们的应用程序。这包括两个钩子:useState和useEffect,它们分别是在 React 中更新组件状态和启动生命周期操作的现代方法。

但是因为钩子是在 React 16.8 中引入的,所以我们首先需要将项目中的react和react-dom库都更新到最新版本。 

为此,请转到项目根文件夹中的package.jsondependencies文件,然后在该字段中,将现有的react和react-dom字段替换为以下内容:

"react": "^18.1.0",

"react-dom": "^18.1.0",

接下来,运行npm update将两个库升级到最新版本。现在我们拥有了 React 的所有最新特性,我们将修改我们的第一个组件。

资源/资产/js/component/Main.js

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

const Main = () => {

    // Sets initial state for products to empty array

    const [products, setProducts] = useState([]);   

    // Call this function to get products data

    const getProducts = () => {

        /* fetch API in action */

        fetch('/api/products')

        .then(response => {

            return response.json();

        })

        .then(products => {

            //Fetched product is stored in the state

            setProducts(products);

        });

    };

    /*useEffect is a lifecycle hook

   * that gets called after the component is rendered

   */

    useEffect(() => {

        getProducts();

      }, []);   

    // Render the products

    const renderProducts = () => {

        return products.map(product => {

            return (

                /* When using list you need to specify a key

                 * attribute that is unique for each list item

                */

                <li key={product.id} >

                    { product.title }

                </li>     

            );

        })

    };

    return(

        <div>

              <ul>

                { renderProducts() }

              </ul>

        </div>

    )

}

export default Main

在这里,我们在products开始时将状态初始化为一个空数组。一旦组件挂载,useEffect就会执行。在其中,我们使用fetch()从/ api /products中检索产品并将其存储在状态中。然后我们定义renderProducts描述组件 UI 的方法。所有产品都在那里呈现为列表。

最后,我们在 return 语句中渲染到页面。

使用Laravel后端构建React应用程序:React  第2张

该页面仅列出产品标题,这很无聊。此外,我们还没有任何交互元素。让我们让产品标题可点击,点击后,将呈现有关产品的更多详细信息。

显示产品数据

以下是我们需要涵盖的内容列表:

  • 跟踪单击的产品的状态。currentProduct让我们用一个初始null值来调用它。

  • 单击产品标题时,currentProduct会更新。

  • 相关产品的产品详细信息显示在右侧。在选择产品之前,它会显示“未选择产品”消息。

资源/资产/js/component/Main.js

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

const Main = () => {

    const [products, setProducts] = useState([]);

    const [currentProduct, setCurrentProduct] = useState(null);   

    // getProducts function goes here

    useEffect(() => {

        getProducts();

     });   

    // Render the products

    const renderProducts = () => {

        return products.map(product => {

            return (    

                // handleClick() function is invoked onClick.          

                <li

                    key={product.id}

                    onClick={() => handleClick(product)}

                >

                    { product.title }

                </li>     

            );

        })

    };

    // Executes when user clicks list item, sets the state

    const handleClick = (product) => {

        setCurrentProduct(product)

    };

    return(

        <div>

              <ul>

                { renderProducts() }

              </ul>

        </div>

    )

}

export default Main

在这里,我们添加createProduct了状态并使用 value 对其进行了初始化null。单击列表项时,该行将onClick={ () =>handleClick(product) }调用该函数。handleClick()该handleClick()方法更新 的状态currentProduct。

现在要显示产品数据,我们可以在 Main 组件中渲染它或创建一个新组件。如前所述,将 UI 拆分为更小的组件是 React 的做事方式。因此,我们将创建一个新组件并将其命名为 Product。

Product 组件嵌套在Main组件内部。组件将Main其状态作为道具传递。Product 组件接受这个道具作为输入并呈现相关信息。

资源/资产/js/component/Main.js

return (

/* The extra divs are for the css styles */

    <div>

        <div>

         <h3> All products </h3>

          <ul>

            { renderProducts() }

          </ul>

        </div>

        <Product product={currentProduct} />

    </div>

);

资源/资产/js/component/Product.js

import React, { Component } from 'react';

/* Stateless component or pure component

 * { product } syntax is the object destructing

*/

const Product = ({product}) => {

  const divStyle = {

      /*code omitted for brevity */

  }

  //if the props product is null, return Product doesn't exist

  if(!product) {

    return(<div style={divStyle}>  Product Doesnt exist </div>);

  }

  //Else, display the product data

  return( 

    <div style={divStyle}>

      <h2> {product.title} </h2>

      <p> {product.description} </p>

      <h3> Status {product.availability ? 'Available' : 'Out of stock'} </h3>

      <h3> Price : {product.price} </h3>

    </div>

  )

}

export default Product ;

应用程序现在应该看起来像这样:

使用Laravel后端构建React应用程序:React  第3张

添加新产品

我们已经成功实现了检索所有产品并显示它们的前端。接下来,我们需要一个表单来将新产品添加到产品列表中。添加产品的过程可能感觉比从 API 获取数据要复杂一些。

以下是我认为开发此功能所需的内容:

  • 为输入表单呈现 UI 的新有状态组件。组件的状态保存表单数据。

  • 提交时,子组件使用回调将状态传递给主组件。

  • Main 组件有一个方法,比如handleNewProduct(),它处理启动 post 请求的逻辑。收到响应后,主组件更新其状态(products和currentProduct)。

这听起来不是很复杂,不是吗?让我们一步一步来。首先,创建一个新组件。我要打电话给它AddProduct。

资源/资产/js/component/AddProduct.js

import React, { useState } from 'react'

const AddProduct = (props) => {

    const [newProduct, setNewProduct] =

        useState(

            {

                title:"",

                description: "",

                price: 0,

                availability: 0

            }

        );

    const handleInput = (key, e) => {

        /*Duplicating and updating the state */

        var newState = Object.assign({}, newProduct);

        newState[key] = e.target.value;

        setNewProduct(newState);

    };

    const handleSubmit = (e) => {

        //preventDefault prevents page reload  

        e.preventDefault();

        /*A call back to the onAdd props. The current

        *state is passed as a param

        */

        props.onAdd(newProduct);

    };

    const divStyle = {

        /*Code omitted for brevity */

    }           

    return(

        <div>

            <h2> Add new product </h2>

            <div style={divStyle}>

                /*when Submit button is pressed, the control is passed to

                *handleSubmit method

                */

                <form onSubmit={handleSubmit}>

                    <label> Title:

                    { /*On every keystroke, the handeInput method is invoked */ }

                        <input type="text" onChange={(e)=>handleInput('title',e)} />

                    </label>

                    <label> Description:

                        <input type="text" onChange={(e)=>handleInput('description',e)} />

                    </label>

                    { /* Input fields for Price and availability omitted for brevity */}

                    <input type="submit" value="Submit" />

                </form>

            </div>

        </div>

    )

}

export default AddProduct

该组件基本上呈现一个输入表单,所有输入值都存储在状态(newProduct)中。然后,在表单提交时,handleSubmit()方法被调用。但是AddProduct需要将信息传回给父级,我们使用回调来做到这一点。

作为父组件的Main组件将函数引用作为道具传递。在我们的例子中,子组件AddProduct调用这个 props 来通知父组件状态变化。因此,该行props.onAdd(newProduct);是通知父组件新产品的回调示例。

现在,在Main组件内部,我们将声明<AddProduct />如下:

<AddProduct onAdd={handleAddProduct} />

事件onAdd处理程序链接到组件的handleAddProduct()方法。此方法托管用于向服务器发出 POST 请求的代码。如果响应表明产品已成功创建,则更新products和的状态。currentProducts

handleAddProduct(product) {

   product.price = Number(product.price);

   /*Fetch API for post request */

   fetch( 'api/products/', {

       method:'post',

       /* headers are important*/

       headers: {

         'Accept': 'application/json',

         'Content-Type': 'application/json'

       },

       body: JSON.stringify(product)

   })

   .then(response => {

       return response.json();

   })

   .then( data => {

       //update the state of products and currentProduct           

       setProducts(prevProducts => prevProducts.concat(data))

       setCurrentProduct(data)

   })

 }

这是应用程序的最终版本:

使用Laravel后端构建React应用程序:React  第4张

接下来是什么?

没有删除和更新功能,应用程序是不完整的。但是,如果您一直密切关注本教程,您应该能够轻松填补空白。为了帮助您入门,我为您提供了删除和更新场景的事件处理程序逻辑。

删除产品的逻辑

handleDelete() {

  const delProduct = currentProduct

  fetch( 'api/products/' + currentProduct.id,

      { method: 'delete' })

      .then(response => {

        /* Duplicate the array and filter out the item to be deleted */

        var newItems = products.filter(function(item) {

        return item !== delProduct

      });            

      setProducts(newItems)

      setCurrentProduct(null)

  });

}

更新现有产品的逻辑

handleUpdate(product) {

    const updProduct = currentProduct;

    fetch( 'api/products/' + currentProduct.id, {

        method:'put',

        headers: {

          'Accept': 'application/json',

          'Content-Type': 'application/json'

        },

        body: JSON.stringify(product)

    })

    .then(response => {

        return response.json();

    })

    .then( data => {

        /* Updating the state */

        var updItems = products.filter(function(item) {

          return item !== updProduct

        })              

        setProducts(updItems.concat(product))

        setCurrentProduct(product)

    })

  }

您需要做的是深入研究,亲自动手,然后使用上述逻辑完成应用程序。我会给你一个提示:删除按钮最好放在Product组件内部,而更新功能应该有自己的组件。我鼓励您接受这一挑战并完成缺少的组件。

概括

我们已经从我们开始的地方走了很长一段路。首先,我们使用 Laravel 框架创建了一个 REST API。然后,我们讨论了混合 Laravel 和 React 的选项。最后,我们使用 React 构建了 API 的前端。

尽管我们主要专注于使用 React 创建单页应用程序,但您可以创建挂载到视图中特定元素的小部件或组件。React 非常灵活,因为它是一个库,而且是一个很好的库。

文章目录
  • 快速回顾
  • 在 Laravel 中设置 React
    • 使用 React Preset 命令(Laravel 5.5 和 Laravel 7+)
    • 手动方法(Laravel 5.4)
      • 资源/资产/js/component/Main.js
      • 资源/资产/js/app.js
  • 开发 React 应用程序
      • 资源/资产/js/component/Main.js
  • 显示产品数据
      • 资源/资产/js/component/Main.js
      • 资源/资产/js/component/Main.js
      • 资源/资产/js/component/Product.js
      • 应用程序现在应该看起来像这样:
  • 添加新产品
      • 资源/资产/js/component/AddProduct.js
      • 该组件基本上呈现一个输入表单,所有输入值都存储在状态(newProduct)中。然后,在表单提交时,handleSubmit()方法被调用。但是AddProduct需要将信息传回给父级,我们使用回调来做到这一点。
  • 接下来是什么?
      • 删除产品的逻辑
      • 更新现有产品的逻辑
      • 您需要做的是深入研究,亲自动手,然后使用上述逻辑完成应用程序。我会给你一个提示:删除按钮最好放在Product组件内部,而更新功能应该有自己的组件。我鼓励您接受这一挑战并完成缺少的组件。
  • 概括