一、开发环境配置
-
首先保证安装Node >= 6 和 npm >= 5.2版本以上的node.js环境
-
然后执行以下的代码,创建一个基本React-App项目
npx create-react-app my-app
cd my-app
npm start
- 删除新项目 src/ 文件夹中的所有文件。这是我们接下来要写代码的区域,在接下来的教程中将会一步步补充进来。注意:不要删除整个src文件夹,只删除文件夹中原始文件。我们将在下一步中用此项目的示例替换默认源文件。
cd src
# 如果你使用 Mac 或者 Linux:
rm -f *
# 或者, 如果你使用 Windows:
del *
# 然后,切换回项目文件夹
cd ..
- 在 src/ 文件夹中添加一个名为 index.css 的文件,这个文件中包含的 CSS代码 。在 src/ 文件夹中添加一个名为 index.js 的文件,这个文件中包含的 JS代码 。然后在 src/ 文件夹 index.js 的文件中,在其顶部添加以下 3 行代码:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
现在,如果您在项目文件夹中运行 npm start,并在浏览器中打开 http://localhost:3000 ,你应该看到一个空的字段。
二、jsx
- 这就是 JSX ,他是 JavaScrip 的一种扩展语法。推荐在 React 中使用这种语法来描述 UI 信息.
const element = <h1>Hello, world!</h1>;
- 你可以用 花括号 把任意的 JavaScript 表达式 嵌入到 JSX 中。
const element = <h1>Hello, {name}</h1>;
- 可以在 if 语句或者是 for 循环中使用 JSX,用它给变量赋值,当做参数接收,或者作为函数的返回值
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
- 用 JSX 指定属性值,可以使用双引号来指定字符串字面量作为属性值,您也可以用花括号嵌入一个 JavaScript 表达式作为属性值:
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;
-
JSX 防止注入攻击,默认情况下, 在渲染之前, React DOM 会格式化(escapes) JSX中的所有值. 从而保证用户无法注入任何应用之外的代码. 在被渲染之前,所有的数据都被转义成为了字符串处理。 以避免 XSS(跨站脚本) 攻击。
-
Babel 将JSX编译成 React.createElement() 调用。以下连个代码是完全一样的。
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
三、元素渲染
- 要渲染一个 React 元素到一个 root DOM 节点,把它们传递给 ReactDOM.render() 方法
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
-
react 元素是 不可突变(immutable) 的. 一旦你创建了一个元素, 就不能再修改其子元素或任何属性。一个元素就像电影里的一帧: 它表示在某一特定时间点的 UI 。就我们所知, 更新 UI 的唯一方法是创建一个新的元素, 并将其传入 ReactDOM.render() 方法.
-
React DOM 会将元素及其子元素与之前版本逐一对比, 并只对有必要更新的 DOM 进行更新, 以达到 DOM 所需的状态。
四、组件和属性
-
从定义上来说, 组件就像JavaScript的函数。组件可以接收任意输入(称为”props”), 并返回 React 元素,用以描述屏幕显示内容。
-
函数式组件和类组件
//函数式
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
//类组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
- 元素也可以代表用户定义的组件,组件名称总是以大写字母开始。当 React 遇到一个代表用户定义组件的元素时,它将 JSX 属性以一个单独对象的形式传递给相应的组件。 我们将其称为 “props” 对象。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
//我们调用了 ReactDOM.render() 方法并向其中传入了 <Welcome name="Sara" /> 元素。
//React 调用 Welcome 组件,并向其中传入了 {name: 'Sara'} 作为 props 对象。
//Welcome 组件返回 <h1>Hello, Sara</h1>。
//React DOM 迅速更新 DOM ,使其显示为 <h1>Hello, Sara</h1>。
-
组件可以在它们的输出中引用其它组件
-
不要害怕把一个组件分为多个更小的组件。
-
Props 是只读的,无论你用函数或类的方法来声明组件, 它都无法修改其自身 props
五、状态和生命周期
-
用类定义的组件有一些额外的特性。 这个”类专有的特性”, 指的就是局部状态。state 和 props 类似,但是它是私有的,并且由组件本身完全控制。
-
把函数式组件转化为类组件
//1.创建一个继承自 React.Component 类的 ES6 class 同名类。
//2.添加一个名为 render() 的空方法。
//3.把原函数中的所有内容移至 render() 中。
//4.在 render() 方法中使用 this.props 替代 props。
//5.删除保留的空函数声明。
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
- 生命周期方法
componentDidMount() 钩子在组件输出被渲染到 DOM 之后运行。
componentWillUnmount() 钩子在产生的 DOM 被销毁时运行
- 不要直接修改 state(状态),用 setState() 代替,唯一可以分配 this.state 的地方是构造函数。
//例如,这样将不会重新渲染一个组件:
this.state.comment = 'Hello';
//用 setState() 代替:
this.setState({comment: 'Hello'});
- state(状态) 更新可能是异步的,React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。
//以下代码可能导致 counter(计数器)更新失败:
this.setState({
counter: this.state.counter + this.props.increment,
});
//要解决这个问题,应该使用第 2 种 setState() 的格式,它接收一个函数,而不是一个对象。该函数接收前一个状态值作为第 1 个参数, 并将更新后的值作为第 2 个参数:
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
-
当你调用 setState(), React 将合并你提供的对象到当前的状态中。
-
无论作为父组件还是子组件,它都无法获悉一个组件是否有状态,同时也不需要关心另一个组件是定义为函数组件还是类组件。这就是 state(状态) 经常被称为 本地状态 或 封装状态的原因。 它不能被拥有并设置它的组件 以外的任何组件访问。
-
一个组件可以选择将 state(状态) 向下传递,作为其子组件的 props(属性):
六、处理事件
-
通过 React 元素处理事件跟在 DOM 元素上处理事件非常相似。但是有一些语法上的区别:
-
React 事件使用驼峰命名,而不是全部小写。
-
通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。
-
在 React 中你不能通过返回 false(即 return false; 语句) 来阻止默认行为。必须明确调用 preventDefault
-
当使用 React 时,你一般不需要调用 addEventListener 在 DOM 元素被创建后添加事件监听器。相反,只要当元素被初始渲染的时候提供一个监听器就可以了。
-
-
一般情况下,如果你引用一个后面没跟 () 的方法,例如 onClick={this.handleClick} ,那你就应该 绑定(bind) 该方法。
如果调用 bind 令你烦恼,有两种方法可以解决这个问题。
代码块
//方法一 使用实验性的 属性初始化语法 ,那么你可以使用属性初始值设置来正确地 绑定(bind) 回调:
class LoggingButton extends React.Component {
// 这个语法确保 `this` 绑定在 handleClick 中。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
//方法二 没有使用属性初始化语法,可以在回调中使用一个 箭头函数
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
七、条件渲染
-
React 中的条件渲染就和在 JavaScript 中的条件语句一样。使用 JavaScript 操作符如 if 或者条件操作符来创建渲染当前状态的元素,并且让 React 更新匹配的 UI 。
-
可以用变量来存储元素。这可以帮助您有条件地渲染组件的一部分,而输出的其余部分不会更改。
-
使用逻辑 && 操作符的内联 if 用法,可以 在JSX中嵌入任何表达式 ,方法是将其包裹在花括号中。这也包括 JavaScript 逻辑 && 运算符。因为在 JavaScript 中, true && expression 总是会评估为 expression ,而 false && expression 总是执行为 false 。
-
使用条件操作符的内联 If-Else,另一个用于条件渲染元素的内联方法是使用 JavaScript 的条件操作符 condition ? true : false
-
在极少数情况下,您可能希望组件隐藏自身,即使它是由另一个组件渲染的。为此,返回 null 而不是其渲染输出。
八、列表(Lists) 和 键(Keys)
- 多组件渲染,可以创建元素集合,并用一对大括号 {} 在 JSX 中直接将其引用即可。
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
-
键(Keys),帮助 React 标识哪个项被修改、添加或者移除了。数组中的每一个元素都应该有一个唯一不变的键(Keys)来标识:挑选 key 最好的方式是使用一个在它的同辈元素中不重复的标识字符串。
-
keys 只在数组的上下文中存在意义。如果你提取 一个 ListItem 组件,应该把 key 放置在数组处理的 元素中,不能放在 ListItem 组件自身中的
- 根元素上。
-
在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。我们可以在操作两个不同数组的时候使用相同的 keys :
-
键是React的一个内部映射,但其不会传递给组件的内部。如果你需要在组件中使用相同的值,可以明确使用一个不同名字的 prop 传入。
//Post 组件可以读取 props.id,但是不能读取 props.key 。
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
- 在 JSX 中嵌入 map(),JSX允许在大括号中嵌入任何表达式,因此可以 内联 map()
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
九、表单
- 在 HTML 中,表单元素如 , 和 表单元素通常保持自己的状态,并根据用户输入进行更新。而在 React 中,可变状态一般保存在组件的 state(状态) 属性中,并且只能通过 setState() 更新。通过使 React 的 state 成为 “单一数据源原则” 来结合这两个形式。然后渲染表单的 React 组件也可以控制在用户输入之后的行为。这种形式,其值由 React 控制的输入表单元素称为“受控组件”。
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
-
在 React 中, 的赋值使用 value 属性替代。
-
注意,选项是初始化选中的,因为它的 selected 属性。React 中,并不使用这个 selected 属性,而是在根 select 标签中使用了一个 value 属性。这使得受控组件使用更方便。您可以将一个数组传递给 value 属性,允许你在 select 标签中选择多个选项。
-
当您需要处理多个受控的 input 元素时,您可以为每个元素添加一个 name 属性,并且让处理函数根据 event.target.name 的值来选择要做什么。
-
比较成熟的表单控件方法,可以在formilk学习。
十、状态提升(Lifting State Up)
-
通常情况下,同一个数据的变化需要几个不同的组件来反映。我们建议提升共享的状态到它们最近的祖先组件中
-
在 React 中,共享 state(状态) 是通过将其移动到需要它的组件的最接近的共同祖先组件来实现的。 这被称为“状态提升(Lifting State Up)”。
-
在一个 React 应用中,对于任何可变的数据都应该循序“单一数据源”原则。通常情况下,state 首先被添加到需要它进行渲染的组件。然后,如果其它的组件也需要它,你可以提升状态到它们最近的祖先组件。你应该依赖 从上到下的数据流向 ,而不是试图在不同的组件中同步状态
十一、组合(Composition) VS 继承(Inheritance)
-
React 拥有一个强大的组合模型,我们建议使用组合而不是继承以实现代码的重用。
-
一些组件在设计前无法获知自己要使用什么子组件,尤其在 Sidebar 和 Dialog 等通用 “容器” 中比较常见。我们建议这种组件使用特别的 children prop 来直接传递 子元素到他们的输出中:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
- 在一个组件中你可能需要多个 “占位符” 。在这种情况下,你可以使用自定义的 prop(属性),而不是使用 children :
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
- 我们可能说一个 WelcomeDialog 是 Dialog 的一个特殊用例。在React中,也可以使用组合来实现,一个偏“特殊”的组件渲染出一个偏“通用”的组件,通过 props(属性) 配置它
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
十二、编程思想
-
确定 UI state(状态)
1.是否通过 props(属性) 从父级传入? 如果是这样,它可能不是 state(状态) 。
2.是否永远不会发生变化? 如果是这样,它可能不是 state(状态)。
3.是否可以由组件中其他的 state(状态) 或 props(属性) 计算得出?如果是这样,则它不是 state(状态)。 -
react生命周期
React生命周期主要包括三个阶段:初始化阶段、运行中阶段和销毁阶段,在React不同的生命周期里,会依次触发不同的钩子函数
详细链接见:图解ES6中的React生命周期