什么是 React?
- React 是一个用于构建用户界面的 JavaScript 库 核心专注于视图,目的实现组件化开发
组件化概念
我们可以很直观的将一个复杂的页面分割成若干个独立组件,每个组件包含自己的逻辑和样式 再将这些独立组件组合完成一个复杂的页面。 这样既减少了逻辑复杂度,又实现了代码的重用
- 可组合:一个组件可以和其他的组件一起使用或者可以直接嵌套在另一个组件内部
- 可重用:每个组件都是具有独立功能的,它可以被使用在多个场景中
- 可维护:每个小的组件仅仅包含自身的逻辑,更容易被理解和维护
搭建 React 开发环境
1 2 3
| cnpm i create-react-app -g create-react-app reactapp npm start
|
JSX
什么是 JSX
是一种 JS 和 HTML 混合的语法,将组件的结构、数据甚至样式都聚合在一起定义组件
1 2 3 4
| ReactDOM.render( <h1>Hello</h1>, document.getElementById('root') );
|
什么是元素
- JSX 其实只是一种语法糖,最终会通过 babeljs 转译成 createElement 语法
- React 元素是构成 React 应用的最小单位
- React 元素用来描述你在屏幕上看到的内容
- React 元素事实上是普通的 JS 对象,ReactDOM 来确保浏览器中的 DOM 数据和 React 元素保持一致
1
| <h1 className="title" style={{color:'red'}}>hello</h1>
|
1 2 3 4 5 6
| React.createElement("h1", { className: "title", style: { color: 'red' } }, "hello");
|
createElement 的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { type:'h1', key: null, ref: null, props:{ className: "title", children: "hello", style: { color: 'red' } }, children:"hello" }
|
JSX 表达式
可以任意地在 JSX 当中使用 JavaScript 表达式,在 JSX 当中的表达式要包含在大括号里
1 2 3 4 5 6 7 8
| import React from 'react'; import ReactDOM from 'react-dom'; let title = 'hello'; let root = document.getElementById('root'); ReactDOM.render( <h1>{title}</h1>, root );
|
JSX 属性
- 需要注意的是 JSX 并不是 HTML,更像 JavaScript
- 在 JSX 中属性不能包含关键字,像 class 需要写成 className,for 需要写成 htmlFor,并且属性名需要采用驼峰命名法
1 2 3 4 5 6
| import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render( <h1 className="title" style={{ color: 'red' }}>Hello</h1>, document.getElementById('root') );
|
JSX 也是对象
- 可以在 if 或者 for 语句里使用 JSX
- 将它赋值给变量,当作参数传入,作为返回值都可以
if 中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React from 'react'; import ReactDOM from 'react-dom'; let root = document.getElementById('root'); function greeting(name) { if (name) { return <h1>Hello, {name}!</h1>; } return <h1>Hello, Stranger.</h1>; }
const element = greeting('zhufeng'); ReactDOM.render( element, root );
|
for 中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React from 'react'; import ReactDOM from 'react-dom'; let root = document.getElementById('root'); let names = ['张三', '李四', '王五']; let elements = []; for (let i = 0; i < names.length; i++) { elements.push(<li>{names[i]}</li>); } ReactDOM.render( <ul> {elements} </ul>, root );
|
更新元素渲染
React 元素都是 immutable 不可变的。当元素被创建之后,你是无法改变其内容或属性的。一个元素就好像是动画里的一帧,它代表应用界面在某一时间点的样子
更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render()方法
1 2 3 4 5 6 7 8 9 10 11 12
| import React from 'react'; import ReactDOM from 'react-dom'; let root = document.getElementById('root'); function tick() { const element = ( <div> {new Date().toLocaleTimeString()} </div> ); ReactDOM.render(element, root); } setInterval(tick, 1000);
|
React 只会更新必要的部分
- React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。
- 即便我们每秒都创建了一个描述整个 UI 树的新元素,React DOM 也只会更新渲染文本节点中发生变化的内容
组件 & Props
- 可以将 UI 切分成一些独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件
- 组件从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素
函数(定义的)组件
函数组件接收一个单一的 props 对象并返回了一个 React 元素
1 2 3 4 5 6 7 8 9
| import React from 'react'; import ReactDOM from 'react-dom'; let root = document.getElementById('root');
function Welcome(props){ return <h1>Hello, {props.name}</h1>; }
ReactDOM.render(<Welcome name="we"/>, root);
|
类(定义的)组件
1 2 3 4 5 6 7 8 9 10 11
| import React from 'react'; import ReactDOM from 'react-dom'; let root = document.getElementById('root');
class Welcome extends React.Component { render(){ return <h1>Hello, {this.props.name}</h1>; } }
ReactDOM.render(<Welcome name="we"/>, root);
|
组件渲染
- React 元素不但可以是 DOM 标签,还可以是用户自定义的组件
- 当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)转换为单个对象传递给组件,这个对象被称之为 props
- 组件名称必须以大写字母开头
- 组件必须在使用的时候定义或引用它
- 组件的返回值只能有一个根元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React from 'react'; import ReactDOM from 'react-dom'; let root = document.getElementById('root');
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } class Welcome2 extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
const element1= <Welcome name="we" />; console.log(element1.props.name); const element2= <Welcome2 name="we2" />; console.log(element1.props.name);
ReactDOM.render( <div>{element1}{element2}</div>, root );
|
状态
- 组件的数据来源有两个地方,分别是属性对象和状态对象
- 属性是父组件传递过来的(默认属性,属性校验)
- 状态是自己内部的,改变状态唯一的方式就是 setState
- 属性和状态的变化都会影响视图更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import React from 'react'; import ReactDOM from 'react-dom'; interface Props {
} interface State { date: any } class Clock extends React.Component<Props, State>{ timerID constructor(props) { super(props); this.state = { date: new Date() }; }
componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); }
componentWillUnmount() { clearInterval(this.timerID); }
tick() { this.setState({ date: new Date() }); }
render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}</h2> </div> ); } }
ReactDOM.render( <Clock />, document.getElementById('root') );
|
不要直接修改 State
- 构造函数是唯一可以给 this.state 赋值的地方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import React from 'react'; import ReactDOM from 'react-dom'; interface Props { } interface State { number: number } class Counter extends React.Component<Props, State> { timerID constructor(props) { super(props); this.state = { number: 0 }; }
componentDidMount() { this.timerID = setInterval( () => { this.setState({ number: this.state.number + 1 }); }, 1000 ); }
componentWillUnmount() { clearInterval(this.timerID); }
render() { return ( <div > <p> {this.state.number} </p> </div> ); } }
ReactDOM.render(< Counter />, document.getElementById('root') );
|
State 的更新可能是异步的
- 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用
- 因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态
- 可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import React from 'react'; import ReactDOM from 'react-dom'; class Counter extends React.Component { constructor(props) { super(props); this.state = { number: 0 }; } handleClick = () => {
this.setState((state) => ( { number: state.number + 1 } )); this.setState((state) => ( { number: state.number + 1 } )); } render() { return ( <div > <p> {this.state.number} </p> <button onClick={this.handleClick}>+</button> </div> ); } }
ReactDOM.render(< Counter />, document.getElementById('root') );
|
State 的更新会被合并
- 当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import React from 'react'; import ReactDOM from 'react-dom'; class Counter extends React.Component { constructor(props) { super(props); this.state = { name: 'zhufeng', number: 0 }; } handleClick = () => {
this.setState((state) => ( { number: state.number + 1 } )); this.setState((state) => ( { number: state.number + 1 } )); } render() { return ( <div > <p>{this.state.name}: {this.state.number} </p> <button onClick={this.handleClick}>+</button> </div> ); } }
ReactDOM.render(< Counter />, document.getElementById('root') );
|
事件处理
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串
- 你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import React from 'react'; import ReactDOM from 'react-dom'; class Link extends React.Component { handleClick(e) { e.preventDefault(); alert('The link was clicked.'); }
render() { return ( <a href="http://www.baidu.com" onClick={this.handleClick}> Click me </a> ); } }
ReactDOM.render( <Link />, document.getElementById('root') );
|
this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class LoggingButton extends React.Component { handleClick() { console.log('this is:', this); } handleClick1 = () => { console.log('this is:', this); } render() { return ( <button onClick={(event) => this.handleClick(event)}> Click me </button> ); } }this
|
向事件处理程序传递参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import React from 'react'; import ReactDOM from 'react-dom'; class LoggingButton extends React.Component { handleClick = (id, event) => { console.log('id:', id); }; render() { return ( <> <button onClick={(event) => this.handleClick('1', event)}> Click me </button> <button onClick={this.handleClick.bind(this, '1')}>Click me</button> </> ); } } ReactDOM.render(<LoggingButton />, document.getElementById('root'));
|
Ref
- Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素
- 在 React 渲染生命周期时,表单元素上的 value 将会覆盖 DOM 节点中的值,在非受控组件中,你经常希望 React 能赋予* 组件一个初始值,但是不去控制后续的更新。 在这种情况下, 你可以指定一个 defaultValue 属性,而不是 value