65.9K
CodeProject 正在变化。 阅读更多。
Home

构建 ReactJs 组件以及 ReactJs 与 AngularJS 的集成。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (4投票s)

2015 年 6 月 3 日

CPOL

10分钟阅读

viewsIcon

32079

构建 ReactJs 组件以及 ReactJs 与 AngularJS 的集成。

引言

本文简要介绍了

  1. 如何构建 React 组件
  2. 不同 React 组件之间的通信
  3. AngularJS 和 React 组件的集成。

描述

何时在 Angular 中将 React 作为救命稻草

当需要使用 AngularJS 渲染大量记录(例如超过 1000 行)时,其性能问题始终会被提出。由于与每个 $scope 值关联的内部 $watch 和 $digest 循环过多,这总是会影响页面性能。

ReactJS 是解决这个问题的一大救星。

thierry.nicola这里发布了一篇好文章,你可以看到使用 AngularJS 渲染 1500 行大约需要 1.35 秒,而 ReactJS 只需要大约 310 毫秒。阅读这篇出色的文章,你将清楚地了解到 React 渲染速度比 Angular 快多少。

我们将简要介绍的内容

因此,在这篇文章中,我们将了解如何构建

  1. React 组件
  2. 不同 React 组件之间的通信
  3. AngularJS 和 React 组件的集成。

注意:所有源代码都可以在 GitHub 这里获取

要运行演示项目,请阅读项目文件夹中 README.md 文件中的说明。

ReactJS

React 组件意味着构建可重用功能。就像 AngularJS 中有指令一样,你可以将指令与 React 组件关联起来。

这意味着我们将使用 React 组件而不是指令。我们将使用 Angular 的控制器、工厂和模型,但为了显示,我们将使用 ReactJS 而不是指令。

我们将在 ReactJS 中构建图片列表,并将其与 AngularJS 集成。

每张图片及其相关功能将构成 React 中的一个子组件,这些子组件的列表将是 React 的一个父组件。

以下页面显示了与 AngularJS 集成的图片列表组件。

在开始构建任何 React 组件时,将其划分为父子层次结构。

因此,你可以看到我们已将组件划分为父组件和子组件,如上图(A)所示。

React 使用虚拟 DOM 方法更新 DOM。

虚拟DOM

虚拟 DOM 是实际 DOM 的 JavaScript 表示;React 跟踪当前状态的虚拟 DOM 与先前状态的虚拟 DOM 之间的差异。然后仅使用这些差异更新实际 DOM。

JSX

我们使用 JSX 语法编写 React 代码。JSX 有助于编写类似 HTML 的语法,这些语法会转换为轻量级 JavaScript 对象。你可以从 React 文档此处获取更多关于 JSX 的信息。

在开发 React 时需要考虑一些基本的构建块

组件如下。只需看一眼,我们就会了解它们是什么以及如何使用它们。

现在我们将分析我们的父组件

我还针对代码块放置了注释和评论框,以进行更多探索。

请阅读注释和评论框中的文本以获取更多线索。

ItemList.jsx(父组件)->

propTypes 用于限制从外部接收到组件中的 props 的类型。你也可以将其设置为可选或必需。

propsType 声明的一些示例

  • React.PropTypes.array,
  • React.PropTypes.bool,
  • React.PropTypes.func,
  • React.PropTypes.number,
  • React.PropTypes.object,
  • React.PropTypes.string,
  • React.PropTypes.func.isRequired(如我们在上面代码中声明的那样)

Props:组件中不会更新的数据部分,将用作只读,然后将其声明在 props 中。

在我们的示例中,我们从 JSON 获取 id、图像 URL、名称,并将其用作只读,因此我们将这些保留在 props 中。

注意:要访问 props,请始终使用 this.props.propName。

State:组件中将要更新的数据部分,即对于该相应组件而言是可变的,那么它将属于 state。

注意:要访问 state,请始终使用 this.state.stateName。

我们正在更新 selectedItems 的字符串数组,它就是当前组件的一个状态。由于它是组件中的可变对象,我们将其作为状态保留。

getInitialState:它用于为状态提供初始值或在组件加载时初始化状态。在这里,我们使用空数组初始化了 selectedItems

getDefaultProps:通过此方法,我们可以为接收到的 props 提供默认值,如果这些 props 未能从外部提供。我们可以查看上面注释代码作为如何实现此功能的参考。

