Front Tech

Laravel(REST API)+React入门范例

不知道什么时候开始的以讹传讹,React变成了“很难入门”的框架,但React其实是近几年主流前端框架中最轻量、最简单的:只专注于视图(View)的处理,从发布至今多次拆分模块(拆出了ReactDOM,拆分了原来的addons),让React核心部分更简单。当然,React的生态很庞大,刚上手就面对整个生态会觉得复杂,但如果是用React完成日常工作中最常见的应用场景——前端读取后端数据实现增删该查,是非常简单的。

本文使用setSetate()和生命周期(Lifecycle)方法,完成具有完整功能的前端React组件。假设读者有一定的Javascrit经验,比如基础的ES6语法。可以使用命令行,能配置PHP运行Laravel。基本不涉及PHP代码,选Laravel是因为它已经预装了React的开发环境,节省配置时间。

环境

Ubuntu 16.04
Laravel 5.6
PHP 7.3.0

安装&配置

安装composer,创建项目dmyz

切换到项目根目录,将前端库更换成React,安装依赖的Javascript包,进行第一次打包:

完成后在public目录下会生成mix-manifest.json文件。最后修改resources/views/welcome.blade.php,加载生成的文件,添加特定ID的DIV用来挂载(mount)React组件:

配置完成,执行php artisan serve启动PHP开发服务器。访问http://127.0.0.1:8000可以看到React的演示组件(如下图)。

React example

其他配置

本节是Typescript和ExtractTextPlugin的配置,不使用可以跳过。

laravel-mix已经支持Typescript,但如果是React+Typescript(JSX)的环境,直接使用mix.ts会报错,建议配置Webpack规则:

React的一些库在Javascript中加载CSS,打包文件时如果需要将CSS输出到app.css文件,laravel-mix已经安装了style-loadercss-loader,所以可以使用以下配置:

JSX

不关闭之前php artisan serve启动的PHP开发服务器,打开另一个窗口,在项目根目录执行npm run hot,如果修改.js文件,页面会自动刷新。

打开Laravel生成的resources/assets/js/components/Example.js文件,核心部分(不算前两行import)其实只有3行:首先继承React.Component,创建一个名为Example的组件(L4),这个组件的render()方法返回一个元素(element),最后把这个组件挂载到页面上指定的位置就可以了(L24)。

7-18行是JSX。React中的JSX只是React.createElement的语法糖,可以不使用。代码层面先看作是HTML就行了。实际使用中对照HTML主要注意以下几点:

  • Javascript的关键字在JSX中通常会进行替换,比如代码中的className(class),以及htmlFor(for);style样式属性的分隔符一般会改成驼峰,例如backgroundColor(background-color)
  • 标签不能自闭合,例如<img src="">在JSX中会报错,必须是<img src="" />
  • 多行JXS代码需要(),如果是单行可以省略

JSX不是React的特性,其他前端框架也有插件可以实现。讨论JSX很多时候是和VDOM思路相关,这是对Javascript性能问题例如redraw/reflow的一种改进。

setState

React需要调用的只有两个方法:setState()forceUpdate(),后者很少会用到。state是React的核心,对比流行的MVC框架,可以看成一个简化的Model。修改state时,组件会重新渲染(render)。大多数情况下setState()是修改state的唯一方式。

给Example组件增加构造函数:

  • L5-11: 构造函数。设置组件的state(this.state)
  • L10: 等待2000ms,组件挂载(mount)完成后调用setState,修改this.state.action的值
  • L13: 如果this.state.action未指定,alert提示init render

页面会在两秒后显示this.state.action的值,也可以看到出现了两次alert。之前alert是写在render()里的,显然render()被触发了两次,第一次this.state.action未指定所以提示了init render,第二次是设置的值。这就是setState很重要的特性:

执行setState会重新触发组件的render()

所以,setState不能写在render()里,否则就无限循环了。这个例子用setTimeout()等待组件挂载,实际不会这么做,React有生命周期方法(Lifecycle methods)。

生命周期方法

当组件被挂载到DOM上时会触发componentDidMount(),将之前的代码做如下修改:

  • L8: 设置state的初始值
  • 11-13: 在componentDidMount中调用setState(),修改this.state.action
  • 14-16: 增加自定义的 _edit(),再次修改this.state.action的值
  • L18: 把_edit()绑定到div的click事件

点击div,页面上的内容会发生变化。所有的生命周期方法参看:http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

REST API

这节介绍使用Laravel设置REST API的方法,如果是其他数据源可以跳过。

利用APIResource需要以此完成以下几步:

编辑3个文件,一是migration文件定义表结构,位置 database/migrations/201..._create_page_table.php

二是controller配置增删改查的方法,编辑app/Http/Controllers/PageController.php:

三是model文件设置允许修改的字段,编辑app/Page.php

最后执行migrations,创建之后通过curl添加2条测试数据:

Example

setState()和生命周期方法,以及事件绑定,掌握这些就可以用React写一个增删改查的应用了。首先是读取数据,在前端显示,完整代码如下:

  • L7-10: 设置两个初始state,pages为空数组,pageId为null
  • L13-15: 用axios请求接口,把返回的内容赋给this.state.pages。Laravel的bootstrap.js文件已经require了axios,也添加了CSRF TOKEN
  • L39-50: map遍历this.state.pages,显示每一项的内容。将_edit()_delete()方法绑定到按钮的click事件
  • L30: _submit()方法绑定到form表单的submit事件

效果如下图:

最后只要补上方法就完工了,代码逻辑都是对state进行处理,然后调用setState()方法更新视图。比如_delete(),是从this.state.pages中移除这条记录:

_submit()是提交表单数据,把返回的数据push到this.state.pages

_edit()最简单,就是设置this.state.pageId和input的value。完整代码如下:

组件拆分和传参

如果不涉及组件拆分重用,了解以上内容就够了。会增加复杂度的是组件之间的联动和传参,React的Props可以看成组件之间传递的参数,传递的Props对在子组件中无法修改。通过良好的设计也能让复杂度保持在可控范围内。

尽量避免不经设计写好一个功能复杂的庞大组件再拆分成小组件。而是先考虑子组件的功能和需要的参数,再去写上层组件的方法。

之前的例子可以抽出表单写成Form组件。表单需要处理数据提交,这个prop没什么疑问。而点击Cancel按钮,取消编辑状态至少有三种写法:一是在上层组件上写一个_cancel()方法,传递给Form组件,此方法将上层组件的this.state.pageId设置为null。二是Form组件增加一个state参数,存储pageId。三是将需要的参数作为children传到Form组件中进行处理。

第一种写法代码量少,但在子组件操作上层的state了;第二种写法把上层的Props作为子组件的state,这种写法是不鼓励的,需要增加生命周期方法componentDidMount。16.4修复了getDerivedStateFromProps的问题,可以用它来判断Props是否变化再决定是否修改state。第三种写法比较笨,不够灵活。优点是容易理解,提高了直接复制代码的可用性。

已知问题/其他说明

PUT请求修改数据现在是无效的,Content-Type: multipart/form-data的PUT请求在Laravel中$request->all()是空,PHP7.3仍然没有解决这个问题。通常是利用POST接收然后转发给PUT处理,或者用x-www-form-urlencoded请求。

另一个常被提到的是Redux。如果是现在刚开始接触React,暂时不用了解。React 16.3以后Context API正式公布了,16.7也会引入hooks让类似的操作更简单,而且如果能保持数据流的简单可以不使用这类工具。

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
提醒