React 教程
React 教程
React 是一个用于构建用户界面的 JAVASCRIPT 库。
React 主要用于构建 UI,很多人认为 React 是 MVC 中的 V(视图)。
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在 2013 年 5 月开源了。
由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
React 特点
- 声明式
- React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据改变时 React 能有效地更新并正确地渲染组件。
- 以声明式编写 UI,可以让你的代码更加可靠,且方便调试。
- 组件化
- 创建拥有各自状态的组件,再由这些组件构成更加复杂的 UI。
- 组件逻辑使用 JavaScript 编写而非模版,因此你可以轻松地在应用中传递数据,并使得状态与 DOM 分离。
- 一次学习,随处编写
- 无论你现在正在使用什么技术栈,你都可以随时引入 React 来开发新特性,而不需要重写现有代码。
- React 还可以使用 Node 进行服务器渲染,或使用 React Native 开发原生移动应用。
阅读本教程前,您需要了解的知识:
在开始学习 React 之前,您需要具备以下基础知识:
- HTML5
- CSS
- JavaScript
我们假定你已经对 HTML 和 JavaScript 都比较熟悉了。即便你之前使用其他编程语言,你也可以跟上这篇教程的。除此之外,我们假定你也已经熟悉了一些编程的概念,例如,函数、对象、数组,以及 class 的一些内容。
如果你想回顾一下 JavaScript,你可以阅读这篇教程。注意,我们也用到了一些 ES6(较新的 JavaScript 版本)的特性。在这篇教程里,我们主要使用了箭头函数(arrow functions)、class、let 语句和 const 语句。你可以使用 Babel REPL 在线预览 ES6 的编译结果。
React 第一个实例
在每个章节中,您可以在线编辑实例,然后点击按钮查看结果。
你可以在官网 https://reactjs.org/ 下载最新版。
<div id="example"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script>
简单组件
React 组件使用一个名为 render() 的方法,接收输入的数据并返回需要展示的内容。在示例中这种类似 XML 的写法被称为 JSX。被传入的数据可在组件中通过 this.props 在 render() 访问。
使用 React 的时候也可以不使用 JSX 语法。尝试使用 Babel REPL,了解 JSX 被编译成原生 JavaScript 代码的步骤。
class HelloMessage extends React.Component {
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('hello-example')
);
有状态组件
除了使用外部数据(通过 this.props 访问)以外,组件还可以维护其内部的状态数据(通过 this.state 访问)。当组件的状态数据改变时,组件会再次调用 render() 方法重新渲染对应的标记。
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
</div>
);
}
}
ReactDOM.render(
<Timer />,
document.getElementById('timer-example')
);
应用
使用 props 和 state,我们可以创建一个简易的 Todo 应用。在示例中,我们使用 state 来保存现有的待办事项列表及用户的输入。尽管事件处理器看似被内联地渲染,但它们其实只会被事件委托进行收集和调用。
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = { items: [], text: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<label htmlFor="new-todo">
What needs to be done?
</label>
<input
id="new-todo"
onChange={this.handleChange}
value={this.state.text}
/>
<button>
Add #{this.state.items.length + 1}
</button>
</form>
</div>
);
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.text.length) {
return;
}
const newItem = {
text: this.state.text,
id: Date.now()
};
this.setState(state => ({
items: state.items.concat(newItem),
text: ''
}));
}
}
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}
}
ReactDOM.render(
<TodoApp />,
document.getElementById('todos-example')
);
在组件中使用外部插件
React 允许你结合其他框架或库一起使用。示例中使用了一个名为 remarkable 的外部 Markdown 库。它可以实时转换 <textarea>
里的内容。
class MarkdownEditor extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = { value: 'Hello, **world**!' };
}
handleChange(e) {
this.setState({ value: e.target.value });
}
getRawMarkup() {
const md = new Remarkable();
return { __html: md.render(this.state.value) };
}
render() {
return (
<div className="MarkdownEditor">
<h3>Input</h3>
<label htmlFor="markdown-content">
Enter some markdown
</label>
<textarea
id="markdown-content"
onChange={this.handleChange}
defaultValue={this.state.value}
/>
<h3>Output</h3>
<div
className="content"
dangerouslySetInnerHTML={this.getRawMarkup()}
/>
</div>
);
}
}
ReactDOM.render(
<MarkdownEditor />,
document.getElementById('markdown-example')
);
类组件与函数组件
我们应该先解释这些组件的不同之处。基本上每个组件可以是 function 或 class。它们之间的主要区别在于,类组件有函数组件中没有的一些功能:它们有 state 并使用 refs、生命周期等。从 v16.7 开始我们可以使用 hooks,因此可以使用 hooks 来进行 state 和 refs。
类组件有两种类型:Component 和 PureComponent。它们唯一的区别是 PureComponent 可以对 props 和 state 进行浅层比较 —— 这在你不想“浪费”渲染资源的情况下有独到的好处,一个组件及其子组件恰好在渲染后处于相同状态。不过它只有一个浅层比较;如果你想实现自己的比较操作(假如你传递的是复杂的 props),只需要用 Component 并覆盖 shouldComponentUpdate(默认情况下返回 true)。从 16.6 + 开始,在函数组件中也可以用类似的东西 —— 全靠 React.memo 这个更高阶的组件,在默认情况下表现得像 PureComponent(浅层比较),在你进行自定义的 props 比较时它还需要第二个参数。
一般来说如果你能用函数组件(假设你不需要类功能)那么就用它。不过从 16.7.0 开始,由于生命周期方法,只能用类组件。但是我认为函数组件更透明,更容易推理和理解。