react(三)
深入 JSX
从本质上讲,JSX 只是为 React.createElement(component, props, ...children)
函数提供的语法糖。
// JSX代码: <MyButton color="blue" shadowSize={2}>Click Me</MyButton> // 编译为: React.createElement( MyButton, {color: 'blue', shadowSize: 2}, 'Click Me' )
如果不存在子节点,你可以使用自闭合(self-closing)格式的标签。
// JSX代码: <div className="sidebar" /> // 编译为: React.createElement( 'div', {className: 'sidebar'}, null )
ReactDOM.render( // <div className='sidebar' />, React.createElement( 'div', {className: 'sidebar'}, null ), document.getElementById('container') );
a)指定 React 元素类型
一个 JSX 标签的开始部分决定了 React 元素的类型,首字母大写的标签指示 JSX 标签是一个 React 组件,这些标签会被编译成 命名变量 的直接引用。所以如果你使用 JSX <Foo />
表达式,那么 Foo
必须在作用域中。
React 必须在作用域中。因为 JSX 被编译为 React.createElement
的调用,所以 React
库必须在你 JSX 代码的作用域中。
例如,所有的 imports 在这段代码中都是必须的。如果你不使用 JavaScript 打包器,而是通过在 <script>
标签加载 React ,它已经作为一个全局 React
存在。
import React from 'react'; import CustomButton from './CustomButton'; function WarningButton() { // return React.createElement(CustomButton, {color: 'red'}, null); return <CustomButton color="red" />; }
b)对 JSX 类型使用点语法
在 JSX 中,你也可以使用点语法引用一个 React 组件。如果你有一个单一模块(module) ,但却导出(exports) 多个 React 组件时,这将会很方便。例如,如果 MyComponents.DatePicker
是一个组件,你可以直接在 JSX 中使用它:
const MyComponents = { DatePicker(props) { return <div>Imagine a {props.color} datepicker here.</div>; } } function BlueDatePicker() { return <MyComponents.DatePicker color="blue" />; }
c)用户定义组件必须以大写字母开头
当一个元素类型以小写字母开头,它表示引用一个类似于 <div>
或者 <span>
的内置组件,会给 React.createElement
方法传递 'div'
或者 'span'
字符串。以大写字母开头的类型,类似于 <Foo />
,会被编译成 React.createElement(Foo)
,对应于自定义组件或者在 JavaScript 文件中导入的组件。
d)JSX 中的 props(属性)
props(属性) 默认为 “true”
如果你没给 prop(属性) 传值,那么他默认为 true
。下面两个 JSX 表达式是等价的:
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
通常情况下,我们不建议使用这种类型,因为这会与ES6中的对象shorthand混淆 。ES6 shorthand 中 {foo}
指的是 {foo: foo}
的简写,而不是 {foo: true}
。这种行为只是为了与 HTML 的行为相匹配。(举个例子,在 HTML 中,<input type="radio" value="1" disabled />
与 <input type="radio" value="1" disabled="true" />
是等价的)
e)jsx中的children
class App extends React.Component { render() { return ( <div> <h1>{this.props.title}</h1> {this.props.children} </div> ); } } ReactDOM.render( <App title="编辑供应商"> <div className="supply">供应商Id: 123</div> </App>, document.getElementById('root') );
浏览器渲染结果:
使用 PropTypes 进行类型检查
随着应用日渐庞大,你可以通过类型检查捕获大量错误。 要检查组件的属性,你需要配置特殊的 propTypes
属性。PropTypes
包含一整套验证器,可用于确保你接收的数据是有效的。
import PropTypes from 'prop-types'; class Greeting extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } Greeting.propTypes = { name: PropTypes.string };
在这个示例中,我们使用了 PropTypes.string
。当你给属性传递了无效值时,JavsScript 控制台将会打印警告。出于性能原因,propTypes
只在开发模式下进行检查。
Refs 和 DOM
不要过度使用 Refs:你可能首先会想到在你的应用程序中使用 refs 来更新组件。如果是这种情况,请花一点时间,更多的关注在组件层中使用 state。在组件层中,通常较高级别的 state 更为清晰。
在 DOM 元素上添加 Ref:React 支持给任何组件添加特殊属性。ref
属性接受回调函数,并且当组件 装载(mounted) 或者 卸载(unmounted) 之后,回调函数会立即执行。当给 HTML 元素添加 ref
属性时, ref
回调接受底层的 DOM 元素作为参数。
class CustomTextInput extends React.Component { constructor(props) { super(props); this.focus = this.focus.bind(this); } focus() { // 通过使用原生API,显式地聚焦text输入框 this.textInput.focus(); } render() { // 在实例中通过使用`ref`回调函数来存储text输入框的DOM元素引用(例如:this.textInput) return ( <div> <input type="text" ref={(input) => { this.textInput = input; }} /> <input type="button" value="Focus the text input" onClick={this.focus} /> </div> ); } } ReactDOM.render( <CustomTextInput />, document.getElementById('container') );
优化性能
使用生产版本:如果你在你的 React 应用程序中进行检测性能问题时,确保你正在使用压缩过的生产版本。默认情况下,React包含很多在开发过程中很有帮助的警告。然而,这会导致 React 更大更慢。因此,在部署应用时,请确认使用了生产版本。
最好在开发应用时使用开发模式,部署应用时换为生产模式。我们提供压缩好的生产版本的 React 和 React DOM 文件(注意只有结尾为 .min.js
的React文件才是适合生产使用的)。
<script src="https://unpkg.com/react@15/dist/react.min.js"></script> <script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
避免重新渲染:React 构建并维护渲染 UI 的内部表示。它包括你从组件中返回的 React 元素。这些内部状态使得 React 只有在必要的情况下才会创建DOM节点和访问存在DOM节点,因为对 JavaScript 对象的操作是比 DOM 操作更快。这被称为”虚拟DOM”。当组件的 props 和 state 改变时,React 通过比较新返回的元素 和 之前渲染的元素 来决定是否有必要更新DOM元素。当二者不相等时,则更新 DOM 元素。
React 顶层 API
React
是 React 库的入口。如果你用 <script>
标签来加载 React,这些顶级 API 都在 React
这个全局变量上。如果你在 npm 下使用 ES6 ,你可以这样写 import React from 'react'
。如果你在 npm 下使用 ES5,你可以这样写 var React = require('react')
。
React 组件允许你将UI拆分为独立的可重用的模块,并且每个模块逻辑也是独立的。 React组件可以通过扩展 React.Component
或 React.PureComponent
来定义。
React.Component
是 React 组件使用 ES6 类(classes) 定义时的基类。
每个组件都有几个 “生命周期方法” ,前缀为 will
的方法在一些事情发生之前被调用,而前缀为did
的方法在一些事情发生后被调用。
constructor(props)
在 React 组件被装载(mounted)前,该组件的 constructor(构造函数) 将被调用。当实现 React.Component
子类的 constructor(构造函数) 方法时,你应该在其它任何其他语句之前调用 super(props)
,否则,this.props
将在 constructor(构造函数) 中是 undefined(未定义) ,这将导致 bug 。构造函数是初始化 状态(state) 的正确位置。 为此,只需将一个对象分配给this.state
; 不要尝试在构造函数上调用 setState()
。 构造函数也经常用于将事件处理程序绑定到类实例。如果你没有初始化 状态(state) ,并且没有绑定方法,你不需要为你的 React 组件实现一个构造函数。
render()
render()
方法是必需的。当被调用时,它会检查 this.props
和 this.state
并返回一个单独的 React 元素。 此元素可以是原生 DOM 组件的表示形式,例如 <div />
,也可以是你自己定义的另一个复合组件。
componentDidMount()
componentDidMount()
在组件 装载(mounting) 后被立即调用。初始化所需要的 DOM 节点的应该放在这里。 如果你需要从远程加载数据,这是一个实例化网络请求的好地方。
componentWillUnmount()
componentWillUnmount()
在一个组件被 卸载(unmounted) 和 销毁(destroyed) 之前立即被调用。 在此方法中执行任何必要的清理,例如使计时器无效,取消网络请求,或清理在 componentDidMount
中创建的任何 DOM 元素。
setState(updater, [callback])
总是会导致重新渲染,除非 setState()
排队更改组件的 state ,并通过更新 state 来告诉 React ,该组件及其子组件需要重新渲染。这是用于 响应事件处理程序 和 服务器响应 更新用户界面的主要方法。setState()shouldComponentUpdate()
返回 false
。
第一个参数可以是一个 updater
函数,您也可以随意的传递 一个对象 作为 setState()
的第一个参数。
ReactDOM
如果使用 <script>
标签加载 React ,这些在 ReactDOM
上的顶层 API 全局可用。 如果你使用 ES6 与 npm ,你可以写 import ReactDOM from 'react-dom'
。如果你使用 ES5 与 npm ,你可以写 var ReactDOM = require('react-dom')
。
React支持所有流行的浏览器,包括 Internet Explorer 9 及更高版本。
ReactDOM.render(
element,
container,
[callback]
)
渲染一个 React 元素到由 container
提供的 DOM 中,并且返回组件的一个 引用(reference)(或者对于 无状态组件 返回 null
)。如果 React 元素先前已经被渲染到了 container
中,那么将对其执行更新,并且对 DOM 只修改需要修改的地方,以反映最新的 React元素。ReactDOM.render()
控制传入的容器节点的内容。当第一次调用时,容器内部的任何现有DOM元素都会被替换。 之后使用 React 的 DOM diffing 算法来进行有效的更新。ReactDOM.render()
不会修改容器节点(只修改容器的子节点)。
DOM 元素
在 React 中,所有 DOM properties 和 attributes(包括事件处理程序)都应该是驼峰命名法命名。 例如,HTML 属性 tabindex
对应于 React 中的 tabIndex
属性。 唯一的例外是 aria-*
和 data-*
属性,它们应该是小写。例如,aria-label
就应该是 aria-label
,不需要驼峰式命名。
属性中的差异 - 有许多属性在 React 和 HTML 之间有不同的使用方式,如:
className:一般要指定 CSS 类,使用 className
属性。 这适用于所有常规DOM和SVG元素,如 <div>
,<a>
和其他。
style(一般不建议在元素上应用 style
属性。在大多数情况下,应该使用 className
引用外部 CSS 样式表中定义的类。 在 React 应用程序中,经常在渲染时使用 style
来添加动态计算的样式):style
属性接受具有驼峰命名属性的 JavaScript 对象,而不是 CSS 字符串。 这与 JavaScript DOM 的 style
属性一致,但是更高效,并且防止XSS安全漏洞。ms
以外的供应商前缀应以大写字母开头。
<form style={{backgroundColor:'#fff',fontSize:'14px'}} className="search-form"></form>
浏览器渲染结果:
const divStyle = { WebkitTransition: 'all', // 注意这里的大写首字母 'W' msTransition: 'all' // 'ms' 'ms'是唯一的小写字母的浏览器前缀,大小写均可 }; function ComponentWithTransition() { return <div style={divStyle}>This should work cross-browser</div>; }