selectItems:这是我们为该组件声明的自定义函数。我们在此方法中更新了数组(状态)selectedItems。如上面的代码所示,我们将其作为 props 传递给其子组件,并从子组件中调用它来修改其状态。

this.setState({:它用于更新组件的状态,并使用更新后的状态重新渲染相应的组件以反映修改后的状态。我们在 selectItems 函数中调用此方法以渲染组件以反映其更新后的状态。

render:它用于在浏览器中渲染你提供的 HTML 标记和你的组件。

render 函数的实际用法如下

React.render(<Component_Name />, document.body);

Component_Name:你使用 React.createClass 方法声明的 React 组件。

document.body:渲染位置。你可以在这里放置任何 HTML 标签而不是 body。

例如,document.getElementById('Div_ID')。

但在我们的例子中,我们没有提供第二个参数,因为我们将使用 Angular 渲染它。

它是如何使用 Angular 实现的,我们很快就会看到!!J

在 render 函数中,我们迭代了接收到的 props(数据),这是一个对象列表,并将其传递给子组件 Item,然后将这些子组件逐个放置在 <li> 元素中。因此我们有新创建的对象 var products

代码片段

在 return 函数中,我们只放置了这个 products 对象,它在 <ul> 标签中嵌入了带有 <li> 元素的子组件 Item。

代码片段

我们还在 <h3> 标签中使用了状态 selectedItems 来显示被点击的子组件名称。

<h3><div className="panel headerItem">selected  item :{ this.state.selectedItems.length>0?this.state.selectedItems.join(', '):"No Items yet" }</div></h3>

点击子组件按钮“Change My status”后,它将在选定项目标签中显示项目名称。再次点击同一按钮将从选定项目标签中删除项目名称。

 

所有这些业务逻辑都放置在父组件的 selectItems 函数中。

它接收从子组件点击的项目的名称,正如我们所看到的,我们已经将此函数作为 prop 传递给子组件。现在我们将看看如何从子组件调用它。

Item.jsx(子组件)->

在 onClick 上调用当前组件的函数 changeStatus。在该函数中,调用作为 props 从父组件接收到的父组件的函数 selectitemthis.props.selectitem(this.props.data.name) 带有参数。


这就是从子组件调用父组件方法并向其传递参数的方式。

 

你可以观察到我们从父组件向子组件传递了两个函数

在父组件中

<Item data={product} clickfunc={clickfunc} selectitem={selectItems} />

因此,在子组件的 change status 函数中,我们通过 props 调用了这两个接收到的函数。

clickfunc 的作用

它调用 Angular 控制器 (MyCTRL) 的函数 “ctrlFunc”。

Angular 方面

我们声明了控制器 myCTRL

我们还创建了工厂 loadItems,用于加载并返回 JSON 文件数据。

在控制器中,我们创建了配置对象“config”,它有一个函数“ctrlFunc”。

以及使用工厂填充 JSON 数据的 mydata 变量。

例如:

//declaring the config object which would be latter passed to react component as prop.

$scope.config = {};

//Function declared on config object.
// Which will be called later from React component.

$scope.config.ctrlFunc=function() {
   alert("Method in controller called");
}

loadItems.LoadData().then(function (data) {
    data.forEach(function(d){ d.image = "../"+d.image });

    //populated the mydata variable of config object using factory.
    $scope.config.mydata = data;
});

ngReact:通过使用 ngReact 模块,我们将集成 Angular 和 React。

关于 ngReact 的更多信息,你可以从 David Chang 编写的此处阅读。

根据此,有两种不同的方式来集成 React 和 Angular。

1. reactComponent 2. reactDirective

我们正在使用其中一种方式,称为 reactDirective。

ngReact.js 中的代码片段

return angular.module('react', [])
              .directive('reactComponent', ['$timeout', '$injector', reactComponent])
              .factory('reactDirective', ['$timeout','$injector', reactDirective]);
}));

reactDirective: 它是将你的 React 组件转换为 Angular 指令的 Angular 工厂。

我们已经在 bower.json 中提到了它的依赖关系。

下载 ngReact 后,将其依赖关系标记到你的 Angular 应用程序中,就像我们所做的那样。

var app = angular.module('app', ['react']);

我们将使用父组件作为 Angular 的指令,因此我们必须将其传递给 ngReat 的

工厂来创建它并将其用作指令。

 

Index.html

脚本依赖部分

<script src="../bower_components/angular/angular.js"></script>

<script src="../bower_components/angular-ui-router/release/angular-ui-router.js"></script>

<script src="../bower_components/react/react.js"></script>

<script src="../bower_components/react/JSXTransformer.js"></script>

<script src="../bower_components/ngReact/ngReact.js"></script>

<script src="item.jsx" type="text/jsx"></script>

<script src="itemList.jsx" type="text/jsx"></script>

<script src="app.js"></script>

这是文件

  1. <script src="../bower_components/react/JSXTransformer.js"></script> 将用于开发 React 组件的 JSX 代码转换为纯 JavaScript 对象。

  2. <script src="item.jsx" type="text/jsx"></script>
    <script src="itemList.jsx" type="text/jsx"></script> 这里我们已将这两个组件文件添加到页面中,但请注意这里的 src 属性

是 .jsx 扩展名而不是 .js 扩展名。因为我们使用 JSX 语法编写组件。

使用创建的 itemlist 指令

<div id="itemslist" >

  <itemlist data="config" watch-depth="reference"/>

</div>
  1. 你可以看到我们提供了“config”对象(在 $scope 上声明)作为组件的 props 值。
  2. 父组件将数据作为其 props,这些数据将作为配置对象在该组件中填充。

3. 该配置对象还包含方法“ctrlFunc”,该方法将从 rect 组件调用。因此,我们也可以从 React 组件调用控制器函数。

以下是控制器函数如何从 Angular 传播到父组件再传播到子组件的流程。

这就是数据及其函数如何从 Angular 控制器流向 React 子组件,以及如何从 React 子组件调用控制器函数。

通过这种方式,我们可以将数据从 Angular 控制器传递到 React 组件。以及从 React 父组件传递到其子组件。

我们也可以使用 React 自己的事件从外部获取数据。

以下是一些常用事件列表

  • componentWillMount – 在组件挂载之前触发
  • componentDidMount – 在组件挂载之后触发
  • componentWillReceiveProps – 每当 props 发生变化时触发
  • componentWillUnmount – 在组件卸载之前触发。

为了完全控制数据,我们还在提供给数据 prop 的 config 对象中注入了函数,该函数将作为回调/将在控制器中通知某些事情。因此,我们已经通过获取 JSON 数据在控制器中创建了此对象,然后将其传递给 React。

它还有助于关注点分离。

你可以使用 componentWillMount/ componentDidMount 在 React 组件中获取数据对象。

有关其实现的更多信息,你可以查看文档中的以下页面

有关其实现的更多信息,你可以查看此处

需要记住的事情

  1. 你可能已经注意到我在子组件中注释了代码
render:function(){

    console.log("Child component called");

    var data=this.props.data;

    // data.image='../'+data.image;//great mistake  to modify prop here.so always 
    // modify object outside before passing it to

    // parent component.

在这里,我们正在将图像源修改为其正确的位置。

但正如我之前提到的,永远不要修改 props。因为每当调用 this.state 时,组件都会重新渲染。因此,由于我们正在修改 props,它将不断附加先前修改的值。你只需取消注释代码并多次点击按钮并调试 data.image 值。你会立即发现这个错误。

因此,我们已经在控制器数据中修改了此 props 值,然后再将其发送到 React。

loadItems.LoadData().then(function (data) {
    data.forEach(function(d){ d.image = "../"+d.image });
    //populated the mydata variable of config object using factory.

    $scope.config.mydata = data;

2. 我们还在父组件和子组件中提到了控制台消息。

父组件

render: function() {

    console.log("Parent component called");

子组件

    render:function(){

    console.log("Child component called");

它帮助你了解每次调用 this.setState 时组件如何重新渲染。

只需点击“change my state”按钮,看看父组件和子组件如何重新渲染。此消息在浏览器中记录了多少次等。

3. 对于样式,我们在这里使用了“className”来应用 css 类,而不是

我们熟悉的 class。请注意,这就是我们将 css 类应用于 HTML 元素的方式。

你也可以提供内联样式

要提供内联样式,你需要在 render 函数内部声明样式对象,如下所示

var myStyle = {

      color: ‘colorname’,

      fontSize: 10

    };

键应遵循“驼峰式”格式。

并按如下方式应用

React.render(<div style={myStyle }>Hello World!</div>, HTML_Element_Object);

:) :) 感谢阅读!!!

源代码

你可以从我的以下 github 链接托管的项目中下载所有源代码

https://github.com/amit-ashtekar/ReactJS_AND_ReactJS_with_AngularJS

© . All rights reserved.